ember JSONAPIAdapter fails to load data properly into store - ember.js

I am using ember-cli and ember-data 1.13.7 and JSONAPIAdapter.
I use http-mock to mock data during local testing.
It worked well when I used the RESTAdapter, but I ran into a problem when switching to the JSONAPIAdapter.
The problem is that the records data does not get loaded into the store and I get an error reading an undefined property.
The adpater just looks like this:
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
namespace: 'api',
});
The ajax call is this:
http://localhost:4200/api/users/1
The http-mock looks like this:
usersRouter.get('/:id', function(req, res) {
res.send({
'users': {
id: req.params.id,
firstname: "John",
lastname: "Doe",
email: "johndoe#example.com",
mobile: "12345678",
nextAppointment: 1
}
});
});
The response looks like this:
{"users":{"id":"1","firstname":"John","lastname":"Doe","email":"johndoe#example.com","mobile":"12345678","nextAppointment":1}}
The response data islooking good but the problem is that together with the response is the header status code of 304, and the fact that the data is not loaded into store. The object with id=1 is created, but all properties in the object are 'undefined' when I look at the stores data in the ember inspector.
Update:
The error I get is:
Error while processing route:
home.index Cannot read property '_internalModel' of undefined
TypeError: Cannot read property '_internalModel' of undefined
Update 2:
It turns out the 304 is not important. The model is still not properly loaded into store when the httpcode is 200 either.
I also fund that this call works fine:
http://localhost:4200/api/users
while this call fails:
http://localhost:4200/api/users/1
They return exactly the same JSON response.

Your call for:
http://localhost:4200/api/users
can't return exactly the same JSON response.
Your call for: http://localhost:4200/api/users/1 should return:
{
"data": {
"id":"1",
"type": "user",
"attributes" : {
"firstname":"John",
"lastname":"Doe",
"email":"johndoe#example.com",
"mobile":"12345678",
"nextAppointment":1
}
}
}
Read more about JSONAPIAdapter:
JSON API ADAPTER AND SERIALIZER in Ember Data v1.13 blog post
jsonapi.org

Related

Ember Data 404 when using store

New to Ember.js, making a simple todo app to learn it a bit better.
In my routes/todos.js I have:
import Route from '#ember/routing/route';
export default Route.extend({
model() {
return [
{
title: 'walk the dog',
completed: false,
},
];
},
});
However, when I change to:
export default Route.extend({
model() {
return this.get('store').findAll('todo');
},
});
I get the error:
ember-console.js:29 Error while processing route: todos Ember Data Request GET /todos returned a 404
What does this error mean?
findAll will always make a request to the API server. If that doesn't exist or it isn't accessible, it will return a 404. Use peekAll if you just want to reference data in the Store without making an API request.
A 404 also will cause an error state to be thrown in the route and for the model to be rejected. Here is one way I've worked around that for APIs that DO throw 404s when there is no data (rather than a 200 with an empty array, for example):
return new EmberPromise(resolve =>
this.get('store').findAll('todo').then(
// resolves successfully whether the API call succeeds or fails
todos => resolve(todos),
err => {
console.warn(err);
resolve([]);
}
)
);

EmberJS embedded items in payload JSONAPI

