Ember data entity id generation - ember.js

When new model is created and saved with RESTAdapter its Id property is undefined, because my backend is responsible for id generation. So when I return to grid there is 2 same entities: first - with empty Id (from RESTAdapter cache, I think) and second - with correct Id returned from backend.
Any ideas? Maybe it is posiible to disable RESTAdapter cache?
UPDATE
My code for entity creation.
submit:function () {
var manager = App.store.createRecord(App.Manager, {
firstName:this.get('firstName'),
lastName:this.get('lastName'),
speciality:this.get('speciality')
});
App.store.commit();
this.get('controller').transitionTo('managers');
return false;
}
NEW UPDATE
Thanks to Mike Grassotti hints. Here some details for my issue.
One antity have Id, another have no Id.
If I remove App.store.commit() code, there is no POST to server and only entity without Id will be displayed.
This entity has isLoaded=false and isError=true.

When new model is created and saved with RESTAdapter its Id property is undefined, because my backend is responsible for id generation.
Right, but there is nothing unusual about this - ember expects id generation to be done by the backend.
So when I return to grid there is 2 same entities: first - with empty Id (from RESTAdapter cache, I think) and second - with correct Id returned from backend.
OK. What do you mean by same 2 entities - surely they are different js objects. Try logging each of them to console like this:
console.log(entityOne.toString());
console.log(entityTwo.toString());
Any ideas?
There are many things that could cause this to happen. Sounds like somehow you are creating two new records and saving just one of them. Or could be the API response does not match what ember expects, causing an extra record to get created.
Try to enable logging on your records, then watch console so you can see what's going on as your model is saved. Hopefully this will give you some insight into when/how the extra record is being created.
record.set("stateManager.enableLogging", true)
Inspect browser communication with your api and compare JSON to see if it matches what the ember rest adapter expects.
Post that JSON and the rest of your source code (model definition, etc.) to Stack Overflow, maybe a second set of eyes will help.
Check this post for some other debugging tips: http://www.akshay.cc/blog/2013-02-22-debugging-ember-js-and-ember-data.html
Maybe it is posiible to disable RESTAdapter cache?
RESTAdapter does not maintain a separate cache of model objects. And since you are not trying to do anything special, there should be no need to take a step like that.

Many thanks to Mike Grassotti, I have found an answer to my question.
The good question was
With App.store.commit() back in, what does the JSON response from
server look like?
+1 for that comment.
I can't find any info in ember-data documentation for that, so some links still would be helpful for me. I change the result returned from backend and everything works fine now.
{
"manager": {
"firstName": "test",
"lastName": "test",
"speciality": "test",
"id": "acd325ac-03eb-419e-be8a-d4ac42e8c235"
}
}

Related

Not possible to use shorthand route handlers if RestSerializer is used? (ember-cli-mirage)

I set up a simple Ember Twiddle to show you my error that is occurring when trying to update a model.
It's considerable that I'm using ember-cli-mirage for mocking the data.
According to the docs, I created a shorthand route that should handle the PUT request.
It does, but with the error: Your handler for the url /api/shops/1 threw an error: Cannot convert undefined or null to object
When using the JSONAPISerializer, everything is working with shorthands (mirage/config.js) and I'm able to update models, but in my case I have to use the RESTSerializer with serialized IDs in the responses.
The request payload when I'm sending the model's attrs are without Id at the end of the property name, f.e.:
// attrs object in PUT request
{
name: "Shop 1",
city: "1" // belongsTo relationship,
}
Now Mirage is trying to find those properties on the respective database model that has to be updated, but cannot find it, because in the database it's cityId and not just city...
I also found this issue report and it’s working, but I was hoping I could avoid something like this. As far as I can remember, in previous versions of ember-cli-mirage (v0.1.x) it was also not needed to override the normalize method in the serializer to be able to make use of the RestSerializer with serializedIds…
My question is:
Is there a way to stick to shorthand route handlers only, or do I really have to write a helper or other custom solution only because I have to use the RestSerializer?
That would be really sad, but at least I would know then.
Thanks for your support!
Short answer: it looks like you need the custom serializer for now until the bug fix for it is merged.
Long answer: that issue looks to be an issue that occurred in the 0.2 -> 0.3 upgrade for Mirage, likely because of underlying DB changes made in Mirage. It'll probably get fixed, but for now you'll need to work around it.

