I am new to Ember and not very strong in JavaScript.
I am trying to use Java Spring MVC on a back-end with Ember on the front end. When I am trying to access a page. Ember makes a request for data to the server. Server answers with an Array, but the Array is not retrieved on the Ember side properly. I get an exception.
Error while loading route: Error: No model was found for '0'
at new Error (native)
at Error.Ember.Error (http://localhost:8080/libs/js/ember.js:844:19)
at Ember.Object.extend.modelFor (http://localhost:8080/libs/js/ember-data.js:9805:33)
at JSONSerializer.extend.extractArray (http://localhost:8080/libs/js/ember-data.js:3172:28)
at superWrapper (http://localhost:8080/libs/js/ember.js:1239:16)
at Ember.Object.extend.extractFindAll (http://localhost:8080/libs/js/ember-data.js:2380:21)
at Ember.Object.extend.extract (http://localhost:8080/libs/js/ember-data.js:2365:37)
at http://localhost:8080/libs/js/ember-data.js:10396:34
at invokeCallback (http://localhost:8080/libs/js/ember.js:9753:19)
at publish (http://localhost:8080/libs/js/ember.js:9423:9)
When debuging the javascript i found out that there is for in loop looping over an array
for (var prop in payload)
This for in loop is not retrieving the array elements, but is rather going throw properties. It might be because of what they talk about in this question on SO.
The - for me - problematic code is on github here.
Am I doing something wrong? should I report a bug?
I have created the following Ember model:
App.User = DS.Model.extend({
email: DS.attr('string'),
active: DS.attr('boolean'),
});
This is how the mapping looks like on server side
#RequestMapping("/users")
public
#ResponseBody
List<User> users() {
System.out.println("getting users");
List<User> list = new LinkedList<User>();
list.add(new User(1,"test#b.c"));
list.add(new User(2,"test2#b.c"));
list.add(new User(3,"test3#b.c"));
return list;
}
This JSON I got from fiddler:
[{"id":1,"email":"test#b.c","active":true},{"id":2,"email":"test2#b.c","active":false},{"id":3,"email":"test3#b.c","active":false}]
By default, I believe ember-data expects your JSON response to be prefixed with the model name, even if it's an array. This means you're data will need to be formatted like this:
{
"users":[
{
"id":1,
"email":"test#b.c",
"active":true
},
{
"id":2,
"email":"test2#b.c",
"active":false
},
{
"id":3,
"email":"test3#b.c",
"active":false
}
]
}
Related
I am trying to build an application using .Net and GraphQL. I need to get materials. not all of them but with the given Ids. When I pass it via playground or client side, I don't have any problem when I debug but I am not sure how to parse in the server side.
name: "materialsByIds",
arguments: new QueryArguments(
new QueryArgument<ListGraphType<IntGraphType>> { Name = "ids"}),
resolve: async (context) =>
{
var ids = context.GetArgument<IntGraphType>("ids");
// Do some action to bring datas
// Send data back
}
What am I missing here is there any methods to parse this in to list of int back?
Instead of using a GraphType for retrieving the argument, use the .NET type you want.
name: "materialsByIds",
arguments: new QueryArguments(
new QueryArgument<ListGraphType<IntGraphType>> { Name = "ids"}),
resolve: async (context) =>
{
var ids = context.GetArgument<List<int>>("ids");
// Do some action to bring datas
// Send data back
}
you can use MediatR. Create a Query class and pass it to mediateR. In CQRS, Command is used to write on DB(Create/Delete/Update of CRUD) and Query is used to read from DB(Read of CRUD).
create 'GetMaterialsByIdsQuery' and inside it write your code to get your materials by Id. then use it inside 'resolve' like this:
resolve: async context =>
{
var ids = context.GetArgument<List<int>>("ids");
return await mediator.Send(new GetMaterialsByIdsQuery(ids));
})
another way is that you can return somthing like MaterialsRepository.GetMaterialsByIds(ids) instead of using mediatR. But it is not recommended to use repository here. you can create a service class, inside it use your repository and then use your service here.
The title expresses my post succinctly. Though the following side-note expresses my specific situation which sparked the question.
*Side Note: I'm making a web app with an Angular Front-end. I've found that once I encapsulate my data in a 'FormData' object from an
html form template, I am then unable to get those values back from
within client-side Angular (as posts like this one state:
How do i get the content from a formData?
). Though when I POST to ASP.Net with a '[FromForm]' attribute the
values get mapped fine.
So to make things easier I might rather acquire data from the form in
the first place and then use it in a 'body' POST instead of a 'form'
POST. Doing this so that I am passing around straight data I can work
with, instead of data wrapped in a form which I'm unable to work with
client-side.
So, thus, I'm wondering the differences between Posting 'forms' and
posting 'bodies', and whether there's any reason I should use one over
the other?
Update
In ASP.Net Core you can put attributes on your parameter types for REST endpoints. [FromForm] and [FromBody] are what I'm curious about. On the angular side, calling these REST endpoints requires passing either a 'FormData' (for the [FromForm]), or some sort of user defined type (for [FromBody]).
See below for examples.
Update
Here's the signature for the ASP.NET Endpoint for the '[FromBody]':
[HttpPost]
public async Task<IActionResult> CreateToken([FromBody] LoginViewModel model)
{...}
Here's the Angular snippet that calls this '[FromBody]' endpoint, along with the type that is passed in for the POST call:
public creds = {
username: "",
password: ""
}
login(creds): Observable<boolean> {
return this.http.post("api/account/createtoken", creds)
.pipe(
map((data: any) => {
this.token = data.token;
this.tokenExpiration = data.expiration;
this.username = data.username;
return true;
}));
}
Here is the signature for the ASP.NET Endpoint for the '[FromForm]':
[HttpPost]
public async Task<IActionResult> Signup([FromForm] SignupViewModel viewModel)
{...}
and here is the Angular snippet that calls this '[FromForm]' endpoint:
registerUser(formData: FormData) {
const options = {
headers: new HttpHeaders().set('Accept', 'application/json')
};
return this.http.post('api/account/signup', formData, options)
.pipe(
map(() => {
return true;
}),
catchError(this.handleError)
);
}
The FromForm attribute is for incoming data from a submitted form sent by the content type application/x-www-url-formencoded while the FromBody will parse the model the default way, which in most cases are sent by the content type application/json, from the request body
I'm getting the error "_id is not allowed by the schema" when trying to use an autoform to update a collection via a ValidatedMethod.
As far as I can see from this example and the official docs there is no expectation for my schema to include the _id field, and I wouldn't expect to be updating the id from an update statement, so I have no idea why this error is happening.
If I switch from using the validated method to writing directly to the collection (with a schema attached to the collection that doesn't have the id in) everything works as expected, so I'm assuming the issue is with my the validate in my ValidatedMethod.
Any idea what I'm doing wrong?
Template: customer-edit.html
<template name="updateCustomerEdit">
{{> quickForm
collection="CustomerCompaniesGlobal"
doc=someDoc
id="updateCustomerEdit"
type="method-update"
meteormethod="CustomerCompanies.methods.update"
singleMethodArgument=true
}}
</template>
Template 'code behind': customer-edit.js
Template.updateCustomerEdit.helpers({
someDoc() {
const customerId = () => FlowRouter.getParam('_id');
const instance = Template.instance();
instance.subscribe('CustomerCompany.get', customerId());
const company = CustomerCompanies.findOne({_id: customerId()});
return company;
}
});
Update Validated Method:
// The update method
update = new ValidatedMethod({
// register the name
name: 'CustomerCompanies.methods.update',
// register a method for validation, what's going on here?
validate: new SimpleSchema({}).validator(),
// the actual database updating part validate has already been run at this point
run( newCustomer) {
console.log("method: update");
return CustomerCompanies.update(newCustomer);
}
});
Schema:
Schemas = {};
Schemas.CustomerCompaniesSchema = new SimpleSchema({
name: {
type: String,
max: 100,
optional: false
},
email: {
type: String,
max: 100,
regEx: SimpleSchema.RegEx.Email,
optional: true
},
postcode: {
type: String,
max: 10,
optional: true
},
createdAt: {
type: Date,
optional: false
}
});
Collection:
class customerCompanyCollection extends Mongo.Collection {};
// Make it available to the rest of the app
CustomerCompanies = new customerCompanyCollection("Companies");
CustomerCompaniesGlobal = CustomerCompanies;
// Deny all client-side updates since we will be using methods to manage this collection
CustomerCompanies.deny({
insert() { return true; },
update() { return true; },
remove() { return true; }
});
// Define the expected Schema for data going into and coming out of the database
//CustomerCompanies.schema = Schemas.CustomerCompaniesSchema
// Bolt that schema onto the collection
CustomerCompanies.attachSchema(Schemas.CustomerCompaniesSchema);
I finally got to the bottom of this. The issue is that autoform passes in a composite object that represents the id of the record to be changed and also a modifier ($set) of the data, rather than just the data itself. So the structure of that object is along the lines of:
_id: '5TTbSkfzawwuHGLhy',
modifier:
{
'$set':
{ name: 'Smiths Fabrication Ltd',
email: 'info#smithsfab.com',
postcode: 'OX10 4RT',
createdAt: Wed Jan 27 2016 00:00:00 GMT+0000 (GMT Standard Time)
}
}
Once I figured that out, I changed my update method to this and everything then worked as expected:
// Autoform specific update method that knows how to unpack the single
// object we get from autoform.
update = new ValidatedMethod({
// register the name
name: 'CustomerCompanies.methods.updateAutoForm',
// register a method for validation.
validate(autoformArgs) {
console.log(autoformArgs);
// Need to tell the schema that we are passing in a mongo modifier rather than just the data.
Schemas.CustomerCompaniesSchema.validate(autoformArgs.modifier , {modifier: true});
},
// the actual database updating part
// validate has already been run at this point
run(autoformArgs)
{
return CustomerCompanies.update(autoformArgs._id, autoformArgs.modifier);
}
});
Excellent. Your post helped me out when I was struggling to find any other information on the topic.
To build on your answer, if for some reason you want to get the form data as a single block you can use the following in AutoForm.
type="method" meteormethod="myValidatedMethodName"
Your validated method then might look something like this:
export const myValidatedMethodName = new ValidatedMethod({
name: 'Users.methods.create',
validate(insertDoc) {
Schemas.NewUser.validate(insertDoc);
},
run(insertDoc) {
return Collections.Users.createUser(insertDoc);
}
});
NB: The Schema.validate() method then requires an Object, not the modifier as before.
I'm unclear if there are any clear advantages to either method in general.
The type="method-update" is obviously the way you want to go for updating documents because you get the modifier. The type="method" seems to be the best way to go for creating a new document. It would likely also be the best option in most cases where you're not intending to create a document from the form data.
Env: Titanium 3.1.3, Alloy 1.2.2.
I'm using the following adapter for persistence on the models/collections: https://github.com/viezel/napp.alloy.adapter.restapi
I have an API that has a different URL structure for a collection than it does a single model. Consider the following:
To get a single record: [GET] /files/:id
To get all the files for a user: [GET] /users/:id/files
I have the following schema for files.js:
exports.definition = {
config: {
"URL": "https://my.api.here/files",
//"debug": 1,
"adapter": {
"type": "restapi",
"collection_name": "files",
"idAttribute": "id"
}
},
extendModel: function(Model) {
_.extend(Model.prototype, {});
return Model;
},
extendCollection: function(Collection) {
_.extend(Collection.prototype, {
initialize: function(){
this.url = "http://my.api.here/users/"+this.user_id+"/files";
}
});
return Collection;
}
}
What I'm trying to do in the above is override the collection initialize method to change the URL structure for the collection. I then call this accordingly:
var currentUserFiles = Alloy.createCollection("files", {user_id:"12345"});
currentUserFiles.fetch({
success: function(files){
console.log("Woo! Got the user's files!");
console.log(JSON.stringify(files.models));
},
error: function(){
console.log("Nope");
}
});
This doesn't work. The fetch() method just continues to try to call /files. I've tried setting url as a property on the collection after it's created, that also don't work.
Ideally, I'd like to do this for both local instances as well as the singleton version of the collection.
So - the question is: can I utilize a different URL for a collection than I do for a model? Obviously, I don't want to just call /files and sort/filter client-side - that'd be a nightmare with a lot of records. What am I missing here?
It's a bit late but for anyone else that comes across this. I problem is where/how the url is specified for model and collection. The model needs a specific id (eg: primary key) passed into it because the model can only be one object. If you need more than one object, then use the collection. Hope this helps :)
extendModel : function(Model) {
_.extend(Model.prototype, {
url : function() {
return "http://my.api.here/users/"+this.user_id+"/files/"+ FILE_ID
},
});
return Model;
},
extendCollection : function(Collection) {
_.extend(Collection.prototype, {
url : function() {
return "http://my.api.here/users/"+this.user_id+"/files"
},
});
},
I am simply trying to load data when my app starts. However, the view loads faster than the http request(of course). I want to refresh my view once my data has been properly loaded because that data defines my view.
I've tried $rootScope.apply from inside the factory where I do my http request, and I also tried directly doing the http request in my controller again with $scope.apply, and neither one worked as they both gave me "$digest already in progress"
Any idea how can I set up my code to make my views refresh on data load? I will be having several different http requests and I would like to know how to set them up properly! I would really appreciate any input!
Here is some of the code I am working with.
app.factory('HttpRequestFactory', function($http, $q) {
var HttpRequestFactory = {
async: function(url, params) {
var deferred = $q.defer();
$http({
url: url,
method: post,
params: params
})
.success(function(data, status, headers, config) {
deferred.resolve(data);
})
.error(function(data, status, headers, config) {
deferred.reject("An error occurred");
});
return deferred.promise;
}
};
return HttpRequestFactory;
});
Factory
function initializeAll(){
HttpRequestFactory.async('../api', {action: 'getall'}).then(function(data) {
//$rootScope.$apply(function () {
allData = data;
//});
angular.forEach(allData, function(value, index){
console.log('Voala!');
});
});
}
Controller calling the factory's function initializeAll()
app.controller("MainController", ["$scope", "$rootScope","MyFactory",
function($scope, $rootScope, MyFactory){
MyFactory.initializeAll();
}
]);
Oh my !
You got the f** matter with AngularJS !
In fact you have to do a "safeApply" like that for example :
$rootScope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
In AngularJS you can only have one $apply or $digest loop at the same time.
For details on these loops look at the docs :
http://docs.angularjs.org/guide/concepts
It will explain what is the $apply loop and you'll understand a lot of things about the two-way-data-binding in AngularJS
Hope it helps.
Don't use $apply: use $watch.
Calling $apply is (almost) always the wrong thing to do. The only time you should ever be calling it is if you've triggered a change outside of an 'angular' method; here, since the trigger occurs in an angular $http request, you can't call $apply because it's already being done at that moment by the $http block. Instead, what you want to do is $watch.
Official Doc for $scope.$watch() here
This will let you watch an object and update whenever it changes. I assume that your view is based on allData and you want it to update immediately; if you're using an ng method, then the watch is automatically setup for you and no more work should be needed. If you're using allData yourself inside a controller, you can write the watch in that controller like this:
$scope.$watch(function thingYouWantToWatch(){
return <accessor call to allData here>;
},
function whatToDoOnChange(newValue, oldValue){
$scope.myCoolThing = newValue; //this is the newValue of allData, or whatever you're watching.
}
);