Ember : 1.13.3
Ember Data : 1.13.5
jQuery : 1.11.3
I am trying to send a JSON payload using ember-data from my EmberJS client to my server. I want to send the entire object graph to the server on saving the project, as I don't want to send multiple requests. I wouldn't mind sending multiple requests, but I am worried about what happens if one of the requests fails in the middle and the data on the server will not be correct.
I wanted to use JSONAPI (http://jsonapi.org/format/#document-compound-documents) as that is becoming the default adapter in Ember. Also, there is a few C# libraries that handle this format, so I thought it would be quite straightforward. However, after reading the spec, it seems that I cannot embed objects if they do not have an id. EmberJS also does not attach the child objects to the JSON either even though I have specified { async: false, embedded: 'always' }) on the DS.attr.
My question is: If an application is used in such a way that an object graph is created on the client side, how do you use JSONAPI format to send the entire object graph to the server? Do I have to generate ids on the client side to satisfy the JSONAPI standard? Then once they get to the server just ignore them so they get saved with an id generated by the ORM?
Here is my labelGroup model:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
labels: DS.hasMany('label-model', { async: false, embedded: 'always' })
});
Here is my project model:
import DS from 'ember-data';
export default DS.Model.extend(DS.EmbeddedRecordsMixin, {
name: DS.attr('string'),
labelGroups: DS.hasMany('labelGroup', { async: false, embedded: 'always'})
});
Here is the POST that I get after doing a save() on the project:
{
"data":{
"attributes":{"name":"Project"},
"relationships":{
"label-groups":{
"data":[
{"type":"label-groups","id":null},
{"type":"label-groups","id":null},
{"type":"label-groups","id":null},
{"type":"label-groups","id":null},
{"type":"label-groups","id":null},
{"type":"label-groups","id":null},
{"type":"label-groups","id":null}
]
}
},
"type":"label-projects"
}
}
UPDATE: I tried using https://www.npmjs.com/package/ember-cli-uuid to generate client side ids which it has. However the data getting output does not include the extra objects, only a reference to their ids. I expected to see an "included" property as specified here:http://jsonapi.org/format/#document-compound-documents, but it is not there.
{
"data":{
"id":"7b4544ee-91cd-493d-8b10-52040e68c283",
"attributes":{"name":"Project"},
"relationships":{
"label-groups":{
"data":[
{"type":"label-groups","id":"08115273-e82a-4d46-93ea-232ce071fb78"},
{"type":"label-groups","id":"9ca94fe9-8077-411e-98d2-1694c6fecce4"},
{"type":"label-groups","id":"d629f1e8-7962-404d-8034-38229ab21f77"},
{"type":"label-groups","id":"c6bda655-5489-4760-847b-bf02239bb2c5"},
{"type":"label-groups","id":"f6fef249-2d1d-43f0-ba64-24b7ff8b5637"},
{"type":"label-groups","id":"a7db25bf-52c8-477b-83e4-64e7c76b072e"},
{"type":"label-groups","id":"f3b5fbb3-261a-4b3d-b481-b9352f8ce2d6"}
]
}
},
"type":"label-projects"
}
}
Ember-data has no support for what you want at the moment. So ember-data will not save your relationships data in a save payload.
But its possible to do this your own by using a custom adapter and serializer.
I strongly recommend you to checkout the API and then look into the source.
If you call .save() on your Model the createRecord method is called on your adapter.
Here serializeIntoHash on the serializer is called to serialize the Model.
serializeIntoHash calls serialize, where serializeBelongsTo and serializeHasMany is called.
Now you can just override serializeHasMany and modify the hasMany before the line:
json[payloadKey] = hasMany;
Here you have the type and the ids as they are sent by ember-data. You could just .forEach the data on the hasMany and then fetch the store for the data and build your included array.
I hope this helps you to understand the serializer and the adapter so you can modify it to do whatever you want pretty easy. Actually this is the best part about ember-data. The structure of the adapter and the serializer, which allows easy modifications.

Getting Django API data with Ember

I'm starting a project working with ember.js with Django Rest Framework for my REST Server.
I'm using an adapter on the ember side to get the data into the right format that Ember needs. I'm at the point where I'm making a request to my server, and getting a 200 with data returning, however Ember is throwing an error Error while processing route: index Assertion Failed: The response from a findAll must be an Array, not undefined.
There are quite a few posts about this error, but I haven't been able to find any that are relevant to my problem, it seems to be a sort of blanket error that can be caused by many things?
I'm at a loss because my server is receiving the request and returning data in the format that I would expect from the server. I'm not sure how to tell if the error is within my Ember app or if my data is not being transformed correctly by the adapter.
// routers/index.js
export default Ember.Route.extend({
model: function() {
return this.store.find('user');
}
});
// config/environment.js
APP: {
API_NAMESPACE: 'v1',
ENV.APP.API_HOST = 'http://localhost:8000',
},
Just having this code in my router is how I get the error. I feel like this is a very simple problem, but can't seem to get past it. I've tried moving that code into different parts such as the controller, just to see that I can fetch data. Hopefully someone has a suggestion, thanks. Let me know anything else I can post to help.
EDIT: Going through the source it appears there is a problem in the serializer:
extractArray: function(store, type, payload) {
// Convert payload to json format expected by the RESTSerializer.
// This function is being overridden instead of normalizePayload()
// because `results` will only be in lists.
console.log(type);
var convertedPayload = {};
if (payload.results) {
convertedPayload[type.modelName] = payload.results;
} else {
convertedPayload[type.modelName] = payload;
}
return this._super(store, type, convertedPayload);
},
The type.modelName is undefined. When I look at the type object, it's just a blank Class(). When i log type.toString() I get joe2go#model:user:
Could it be something related to my model? Everything seems pretty standard to me.
import DS from 'ember-data';
export default DS.Model.extend({
username: DS.attr('string'),
firstName: DS.attr('string'),
lastName: DS.attr('string')
});
There is apparently a bug in the package.
extractArray: function(store, type, payload) {
// Convert payload to json format expected by the RESTSerializer.
// This function is being overridden instead of normalizePayload()
// because `results` will only be in lists.
var convertedPayload = {};
if (payload.results) {
convertedPayload[type.modelName] = payload.results;
} else {
convertedPayload[type.modelName] = payload;
}
return this._super(store, type, convertedPayload);
},
Inside the serializer it refers to type.modelName, but that should be type.typeKey

Cannot read property 'match' of undefined at Ember.DefaultResolver.extend.podBasedComponentsInSubdir

