how to paginate ember-data relationships - ember.js

How do you paginate the request for related data? For example, if my Person has a thousand Task models attached to it if I do the following, in RESTful thinking, I would get all of them.
var tasks = person.get('tasks');
That would be way too much data. How do I force some query parameter onto the request that works behind the scenes? Ideally to an endpoint with something like this attached to the end of it.
?&offset=3&limit=3
Here is a fiddle to illustrate what I'm trying to accomplish in the IndexController. I have no idea what the "ember way" is to do paginated requests using ember-data.

It didn't exist when this question was first asked, but there is now an addon called ember-data-has-many-query that seems capable of this, at least for RESTAdapter and JSONAPIAdapter. It appears to have some quirks due to ember-data not yet supporting pagination as a first-class concept. If this makes you uneasy, there is always store.query, but this does require your API to support (in your example) a person_id filter parameter on the /tasks endpoint.
Related:
ember-data issue #3700: Support query params when fetching hasMany relationship
json-api issue #509: Pagination of to-many relationships is underspecified
(it doesn't look like this question involved JSON API, but the discussion is relevant)

As today there is still no default way to handle pagination in ember.
First we should probably look at the more simple thing, pagination of a findAll request.
This can be done with something like .query({page:3}), but leads to some Problems:
This is a good solution for classic pagination, but for a infinite-scroll you still need to manually merge the results.
The results are not cached, so moving forward and backward on an paginated list results in a lot of querys. Sometimes this is necessary if the list is editable, but often its not.
For the second problem I build a little addon called ember-query-cache that hooks into the store and allows you to cache the query results. A very short demo is available here.
Now if we talk about a relationship I would honestly recommend to use top level .query until you have better support from ember-data itself:
store.query('task', { person: get(person, 'id'), page: 3 }
There is nothing bad about it. You get your result and have the relationship in the other direction. It works without any hacking into ember-data as long you don't need caching, and if you need caching it requires the very few hacking I've done in my addon.
We still hope for ember-data to become fully JSONAPI complete, and that would require pagination. I think form an API perspective the best thing would be to have the ability to ask for the next and previous page on the ManyArray returned by the relationship. It would along with the JSONAPI where a next and previous link is provided. But to acomplish that now you would have to hack deep into ember-data without getting a big improvement over the top level .query, which I used successfully in many projects.

From the Ember.js guides on using models, you can also submit a query along with the find() call.
this.store.find('person', { name: "Peter" }).then(function(people) {
console.log("Found " + people.get('length') + " people named Peter.");
});
From the guide:
The hash of search options that you pass to find() is opaque to Ember
Data. By default, these options will be sent to your server as the
body of an HTTP GET request.
Using this feature requires that your server knows how to interpret
query responses.

Related

Ember Data: Pushing the data to the store will not replace the changedAttributes of the record?

I am using Ember Data and I have a model say my-model. I am having a realtime notification server to update my application if there is any change for a record. When I am editing an attribute of my-model from UI, the model has some changedAttributes and when the real time notification comes, I am fetching the record from the server and pushing it to the store using store.push(store.normalize('my-model', data)).
Now, In the store the model still has my changedAttributes and it is not replaced. So I believe, the Ember Store will not replace the entire record and will replace only the clean attributes of the record when I do a store.push. I just want to confirm the behaviour. Can someone confirm if my understanding about this is right?
For something like this I think your best bet is to add a test to ember data itself to cover the desired behavior. This would be much more reliable than anything you might hear on Stackoverflow.
I've written a small Ember Twiddle to test that behavior: https://ember-twiddle.com/a8eb87a1c7e5019214320d81af05aca5?openFiles=templates.application.hbs%2C As it shows ember-data does not reset dirty attributes if the record is pushed again into the store - at least not for the tested version 3.4.2, which is a little bit outdated.
I wasn't able to find any tests in ember-data repository that covers your use case but I'm also not that familiar with Ember Data's source code. So you might want to open an issue there or ask on Ember Community Discord or Ember Discussion Forum if this is expected behavior.
To be honest I guess there should be a straight-forward solution to your problem as realtime notification (e.g. through WebSocket) is a common use case.

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-Model: How to establish a hasMany or belongsTo relationship by using a "foreign key"?

Summary
I have a bit of a problem using Ember-Model, trying to establish a unique relationship between two models.
Based on current responses that I have received here on S.O., Ember Forums, and #emberjs. I am beginning to believe that there is no built-in solution for this problem, and I am reformatting my question to specify what is needed.
Details
I am populating a template currently with a full set of debtor information. All the information comes from multiple calls to the server.
The first bit is the basic Debtor info. This part is easy because I can use the model hook and a dynamic segment to retrieve it.
My server returns a JSON for a Debtor... Here's the short version:
{
"debtor" = {
"debtor_id": 1003,
"debtor_name": Steve,
//... more JSON
"debtor_contact_id": 1345
}
}
The dynamic segment for Debtor is filled with the value of the debtor_id, but also notice this debtor has a debtor_contact_id. Every Debtor record retrieved from the server has a unique debtor_contact_id. On the database, this value is a "foreign key" that will tell which contact table belongs to which debtor table.
There is no way to predict which contact info relates to which debtor without this key/value pair.
I currently have "Contacts" belongsTo "Debtor", but that is not enough to do the job.
When it is time to fill the "Contacts" model. Ember-Model needs to know to build the value from debtor_contact_id into the ajax URL as a query parameter in order to GET the correct API.
I am still learning all of this stuff and so far I have not been able to fully follow any tutorials because my use case has an extra step needed somewhere.
This is the expected behavior I am hoping to see:
Model hook will work as expected to pull the specific debtor and put it into a "debtor" model (this part is currently working just fine)
Somehow "debtor_contact_id" is read from the payload
that value is added as part of a server query to find a separate API
the resulting contact info will be pulled into a "contact" model
hopefully a hasMany/belongsTo relationship can be established after both corresponding models are returned.
all this needs to be done in one promise before entering my template
You will also find the question at: discuss.emberjs.com if that is more appropriate place to discuss.
I can elaborate more if this does not make sense... thanks!
Assuming you are using ember-data, alongside the attributes of your model you need to add:
debtor_contact: DS.belongsTo('name_of_the_other_model')
This then provides you a promise which will resolve to the other model on demand. It won't resolve straight away, but bound variables in templates will update as it is resolved. The other API call will be made for you if things are set up properly.
http://emberjs.com/api/data/classes/DS.html#method_belongsTo
The answer I gave here might also be helpful if you need to force resolving the relationship for some reason: Ember Unbound & Belongsto

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

Adding a URL parameter to Ember-Data

I have a well behaving API that I'm going against but in one case there is a twist that I'm hoping Ember can accommodate. Specifically when I call DELETE on an endpoint, the default behaviour is to not truly delete it but rather to set the workflow status to "marked_for_deletion" which makes it invisible to most apps. There are, however, situations where I want to ACTUALLY delete the record and doing this is simply a matter of including a URL parameter of immediate=true. If I were doing this with AJAX it would be as simple as can be but I'm wondering what the "right" way of doing this is with Ember-Data.
Anyone have a view?
must use query parameter. It's not related with ember-data check this guide.
Be careful because API will be changed