Marking ember-data records as existing - ember.js

I have a somewhat unique circumstance where I wrote a caching layer for ember-data that serializes records to localstorage. When I deserialize my cache into the ember-data models I use this.store.createRecord('model_name', cacheData);. These records I am caching have existing values on the server. This works for me fine up until I want to save() the record. The save thinks the record is a new record even though it has an "id" attribute. When save() is called a POST is made to my application server instead of a PUT. Does anyone know a way to flag records in the store as not new.

You should use store.push to add already existing records instead of store.createRecord
http://emberjs.com/api/data/classes/DS.Store.html#method_push

I couldn't find anything official, but models do have an isNew property. Since this is a read-only property, you can't set it directly, but you can set it on the currentState object like so:
var model = this.store.createRecord('model_name', cacheData);
model.set('currentState.parentState.isNew', false);
model.get('isNew') // => false
I can't speak to whether this is the best way to do it, but it should do what you're asking. Make sure you have good tests :)

Related

Ember: Edit model object without setting isdirty

This topic har been discussed on stackoverflow before, but not with latest version of ember data, I think. At least none of the suggestions I have found have worked for me.
I use the latest version of Ember and Ember data (versjon 2.13.0). I use the JsonApiAdapter.
Scenario
After I load a record from the server I want to do a few changes to some of its properties. These changes shall not make the record dirty, and the changed attributes shall not show up in the record.changedAttributes(). Any changes the user may do after that shall make the record dirty.
Searching for a solution
I have tried to manually change the isDirty flag, but it didn't do it. I have also tried to find the place in the ember data code that sets the state after a record has been loaded (because essentially I am trying to do the same thing) but I haven't found where it is.
I have also tried record.send('pushedData'), but I didn't change anything of the state of the record.
Any help appreciated.
I know 3 methods which allow to modify server's response without dirtying records:
You can override adapter's handleResponse method and make modifications right in payload.
You can override serializer's normalize method.
You can load records from server with Ember.$.ajax method, modify response and then pass it to store's pushPayload method.
First two methods are good if you need to modify record after every load from server (no matter from what route/controller you do it). Both adapter and serializer can be model-specific. If you need to do it in only one place (controller or route), or if you need an access to route's/controller's variables - 3rd method is good.
I'm not aware about any way to mark record as dirty/not dirty. If you modify a record after it was stored, it becomes dirty.

How to not lose hasMany changes on Ember query?

In my application, I search for documents with query.
Then I edit one attribute in a single document, and then I call search query again.
Result is OK, I see document still in a dirty state with changed attribute.
Then I again pick one of documents and edit its hasMany relation (from 2 items to 4 items). And then I call search query again.
Result is NOT OK, hasMany relation change is lost/disposed/rollbacked.
Is there a way so Ember query (i guess it's some Ember internal reload) does not rollback not saved relation changes ?
I am using Ember 2.9.1
For now i have no other way than prohibit any filter query actions or route actions anything that could call query again, since that would cause lost data that user set.
Ember's store.query method always refetches the models from the backend (unlike the find* methods). With a query, it's the only way to make sure you have the most up-to-date data (and that the models loaded into the store are still valid). Even in your instance, you may run into unexpected results if you change the data such that it no longer meets your query criteria.
If you would like to keep edits around between queries, I would recommend making a copy of all models which are dirty (check the hasDirtyAttributes attribute). You can gather them with peekAll. Once copied, you can then make the query and patch the records by ID. Perhaps by using Ember.assign.
Even using that method, I would still think that you will get into trouble tracking the changes and making sure the records stay consistent with your query. Like what do you if that record wasn't returned (deleted on the server or no longer meets your criteria)? Do you drop your edits? What if you have a conflict between the data from the server and your local version (e.g. another user patched the hasMany relationship that the other user is now querying and modifying)?
ember-changeset seems like it could be useful here. However, it's not obvious how to get a ChangeSet to apply to a new instance of the same model. Though it may be possible to keep a snapshot and match them up manually. However, you still run into data consistency issues.

How do you use in practice findAll and peekAll in Ember?

From EmberJS documentation i get the following two ways to retrieve all records of a given type, one that makes a request and one that doesn't.
var posts = this.store.findAll('post'); // => GET /posts
var posts = this.store.peekAll('post'); // => no network request
It seems to me that i always need to do first a findAll but isn't clear for my understanding when should i do a peekAll.
For example, the user enters my blog and then i get all the posts using findAll, then at some point in the same flow i need all those post, so i should use a peekAll to save bandwidth. So how should i know that i have requested all posts previously ? Should i save some global state to handle that ?
I would assume that the first time the client request a peekAll if there isn't any record it will automatically do a findAll or maybe i should that manually but it probably introduce some boilerplate.
How do you use in practice findAll and peekAll or they equivalents for single record ? Any recommendation ?
.findAll is cached:
First time store.find is called, fetch new data
Next time return cached data
Fetch new data in the background and update
This is the behavior of the new findRecord and findAll methods.
As you can read in Ember Data v1.13 blog post.
So, taking your example:
var posts = this.store.findAll('post'); // => GET /posts
// /\ or load from cache and update data in background /\
var posts = this.store.peekAll('post'); // => no network request
And:
It seems to me that i always need to do first a findAll but isn't
clear for my understanding when should i do a peekAll.
Yes, you need to do first .findAll, but you are encouraged to use .findAll in all places, as it is cached and suited for multiple requests for data (from many places across application without wasting bandwidth).
For example, the user enters my blog and then i get all the posts
using findAll, then at some point in the same flow i need all those
post, so i should use a peekAll to save bandwidth. So how should i
know that i have requested all posts previously ? Should i save some
global state to handle that ? I would assume that the first time the
client request a peekAll if there isn't any record it will
automatically do a findAll or maybe i should that manually but it
probably introduce some boilerplate.
I think user needs to have always up to date data in your application. What if you add blog post while he is browsing page? If you would use .peekAll() then user would need to refresh page to get latest data.
If you would like to save bandwidth then I would recommend you to implement maybe some kind of additional logic in Ember Adapter, but you have to find way to balance user requests with need to always serve latest data. You can do this by overriding Adapter's methods:
shouldReloadAll: function(store, snapshotRecordArray)
shouldBackgroundReloadAll: function(store, snapshotRecordArray)
See more info about these methods in Ember API docs.
How do you use in practice findAll and peekAll or they equivalents for
single record ? Any recommendation ?
If you are completely sure that you always have up to date data after first request then use .peekAll. There is data can be always up to date, because, for example it almost never changes in your database. It depends however what are your needs and how did you design your data models. It's hard to find good example, but maybe imagine if you would have some models which contain only constants. Like PI value etc. Maybe you have imported it from somewhere and it is complete, closed set of something that will never change. Then, after first .findAll, (for example if it's core function to your application it could be defined in Application route beforeModel hook) you would be sure that no more requests are needed and you have all data.
You could also use .peekAll if your application would have something like Offline Mode and can rely only on data you already have.

