I've got a model called "Membership" that has a string attribute "inviteToken" which I would like to use as my primary key.
I've created the following serializer, but cannot get it to pick up the primary key from the JSON.
app/serializers/membership.js:
import DS from 'ember-data';
export default DS.JSONAPISerializer.extend({
primaryKey: 'invite-token' // also tried 'inviteToken'
});
The specific error I'm getting is:
Error while processing route: invitations.show Assertion Failed: You
must include an 'id' for membership in an object passed to 'push'
Error: Assertion Failed: You must include an 'id' for membership in an
object passed to 'push'
Which happens when I try to get a record by its ID in the route:
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
return this.store.find('membership', params.token);
}
});
API Response:
{
"jsonapi":{
"version":"1.0"
},
"data":{
"type":"membership",
"id":"30",
"attributes":{
"invite-token":"5bGo7IhZh93E4SB07VWauw"
}
}
}
The strange thing is that if I use "type" as the primary key, I see "membership" as the id in the ember inspector. It's as if ember data doesn't know how to use something from the "attributes". I'm using ember data 2.4.0.
Update
I can hack this to work in my serializer by doing this:
import DS from 'ember-data';
export default DS.JSONAPISerializer.extend({
normalize: function(type, hash) {
const json = this._super(type, hash);
json.data.id = json.data.attributes.inviteToken;
return json;
}
});
The serializer expects the value of primaryKey to refer to a top level element in the json. This is why "type" and "id" works. It currently does not support nested properties (for example primaryKey: "attributes.invite-token")
However there are two good workarounds:
The first is overriding the extractId method. The default implementation is quite simple. In your case you could do something like:
extractId(modelClass, resourceHash) {
var id = resourceHash['attributes']['invite-key';
return coerceId(id);
},
The second way is the method you discovered, a more brute force approach, and that is to assign the id manually in the normalize function.
Related
I have two APIs, let's say :
aaa.com/api/v1/events
bbb.com/api/events
Both implement JSONAPI adapter, and then return a payload with type="events".
As a result, I have to scope those models into subfolders:
models/aaa/event.js
models/bbb/event.js
Which result in querying the models this way:
this.get('store').findAll('aaa/event');
Then, I have implemented an adapter:
// adapters/aaa/event.js
import config from '../../config/environment';
import DS from 'ember-data';
import Ember from "ember";
const { String } = Ember;
export default DS.JSONAPIAdapter.extend({
host: 'aaa.com',
namespace: 'api/v1',
pathForType: function(type) {
return String
.underscore(type) // `aaa/some-model` -> `aaa/some_model`
.pluralize() // `aaa/some_model` -> `aaa/some_models`
.replace("aaa/", ""); // `aaa/some_model` -> `some_model`
},
});
So far so good, Ember correctly calls aaa.com/api/v1/events, the returned payload is typed events but Ember fails to translate it to aaa/events.
How can I proceed ?
So you got the model specific serializer for aaa/event and bbb/event. you can overridre modelNameFromPayloadType method to translate events to your required model name aaa/events.
For aaa/event serializer,
modelNameFromPayloadType(payloadType) {
return 'aaa/event';
}
Refer:
https://emberjs.com/api/ember-data/2.14/classes/DS.JSONAPISerializer/methods/modelNameFromPayloadType?anchor=modelNameFromPayloadType
Hooking up my 1st model to my backend and am getting the following error when calling Ember Data's findAll()
TypeError: Cannot read property 'type' of undefined
The route,
import Ember from 'ember';
export default Ember.Route.extend({
model() {
this.store.findAll('restaurant');
}
});
The model,
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr()
});
I can see that Ember is hitting my local server and returning properly formatted JSON from the correct url,
{
"restaurants": {
"id":1,
"name":"Restaurant Name"
}
}
I have seen similar questions about this but it usually has to do with improperly formatted JSON.
The default data format Ember data used is JSON API so your data should look something like below,
{
"data": {
"id": "1",
"type": "restaurants,
"attributes": {
"name": "Name"
}
}
}
To make Ember data fit your needs without change how backend produce data's format. You can add your own adapter which is used to transfer your Ember data's request to the right request.
Read more about the adapter at the link below,
https://guides.emberjs.com/v2.6.0/models/customizing-adapters/
Your JSON file is formatted like a REST API response, but by default Ember uses JSONAPIAdapter.
First fix the JSON, it should return an array:
[{
"id":1,
"name":"Restaurant Name"
}]
Then change the adapter and configure serializer:
// in app/adapters/application.js
import Ember from 'ember';
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
})
// in app/serializers/application.js
import Ember from 'ember';
import DS from 'ember-data';
export default DS.JSONSerializer.extend({
})
Read more:
Customizing adapters
JSONAPIAdapter
RESTAdapter
I have created a model in my ember app called ticket-stats:
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
get_tickets_more: attr(),
get_tickets: attr(),
get_avg_tickets: attr()
});
The data is pulled from JSON api: http://domain.com/data/ticketStats?blah=blah...
So i have added a special adapter for this model called ticket-stats:
import JSONAPIAdapter from 'ember-data/adapters/json-api';
export default JSONAPIAdapter.extend({
host: 'http://domain.com',
namespace: 'data',
pathForType: function(type) {
return Ember.String.camelize(type);
}
});
I get the data for this model in route:
import Ember from 'ember';
export default Ember.Route.extend({
model () {
var ticketData;
this.store.query('ticket-stats', { teamID: 218, attUID: 'oc7569', useProd: 1})
.then(function(stats) { ticketData = stats; });
return Ember.RSVP.hash({
currentUser: this.currentUser,
ticketStats: ticketData
});
}
});
And, i get a TypeError:
ember.debug.js:32096 TypeError: Cannot read property 'type' of undefined
at _pushInternalModel (store.js:1524)
at push (store.js:1501)
at finders.js:171
at Object.Backburner.run (ember.debug.js:678)
at _adapterRun (store.js:1733)
at finders.js:168
at tryCatch (ember.debug.js:53806)
at invokeCallback (ember.debug.js:53821)
at publish (ember.debug.js:53789)
at ember.debug.js:32054onerrorDefault # ember.debug.js:32096exports.default.trigger # ember.debug.js:54476(anonymous function) # ember.debug.js:55727Queue.invoke # ember.debug.js:333Queue.flush # ember.debug.js:397DeferredActionQueues.flush # ember.debug.js:205Backburner.end # ember.debug.js:560(anonymous function) # ember.debug.js:1126
Any ideas as to why this is happening? This error goes away when i remove the pathForType function in the adapter, but then i get another error about getting the data from http://domain.com/data/ticket-stats?... which is not the correct URL. I have to convert to camelCase, ticket-stats => ticketStats.
This is what my json looks like:
{
"get_avg_tickets": { ... },
"get_tickets_more": { ... },
"get_tickets": { ... }
}
I also modified the application serializer by simply replacing JSONAPISerializer with JSONSerializer: app/serializers/application.js
import JSONSerializer from 'ember-data/serializers/json';
export default JSONSerializer.extend({
});
Any help would be appreciated! I'm very new to Ember.
you need to use this serializer here is the reference link
http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html
import DS from 'ember-data';
export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
normalizeQueryResponse(store, primaryModelClass, payload, id, requestType) {
payload = { ticketStats: payload };
return this.normalizeArrayResponse(store, primaryModelClass, payload, id, requestType);
},
});
your json should be in this format:
{
"get_avg_tickets": 45,
"get_tickets_more": propertyvalue,
"get_tickets": propertyvalue
}
otherwise you need to normalize your response in normalizeQueryreponse of serilializer
Also ready following doc for your help
http://thejsguy.com/2015/12/05/which-ember-data-serializer-should-i-use.html
i hope it will help you. dont forget to accept my answer.
you need to use serializers which are best suited for your json data.
http://emberjs.com/api/data/classes/DS.JSONAPISerializer.html
This error is raising because your json data received from api is not fully fit into you application requirements for json formate.
Please Share json data here. because domain.com json url is not working.
there are so many issues in your code. let me to guide you one by one
I'm working on an Ember app that is using Ember Data and the now default json-api adapter.
According to the json-api spec (http://jsonapi.org/format/#crud-deleting) when deleting a record your server should return a 200 response if the deletion is successful and the server responds with just a top level meta key.
My current server does just this and I'm trying to figure out how to access the data in the top level meta object when using Ember Data's model.destroyRecord() method.
myModel.destroyRecord().then(function(model){
// the returned value is the model. How can I get the actual metadata
// returned by the server?
});
The server response contains information about what exactly was deleted and looks like this:
{
"meta": {
num-deleted-a: 10,
num-deleted-b: 100,
num-deleted-c: 200
}
}
I'd like to get this information so I can display it to the user.
Thank you!
I am using the following versions:
Ember : 2.2.0
Ember Data : 2.3.3
jQuery : 1.11.3
After upgrading to Ember 2.6.1 and Ember 2.6.1 I was no longer able to access the store._metadataFor property.
To get access to the metadata from a particular call I now override the serializer for the model and add a meta property to the model itself that simply passes through the metadata.
As an example I have a record type called vote which when saved returns some metadata.
For the model I do the following:
// Vote Model (/app/models/vote)
export default DS.Model.extend({
vote: DS.attr('number'),
// Since i don't provide a transform the values for meta are passed through in
// raw form
meta: DS.attr()
});
Then in the serializer for the vote model I do the following:
// Vote serializer (/app/serializers/vote)
import DS from "ember-data";
export default DS.JSONAPISerializer.extend({
normalizeSaveResponse(store, primaryModelClass, payload, id, requestType) {
// The metadata in the payload does get processed by default and will be
// placed into a top level `meta` key on the returned documentHash
let documentHash = this._super(store, primaryModelClass, payload, id, requestType);
// Make sure we always have an empty object assigned to the meta attribute
if(typeof(payload.meta) !== 'object'){
payload.meta = {};
}
// Move the metadata into the attributes hash for the model
documentHash.data.attributes.meta = payload.meta;
return documentHash;
}
});
Note that in the above example I'm only adding in metadata to the vote model when making a save call to the store. If you wanted to always add in the metadata then you would override the normalize method instead of the normalizeSaveResponse method.
Then you can access a meta field in the results of your save call.
let vote = self.store.createRecord('vote', {
vote: voteValue
});
vote.save().then(function(result){
// this will now contain your metadata
console.info(result.get('meta'));
});
Ember does not support meta for single model requests (find,save and destroyRecord) at the moment!
If you want this you have to hook into ember internals.
The following code uses ember internals from ember 2.3 and may break in future versions!
There is the undocumented _metadataFor function on the store that gives you the last metadata for a given type. I use a custom initializer to always save it to the Model:
import Ember from 'ember';
import DS from 'ember-data';
const {set} = Ember;
export function initialize(application) {
DS.Model.reopen({
meta: null,
didCommit() {
this._super(...arguments);
set(this, 'meta', this.store._metadataFor(this.constructor.modelName));
}
});
};
export default {
name: 'meta',
initialize: initialize
};
After this you can do model.save().then(() => console.log(model.get('meta'))) or model.destroyRecord.then(() => console.log(model.get('meta'))).
Maybe checkout this ember-twiddle.
Hi according to the ember ds 1.13 release docs:
If your app is using the vanilla JSONSerializer or RESTSerializer,
you will not have to make any changes, and your app will continue to
work. The existing serializers have been updated in a backwards
compatible way to return JSON API data to the store.
Currently I am the default RESTAdapter:
export default DS.RESTAdapter.extend({
host: 'http://localhost:9990',
namespace: 'api/v1'
});
Which has a custom serailzer for the model:
export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
comments: { embedded: 'always' }
}
});
When I attempt to use the new queryRecord method:
this.store.queryRecord('discussion',{ titleid: self.get('title_id')});
I get the following exception in the logs:
Error while processing route: title.index Assertion Failed: You tried to make a query but your adapter does not implement `queryRecord` Error: Assertion Failed: You tried to make a query but your adapter does not implement `queryRecord`
at new Error (native)
at Error.EmberError (http://localhost:4200/assets/vendor.js:25705:21)
at Object._emberMetalCore.default.assert (http://localhost:4200/assets/vendor.js:15895:13)
at ember$data$lib$system$store$$Service.extend.queryRecord (http://localhost:4200/assets/vendor.js:80502:15)
at loadDiscussionModel (http://localhost:4200/assets/ui.js:2728:32)
at renderTemplate (http://localhost:4200/assets/ui.js:2715:12)
at _emberRuntimeSystemObject.default.extend.setup (http://localhost:4200/assets/vendor.js:37282:14)
at Object.callHook (http://localhost:4200/assets/vendor.js:65078:38)
at handlerEnteredOrUpdated (http://localhost:4200/assets/vendor.js:63868:12)
at setupContexts (http://localhost:4200/assets/vendor.js:63836:9)
serializer/application.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
serialize: function(record) {
return this._super(record, {includeId: true});
},
isNewSerializerAPI: true
});
According to source code default adapter does not have an implementation for queryRecord method: https://github.com/emberjs/data/blob/e89732a5aefb6a81b46927da1c6f048f4aede85e/packages/ember-data/lib/system/adapter.js#L226
Nor it's defined in RESTAdapter, neither in new JSONAPIAdapter. To my mind, this is due to query requests are too specific for every project thus are hard to generalize.
Nevertheless documentation contains explanation and example of implementation: http://emberjs.com/api/data/classes/DS.Adapter.html#method_queryRecord
By the way, there are two errors:
id shold be passed as 4th argument;
type.typeKey should be replaced with typeClass.modelName.
We prefer using simpler implementation in our own project:
export default DS.RESTAdapter.extend({
...
queryRecord: function(store, type, query, id) {
return this.ajax(this.buildURL(type.modelName, id, null, 'query', query), 'GET');
}
});
You can replace id argument with null in buildUrl method if needed.
Update
I forgot to mention, that in ember-data 1.13.5 RESTAdapter's default urlForQuery implementation returns url without actual query parameters passed.
So here's out implementation based on default _buildUrl method, with id replaced by query:
urlForQuery: function(query, modelName) {
var url = [];
var host = this.get('host');
var prefix = this.urlPrefix();
var path;
if (modelName) {
path = this.pathForType(modelName);
if (path) {
url.push(path);
}
}
if (prefix) {
url.unshift(prefix);
}
url = url.join('/');
if (!host && url && url.charAt(0) !== '/') {
url = '/' + url;
}
if (query) {
var queryParams = [];
for(var paramName in query) {
if(query.hasOwnProperty(paramName)) {
queryParams.push(paramName + '=' + encodeURIComponent(query[paramName]))
}
}
if(queryParams.length > 0) {
url = url + '?' + queryParams.join('&');
}
}
return url;
}
This method is in the same adapter as queryRecord from the original answer.
Adding isNewSerializerAPI: true to the all the relevant model Serializers worked to a certain degree (it removed the Error stated below). However the original error still occurrs.
Before due to an incorrect import the following Error in the console logs was not being displayed.
Error: Assertion Failed: is using the
old serializer API and expects
it collaborates with to do the same. Make sure to set
isNewSerializerAPI: true in your custom serializers if you want to
use the new Serializer API.
Also FYI according to the documentation this flag will not be required in Ember Data 2.0:
http://emberjs.com/blog/2015/06/18/ember-data-1-13-released.html
If you have customized your serializer, you should upgrade to Ember
Data 1.13, check the upgrade guide to see if you need to make any
changes, and then set a temporary flag on your Serializer:
isNewSerializerAPI. This will opt you into the new serializer API.
Once you are on the Ember Data 2.0 train, new Serializer API is the
default one, and there is no need for a flag.