I'm getting a really opaque error message (opaque in the sense I have no point of reference for my own source) from console, I'm not entirely sure where to look, I feel it's likely an error in library code but before posting this on github I'll just double check it's not my own fault.
The Problem
The Problem is simple, I'm calling this.store.find('player'), in hopes to get a list of all players, and then display them in some kind of list, but I'm not even getting past the loading part. The data is pulled from the server and looks properly formatted, but something seems to be failing after the route.model method call. And the error message seems to be somewhere in the ember.js library code with nothing pointing back to my own code.
Server Response
The content type is of course application/json, and note the id property is actually _id.
[
{
"_id":"55405a5102b4ed623c225e87",
"alias":"mikeTest",
"__v":0,
"scans":[],
"createdAt":"2015-04-29T04:13:05.223Z"
}
]
Error message
Note there is part of the stack trace pointing to my source, only Ember source. Which has made this a pain to debug.
Error while processing route: leader Cannot read property 'match' of undefined TypeError: Cannot read property 'match' of undefined
at Ember.DefaultResolver.extend.podBasedComponentsInSubdir (http://localhost:4200/assets/vendor.js:60138:76)
at http://localhost:4200/assets/vendor.js:60190:34
at Array.exports.default.mixin.Mixin.create.find (http://localhost:4200/assets/vendor.js:39572:30)
at Ember.DefaultResolver.extend.findModuleName (http://localhost:4200/assets/vendor.js:60188:44)
at resolveOther (http://localhost:4200/assets/vendor.js:60051:37)
at superWrapper (http://localhost:4200/assets/vendor.js:28141:20)
at exports.default.EmberObject.default.extend.resolve (http://localhost:4200/assets/vendor.js:15454:35)
at Object.resolve [as resolver] (http://localhost:4200/assets/vendor.js:15217:23)
at resolve (http://localhost:4200/assets/vendor.js:12792:29)
at Object.Registry.resolve (http://localhost:4200/assets/vendor.js:12336:21)
Source
This ember app is very young, so there is very little source at the moment, but this is all the relevant source at the moment.
Routes
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
export default Router.map(function() {
this.resource('leader');
this.resource('profile');
this.route('loading');
});
Leader route
Leader has a template and a controller, but they are basically empty right now.
import Ember from 'ember';
export default Ember.Route.extend({
model: function () {
return Ember.RSVP.hash({
players: this.get('store').find('player')
});
},
});
Player Model
import DS from 'ember-data';
export default DS.Model.extend({
alias: DS.attr('string'),
createdAt: DS.attr('date'),
scans: DS.hasMany('scan'),
});
Application Adapter
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
namespace: ''
});
Application Serialiser
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
primaryKey: function (type) {
return '_id';
},
serializeId: function(id) {
return id.toString();
}
});
Versions
I'm not sure if any of the versions here are particularly important
ember-cli is 0.2.3
ember-data is 1.0.0-beta.16.1
ember is 1.11.1
Things I've tried
removing properties from the model, in the event the relationships seemed to be the problem (nothing changed)
tried setting up a serialiser and adapter for the application (included above), nothing changed.
the serialiser in the event that the id field in the response is actually _id.
tried updating ember data, nothing changed.
Okay I figured out what was being done wrong... I forgot to check if the data being returned by the server abides to the convention/protocol required to use ember data. The JSON returned by the server looks like this.
[
{
"_id":"55405a5102b4ed623c225e87",
"alias":"mikeTest",
"__v":0,
"scans":[],
"createdAt":"2015-04-29T04:13:05.223Z"
}
]
It should actually look like this
{
"players": [
{
"_id":"55405a5102b4ed623c225e87",
"alias":"mikeTest",
"__v":0,
"scans":[],
"createdAt":"2015-04-29T04:13:05.223Z"
}
]
}
So yes this was me being dumb and missing something.
Why is this Required
Ember data expects JSON returned from the server to meet the JSON API Standard, which is a standard that specifies the formatting of the JSON returned from a server. In this case, the data didn't meet the JSON API standard, as I forgot to put the array of players under a key called players. There are some more examples of this in the Ember v1.10.0 guide to models.
The reason Ember Data expects this is so Ember Data can make the certain assumptions about the data returned from the server.

How to disable model autobinding in ember data

I have ember action in controller:
actions: {
createRecord: function(){
var record = this.store.createRecord('record',{});
record.save().then(function(response){
console.log(response);
});
}
In promise (after saving) I receive message in format like this:
{
"content": "Error/success",
"detail": "Detail"
}
It's look like ember data make auto-bind and crash with message:
Error: No model was found for 'content'
How I can fix this, I don't need model for response
Firstly this isn't following rest standards, there are response codes that define success/failure. In your case, you are responding in a fashion that is telling ember data, hey, everything went a okay, and here's some data that should be loaded into the store.
If you have no control over the endpoint, then you'll need to create a custom serializer, and override the appropriate method (probably this one http://emberjs.com/api/data/classes/DS.JSONSerializer.html#method_extractSave ), and rip out the erroneous json before handing it to ember data.