Ember-Data: Adding Server Queries to AJAX Requests

I am having trouble with a specific case using Ember-Data.
Typically Ember expects a model class, the route, the ajax request, and the returned JSON, to all follow a similar pattern.
The RESTAdapter tries to automatically build a URL to send to the server, which is ok for some situations, but I need full control over some of my request URLs particularly when it comes to appending additional parameters, or matching an API to a route that has a completely different URL structure.
Ember sadly, has no guides for this, though I did find something about the buildURL method
I am not comfortable enough rooting through the source code to find out what happens under the hood though I do not want to break ember data just to fix a few use cases.
I have set my RESTAdapter's namespace to api/rest
The model and resource I want to populate is view-debtors
The specific service I want to reach is at debtor/list
I also need to pass extra parameters for pagination ?page_size=10&page_number=1, for example.
I am completely lost how to do this. I cannot change the API structure... there are too many services depending on them.
Some Small Progress
I went ahead and used my current knowledge to get a little closer to the solution.
I created a model and called it "list"
I extended RESTAdapter for "list" to change the namespace to "api/rest/debtor"
I changed the model hook for "view-debtors" route to store.find('list')
The result now is that the AJAX call is almost correct... I just need to add those extra parameters to the server queries.
This is where I stand now... can I add those server queries via the model hook? or better yet can I also control server queries via ember actions to get new AJAX requests?
Stepping back a bit. Is my method so far a good practice? Because I am using a route's model hook, to set the model to list, will this only work if the routes URL is typed in directly?
So many questions :p
You can find by query which will append a query string onto the end of your request using the object provided.
// this would produce /api/rest/debtor/lists?page_size=1&page_number=10
this.store.find('list', {page_size:1, page_number:10});
Personally I think it's a bit hacky to go fudging the model names and namespace to make it supposedly fit your backend's url structure. It really depends on what you're attempting to do. If you want all the full features of CRUD using Ember-Data for this particular list of data, you're going to be hacking the end-point left and right. Whether or not Ember Data really helps you is questionable. If you are just reading data, I'd totally just fetch the data using jquery and sideload it into Ember Data.
var store = this.store;
$.getJSON('/api/rest/debtor/lists?page_size=1&page_number=10').then(function(json){
//fix payload up if necessary http://emberjs.com/api/data/classes/DS.Store.html#method_pushPayload
store.pushPayload('type', json);
}).then(function(){
return store.all('type'); // or store.filter('type') if you want to filter what is returned to the model hook
});
pushPayload docs

How to show all the data changes since the last commit in Ember data?

I know that Ember data's model has the isDirty attribute, but how can I use it to show a list of all the changes/deltas of the data since the last commit?
Tom Dale's talk (listen until 36:44) mentioned that it is possible to tweak the adapter(github, api) or serializer (github, api) hooks to do that. Can anyone give me an example?
current research: I'm using the local storage adapter which adds stuff to the dirty set, which I think might be what I want. It is found in the ember data store, ember data adapter, ember data relationship changes. I am trying to figure out how everything fits together to show the data changes.
If it's any help, Ember Data stores the new (unsaved) values of attributes in the model's _attributes hash. It's quite internal though so you might want to be careful with it - I would n't recommend manipulating it directly.