Ember Data expected to find records with the following ids in the adapter response but they were missing

I seem to have a recurring problem when i'm trying to store data using ember.
When i clear my localforage (Chrome Dev Tools -> tab Application) and reload my application, I create a series of request to my API and with the results of those API calls I create and store them in my localstorage.
Oftentimes, right after i cleared my storage i get the following warning:
Ember Data expected to find records with the following ids in the adapter response but they were missing: [1,2]
After that, I get an error
Assertion Failed: The id 1 has already been used with another record for modelClass ocularium-frontend#model:application-settings:
This should never happens since I've cleared my storage and did a refresh. He finds data that's there but actually not there.
Is there a way to fix this and actually save the data? I tried catching it and storing it again. Tried to 'update' it and storing it again, didn't work.
Normally, I get an API response with valid JSON. Ember then uses a model like below, to save it to the store. The model has the same name as the store-type, in this case "application-settings"
import DS from 'ember-data';
export default DS.Model.extend({ name: DS.attr() });
And then to save it I use this code where 'storeKey.type' = "application-settings" and data = the new data.
this.store.createRecord(request.storeKey.type, data).save();
I seem to have found a solution. When clearing your 'keyvaluepairs' in the tab Application you only clear it there but it's still in ember-data. When refreshing it's 'there' but not in the localforage.
Therefore I run over every key in my store and unload it. After that i clear my localforage.
let store = this.get('store');
for (let key in store.typeMaps) {
store.unloadAll(store.typeMaps[key].type.modelName);
}
window.localforage.clear().then(() => {
Ember.Logger.log('EMBER-DATA STORAGE CLEARED');
});
If you then call your functions to save to the store, you won't get above warnings and errors.

How to handle requests for non existing dynamic segments in Ember?

