Cant retrieve local records from store using Ember Data - ember.js

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;
});

Related

Iterate over an ember model query

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/

Update view when pushing models to the store

I have quite a complex page in my application with lots of different models being shown. I live-update several of these models through a /updates REST call. I pass a last_request_timestamp parameter and it returns the models that were created or modified since the last call.
I add the models to the store by using store.push(model, model_json). However, the templates are not updated after the models have been pushed. How can I ensure that there is a binding between the models in the store and the view?
Ok, I figured it out. The Ember.js FAQ says
Filters, on the other hand, perform a live search of all of the records in the store's cache. As soon as a new record is loaded into the store, the filter will check to see if the record matches, and if so, add it to the array of search results. If that array is displayed in a template, it will update automatically.
...
Keep in mind that records will not show up in a filter if the store doesn't know about them. You can ensure that a record is in the store by using the store's push() method.
So in the controller for the view that I want to live-update, I use filter() on the store to fetch the models.
App.PostController = Ember.ObjectController.extend({
comments: function() {
var postId = this.get('id');
return this.get('store').filter('comment', function(comment) {
return comment.get('post.id') == postId;
});
}.property('comments')
});
Now, whenever I push() a new Comment to store, it is automatically added to the appropriate post in the view.
You probably need to explicitly push them into a collection that is being represented on the page by using pushObject. store.push will return a live record, so you could do something like this.
var book_record = store.push('book', model_json);
this.get('controllers.books.content').pushObject(book_record);
That's assuming that the BooksController is a standard ArrayController.
Unfortunately it requires two steps (but simple steps). You need to add it to the store and then save the record for it to propagate changes to listeners via the adapter.
var pushedRecord = store.push(model, model_json);
pushedRecord.save();
Also if you have multiple records you can call pushMany instead of pushing each individually. You still have to save each.
store.pushMany(model, jsonArray).forEach function (pushedInstance) {
pushedInstance.save();
}

How to "replace" an item inside an ember array as of 1.0

In ember.js RC5 I was able to replace 2 elements inside an array of ember objects like so
appointments.replace(starting-index, number-of-elements-to-remove);
but today that doesn't actually "splice out" the elements as it did pre ember 1.0
If I try to hack it and splice the array myself like so, it does nothing (no error either)
[].concat(appointments).splice(starting-index, number-of-elements-to-remove);
Anyone know how to remove / replace elements from a list of ember objects as of 1.0?
Update
The ember inspector shows the model type as DS.RecordArray (using the latest ember-data 1.0 beta 3)
I tried the removeAt but it still doesn't work sadly
This is how I populate the array in my app
var appointments = self.store.all('appointment');
Why not use removeAt instead?
It has the same signature as you need removeAt(start, length).
See here http://emberjs.com/api/classes/Ember.ArrayController.html#method_removeAt
Hope it helps.
That definitely still works, are you sure it's an ember array? Or is this an Ember Data RecordArray?
http://emberjs.jsbin.com/aKonIk/1/edit
Oh, the real issue is store.all. It is a filtered record array. One of the special properties of Filtered Record Arrays is they are live, meaning they stay up to date. Filtered RecordArray:
** Takes a type and filter function, and returns a live RecordArray that remains up to date as new records are loaded into the store or created locally.
Meaning as you remove the records, they probably are getting reinserted.
I'll test this out, but I'm pretty sure it will always stay up to date with the latest records in the store, so you'd either need to copy the records to a different array, or delete the records from the store.
Update
It appears it still works as well (at least in my simplistic example below)
http://emberjs.jsbin.com/uHiCAc/3/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.