Iterate over an ember model query - ember.js

this.store.findAll('game').then(function(results){
// RUN SOME OPERATION ON THEM
})
I would like to know how I can play with the results variable. I understand I can do
results.get('firstObject') // returns the first object.
I'd like to know everything else I can do with it. Is there any api documentation for the results collection?
Thanks!

From ember guides,
The below methods, will return the Promise, it will be resolved to Record or RecordArray.
store.findAll() returns a DS.PromiseArray that fulfills to a DS.RecordArray.
store.findRecord returns a promise that will be resolved with the record.
store.query() returns a DS.PromiseArray in the same way as findAll.
The below two are synchronus method, it will retrieve what is available in the store and returns record itself. it will not request the server to fetch data.
store.peekAll directly returns a DS.RecordArray.
store.peekRecord direclty returns record
It's important to note that DS.RecordArray is not a JavaScript
array, it's an object that implements Ember.Enumerable. This is
important because, for example, if you want to retrieve records by
index, the [] notation will not work--you'll have to use
objectAt(index) instead.
From Ember.Enumerable, most of the time I happened to use the following,
forEach to iterate
map to transform to new Array
filterBy findBy for filtering based on single property check
toArray converting to normal native array

findAll will return a Promise which will resolve to a RecordArray.
The RecordArray is an ArrayProxy.
http://emberjs.com/api/classes/Ember.ArrayProxy.html
This is everything you need.
If you google "ember findall" you will find docs for "Ember.js - Models: Finding Records - Guides" as well.
https://guides.emberjs.com/v2.5.0/models/finding-records/

Related

Ember store.findAll is reloading view and store.query is not

At the moment, when an article is added to the store, my view is not updated when I use store.query(), filtering server side, in my route but it's updated when I use store.findAll() with filtering client side.
With findAll, filtering client side
//route.js
model() {
return this.get('store').findAll('article');
}
//controller.js
articleSorted: computed.filterBy('model', 'isPublished', true),
and with query filtering server side
//route.js
model() {
return this.get('store').query('article', { q: 'isPublished' }),
}
The fact is that findAll is reloading and query is not.
I've found this but did not understand
https://github.com/emberjs/ember.js/issues/15256
thanks for the question. I'll try to answer it the best I can but it would seem like some more documentation should be added to the Ember Guides to explain this situation 🤔
Essentially this.store.findAll() and this.store.query() do two very different things. findAll() is designed to be a representation of all of the entities (articles in your case) so it makes sense that the result will automatically update as the store finds more articles it should care about. It does this because it doesn't return an array of articles, it returns a DS.RecordArray that will automatically update.
query() on the other hand is designed to ask the backend every time what it expects the result to be, and you are usually passing a number of parameters to the query() call that the backend is using to find or filter results. It would be impossible for the frontend to know exactly how the backend interprets these query parameters so it is not possible for it to "auto-update" when a new article is added that would satisfy the same query.
Does that make sense? Would you like me to go into any more detail?
When using store.query to fetch data from the server, the view can still be auto-updated with new client-created store data before it's saved to the server, by using a "live" record array for it.
While data from store.query isn't live, data from store.peekAll is, so you can query first but then leverage store.peekAll for display. You can query before setting your model to the peeked data, or keep your query as the model but use some other property of peeked data for display. The important part is to ensure the query is resolved before peeking at the store.
Example based on the current code in your question:
// route.js
beforeModel() {
// using return ensures this hook waits for the promise to resolve before moving on
return this.store.query('article', { q: 'isPublished' });
}
model() {
// Queried server data should now be available to peek at locally,
// so we can set the model to a live version of it. Don't append filterBy here,
// otherwise it will no longer be live.
return this.store.peekAll('article');
}
// controller.js
// seemingly redundant filter since using query, but needed if there are other records
// in the store that shouldn't be displayed, and is recomputed when
// a record is added or removed from the store-based model
articleSorted: filterBy('model', 'isPublished', true) // filterBy imported from '#ember/object/computed'
// template.hbs
{{#each articleSorted as |article|}}
{{!-- this displayed list should update as new records are added to the store --}}
{{article}}
{{/each}}
Note that after a new record is saved to the server, the query can be updated via its update method or via a route refresh. This will re-run the query and get the updated results from the server. If the query is the model, that would look like model.update(). If it was saved to someOtherProperty, then someOtherProperty.update(). In either case, route.refresh() could be used instead to re-run all route hooks.
Some specific comments/examples that I think are helpful:
https://github.com/emberjs/ember.js/issues/15256#issuecomment-302894768
https://github.com/emberjs/ember.js/issues/15256#issuecomment-302906077
https://github.com/pouchdb-community/ember-pouch/issues/232#issuecomment-428927114

Cant retrieve local records from store using Ember Data

I am trying to retrieve records which have already been loaded into the store by using this line in a controller:
var allProducts = this.store.all('product');
However, this is returning a strange object (see screenshot). When I call length on it, the result is "undefined." I have used the Chrome Ember inspector to confirm that records have indeed been loaded into Product before the above line of code is run. I thought since store.all returns a recordarray I could iterate over it immediately unlike a promise. Where am I going wrong please?
The strange object that is returned is a record array. This is important so that Ember can set up observers for arrays that are loaded. I believe this is what is causing your confusion. See more specifics in the docs:
It's important to note that DS.RecordArray is not a JavaScript array.
It is an object that implements Ember.Enumerable. This is important
because, for example, if you want to retrieve records by index, the []
notation will not work--you'll have to use objectAt(index) instead.
You will have to look at the documention for DS.RecordArray but you should be able to iterate over it using the forEach method. See the ember array documentation for more details.
The issue was that I was trying to iterate over the recordarray using a traditional for loop. Seems that a) recordarray cannot return length and 2) one must use a forEach loop to iterate over it, which is what I had initially did but dropped because forEach does not support break or continue.
Ahh promises! :)
You should be able to do this:
var allProducts = this.store.all('product').then(function(products) {
return products;
});