If an user modifies the dynamic segment (object ID) in the URL of an Ember App with Ember Data, what's the best practice to handle these URLs as these might refer to non existing Model entries?
In a minimal example one can observe, that for each call with a non-existent ID (for example http://emberjs.jsbin.com/hurozaju/9#/color/30) there is an empty object added to the local ember data store. This is easily observable by the increasing number of "dots" in the output.
The error-action of App.ColorRoute redirects (as intended) to "colors" in case there is a 404 occurring while fetching the model by ID.
Why is there a "new" Object in the store?
Shouldn't the data be left unmodified?
Is there a chance to prevent the creation of new objects in this case?
I spend some time with this problem and i think this is ember-data beta-7 bug. Please report this issue in github.
here is example code how to work around this issue jsbin. This is tested with data-beta.7 and work and with data-beta.4 not working.
Sorry for not waiting as anounced...
This issue is now reported to ember-data on github.

Filtered arrays and since() functionality in ember-data

I have a backend resource that contains user activities and in the application I would like to present activities based on a single day's worth of activities. I have an ArrayController called ActivitiesController defined in the router like this:
this.resource('activities', { path: '/activities/:by_date' }, function() {
this.route('new');
});
The REST API provides the following GET method:
GET /activities/[by_date]
So far this looks pretty symmetrical and achievable but I'm running into two problems:
Parameterized array find. Typically a parameterized route would be serviced by a ObjectController but in this case the by_date parameter simply reduces/filters the array of activities but it's still an array that's returned. I'm not sure how to structure this in the model hook in the ActivitiesRoute so that its effectively doing a "findAll" rather than expecting a singular resultset.
Since functionality. As there is a reasonable network cost in bringing back these arrays of activities I would like to minimize this as much as possible and the REST API supports this by allowing for a since parameter to be passed along with the date of the last request. This way the server simply responds with a 304 code if no records have been updated since the last call and if there are new records only the new records are returned. Is there anyway to get this "out of the box" with ember-data? Does this require building a custom Adaptor? If so, are there any open source solutions that are available?
p.s. I was thinking that part of the answer to #2 might be to incorporate Alex Speller's Query Parameters: http://discuss.emberjs.com/t/query-string-support-in-ember-router/1962/48
What does your route's model hook look like? I am thinking something like this should work:
model: function(params) {
return this.store.find('activity', { by_date: params.by_date });
}

How to update just 1 record in Ember.js with Ember-data? Currently save() and commit() on 1 record actually updates all records of model

Premise: My question is based on my research of Ember-data, which may or may not be correct. So please correct me if I have any misunderstanding. The examples are running with the latest ember as of July 2, 2013.
To edit a record of my model, just 1 record, you need to call this.get('store').commit() or this.get('model').save(). However, downstream of either of these functions actually have to update all of the records of the model, even those left untouched. So this is quite inefficient, especially if the model has numerous records.
What's the best way to update one and only one record when I'm saving the edits of one record?
UPDATE: this problem only occurs for the local-storage-adapter, not the RESTAdapter.
UPDATE #2: I did have a huge misunderstanding. Everything is okay, save() and commit() both update just 1 record, I've been fooled by local storage adapter _saveData's JSON.stringify(this._data) which printed out all records. I assumed that whatever it printed out was the data that is changed, but turns out in _saveData's callers the records in updateRecords and _didSaveRecords were just the single record I was changing. The statements below about different objects containing "all records of the model" can no longer be duplicated. I guess I misread the debugging information.
It makes sense because _saveData uses localstorage, which currently can only setItem for an entire object, which in my case is the model containing all the records. Since localstorage can't update individual entries of that object, the JSON must contain all the records.
Details:
Running Examples:
this.get('store').commit() is used in doneEditing of updating a post this jsbin.
this.get('model').save() is used in acceptChanges of updating a todo this jsbin.
If you turn on Chrome debug and walk into the above two functions, you'll see something similar to below:
Downstream, there is currentTransaction or defaultTransaction, and both have all records of the model inside.
In the case of get('store').commit(), it eventually calls DS.Store's commit, which in turn calls: (see github)
get(this, 'defaultTransaction').commit();
In the case of case of get('model').save(), it eventualy calls DS.Store's scheduleSave and flushSavedRecords, which call: (see github)
get(this, 'currentTransaction').add(record);
get(this, 'currentTransaction').commit();
Note at the end a commit() is called on xxxTransaction, this is DS.Transaction's commit().
DS.Transactionscommit()has acommitDetails, which is based on xxxTransaction, socommitDetails` also has all the records of the data. (github)
Then DS.Adapter's commit and save are called and indeed every single record is updated (github):
this.groupByType(commitDetails.updated).forEach(function(type, set) {
this.updateRecords(store, type, filter(set));
}, this);
(minor side note/question: when was commitDetails set to "updated"?)
I know that DS.Adapter can be customized, but clearly the problem of saving more than one data (i.e. all of the model entries) are set from DS.Store's commitDefaultTransaction and currentTransaction.
Somehow I feel it would be a bad idea to tinker with DS.Store or anything upstream of DS.Adapter, like making my own version of save() and commit(). Basically I am reluctant to customize anything I'm told not to, since there might be ugly side effects.
So what should I do if I want to use Ember data but can only afford to update one record only at a time?
You can create a new transaction just for managing that record, using transaction() method of the store. This transaction has the same api as the defaultTransaction.
var transaction = this.get('store').transaction();
transaction.add(model)
transaction.commit();
Committing this transaction won't affect other changes. See this blog post for further ideas.