Ember.js ebryn/ember-model differences between Model.find() and Model.fetch()

Good Morning,
why there are two methods, who returning the almost the same result.
I know "only", that the method Method.fetch() returns a promise.
What is the main difference between this two methods?
They return different results :) fetch returns a promise (as you said) immediately, which upon resolution will be your record. find returns a record immediately (possibly empty). If the record already exists client side then it will return that record, if it doesn't, it's an empty record, and once ember model finishes fetching the data it will populate the record with it's properties. They both have their pros/cons. fetch is safer for async programming if you depend on the record being populated before using it. find is convenient for immediate response time, with delayed population. Click run inside the examples a few times to view the differences.
Find
http://jsbin.com/UXOtOJI/20/edit
Fetch
http://jsbin.com/porozuno/1/edit

Find model returns undefined when trying to get the attribute of a model by first finding the model by another attribute?

I would like to do something like:
App.Model.find({unique_attribute_a: 'foo'}).objectAt(0).get('attribute_b')`
basically first finding a model by its unique attribute that is NOT its ID, then getting another attribute of that model. (objectAt(0) is used because find by attribute returns a RecordArray.)
The problem is App.Model.find({unique_attribute_a: 'foo'}).objectAt(0) is always undefined. I don't know why.
Please see the problem in the jsbin.
It looks like you want to use a filter rather than a find (or in this case a findQuery). Example here: http://jsbin.com/iwiruw/438
App.Model.find({ unique_attribute_a: 'foo' }) converts the query to an ajax query string:
/model?unique_attribute_a=foo
Ember data expects your server to return a filtered response. Ember Data then loads this response into an ImmutableArray and makes no assumption about what you were trying to find, it just knows the server returned something that matched your query and groups that result into a non-changable array (you can still modify the record, just not the array).
App.Model.filtler on the other hand just filters the local store based on your filter function. It does have one "magical" side affect where it will do App.Model.find behind the scenes if there are no models in the store although I am not sure if this is intended.
Typically I avoid filters as it can have some performance issues with large data sets and ember data. A filter must materialize every record which can be slow if you have thousands of records
Someone on irc gave me this answer. Then I modified it to make it work completely. Basically I should have used filtered.
App.Office.filter( function(e){return e.get('unique_attribute_a') == 'foo'}).objectAt(0)
Then I can get the attribute like:
App.Office.filter( function(e){return e.get('unique_attribute_a') == 'foo'}).objectAt(0).get('attribute_b')
See the code in jsbin.
Does anyone know WHY filter works but find doesn't? They both return RecordArrays.

Manipulating a RecordArray

I have a RecordArray that has come back from a App.ModelName.find().
I'd like to do some things with it, like:
paginating through the set of records within
adding records from another findQuery into the array
I may be confused, but it seems like it's difficult (or at least undocumented) on how to work with the records that come back from find()/findAll()/findQuery() other than looping over the set and displaying them as normal.
This is further complicated by the array that gets returned from all(), which seems to be closer to an identity map, maybe.
None of this may be possible, but if it isn't I can open issues and start to work on that myself.
The RecordArrays returned by Ember Data aren't really meant for modification. In particular, Model.find() (sans-argument) and Model.all() return live arrays that keep updating as new matching records are available.
If you want to manipulate an array of models, you're best off using Model.find({})(the argument makes it use findQuery()) and observing the isLoaded property. Something like this:
query: null,
init: function() {
// should really do this in the route
this.set('query', Model.find({}));
},
content: function() {
var query = this.get('query');
return query && query.get('isLoaded') ? query.toArray() : [];
}.property('query.isLoaded')
Now content returns a plain old array and you can have your way with it (though you still need to wait for the records to load before you can start modifying the array).
If the issue is that you want a query to keep updating, then consider using Model.filter(), which returns a live array like find(), but accepts a matching function. Note that confusingly, but quite intentionally, none of find(), all(), and filter() have an isLoaded property.
As for pagination, you could try a simple mixin approach, or a more elaborate rails-based solution.