peekRecord() is not working but peekAll() is working - ember.js

My backend always responds with all available data and it took a considerably amount of time. So I'm reloading store periodically and I plan to use peekAll() and peekRecord().
My code is:
model: function() {
return Ember.RSVP.hash({
'clusters': this.store.peekAll('cluster'),
'single': this.store.peekRecord('cluster', 'cluster::My')
});
When code is executed, at first I can see that both of these items do not contain content. After few seconds data are loaded to store and I can see content 'clusters' on template as expected. But 'single' is still completely without content ({{model.single}} does not return nothing in template). But when I have a button with action:
alert(this.store.peekRecord('cluster', 'cluster::My'));
I can see that the record was found. Records are also available via Ember Inspector. What am I doing wrong that only peekAll() works in model for me.

The semantics of both methods are:
store.peekAll returns a live array that is updated as the store is updated.
store.peekRecord returns the corresponding object in the current cache, or null, and it does not update.
So the behaviour you're observing is the expected one. If you want to use the peek methods, my advise is to make sure that the initial request has finished loading before fetching any data from the store.

Related

Emberjs inside of get computed making request to backend multiple times cause infinite loop

I have a table basically in every row i have get function that makes a backend request with store service. But somehow when there is one row it works expect, but when there is multiple rows it always try to recalculate get function which makes sending infinite request to backend. I am using glimmer component
I cannot use model relation on ember side at this point, there is deep chain on backend side. Thats why i am making backend request.
get <function_name>() {
return this.store.query('<desired_model_name>', { <dependent1_id>: <dependent1_id_from_args>, <dependent2_id>: <dependent2_id_from_args> });
}
I fixed this problem with using constructor. But do you have any idea why this get function re-calculate all the time? Dependent_ids are constant.
Weird thing is when results are [] empty array it does not re calculate every time. Even the query results are same it still try to recalculate every time and making infinite request to backend.
But do you have any idea why this get function re-calculate all the time?
When something like this happens, it's because you're reading #tracked data that is changed later (maybe when the query finishes).
because getters are re-ran every access, you'll want to throw #cached on top of it,
// cached is available in ember-source 4.1+
// or as early as 3.13 via polyfill:
// https://github.com/ember-polyfills/ember-cached-decorator-polyfill
import { cached } from '#glimmer/tracking';
// ...
#cached
get <function_name>() {
return this.store.query(/* ... */);
}
this ensures a stable object reference on the getter that the body of the getter only re-evaluates if tracked data accessed within the getter is changed.
Weird thing is when results are [] empty array it does not re calculate every time. Even the query results are same it still try to recalculate every time and making infinite request to backend.
Given this observation, it's possible that when query finishes, that it's changing tracked data that it, itself is consuming during initial render -- in which case, you'd still have an infinite loop, even with #cached (because tracked-data is changing that was accessed during render).
To get around that is fairly hard in a getter.
Using a constructor is an ok solution for getting your initial data, but it means you opt out of reactive updates with your query (if you need those, like if the query changes or anything).
If you're using ember-source 3.25+ and you're wanting something a little easier to work with, maybe ember-data-resourecs suits your needs
the above code would be:
import { query } from 'ember-data-resources';
// ...
// in the class body
data = query(this, 'model name', () => ({ query stuff }));
docs here
This builds off some primitives from ember-resources which implement the Resource pattern, which will be making a strong appearance in the next edition of Ember.

Ember Data - ArrayController's content does not reload, after adding new items by store.pushPayload method

I have a list of items handling by Ember.ArrayController. I'm doing some PATCH action on records, which updates existing items and adding a new ones, if it is needed from the context. All changes I'm sending back from the server and I'm pushing it to the store by using Store.pushPayload() method.
I do something like that:
All changes in existing records are automatically updated - so observers of particular items are run.
Unfortunately when I have a new items in payload - they do not appear on the list - observes of ArrayController.content are not called.
I also tried to manually notify ArrayController about the changes by doing:
_this.store.pushPayload(response);
var tasksController = _this.get('controllers.tasks');
tasksController.contentDidChangedManually();
And in controller:
contentDidChangedManually: function() {
this.set('contentChangedManually', new Date().getTime());
},
filteredContent: function() { // my content filters... }.property('arrangedContent', 'contentChangedManually')
But it does not work, because contentDidChangedManually() is run before pushing a payload is done. Unfortunately, Store.pushPayload() does not return a promise, so I can't run it when new records are ready.
Does anyone have a solution for this? Thanks in advance.
It sounds as if you are using find to load your model data, you likely want to use DS.Store.filter instead as that returns a live updating record array:
http://emberjs.com/api/data/classes/DS.Store.html#method_filter

clarification of Ember's this.get() method

This is more of a general question than anything specific, but I'm new to ember and don't really understand when and how to use Ember's this.get('foo') (and similarly bar.get('foo')).
For example, in my route I have a user object on which there is a property called credits
user = this.store.find('user', userId)
console.log(user)
credits = user.get('credits')
console.log(credits)
my console.log shows me that user.content._data.credits has a value and also has a methods called get content and - more specifically - get credits. However, console.logging credits always returns undefined.
if i set the user as a model though, using this.get('user.credits') in my controller works fine.
I've read the docs about the advantages .get offers with computed properties, but could anyone concisely explain some ground rules of when to use this.get('foo') vs. bar.get('foo') and why it works in some places but not others.
Thanks!
You always need to use Em.get and Em.set for getting and setting properties of an Ember.Object. It's the basic rule. Without it you may find variety of bugs in observers/rendering and other places.
There is a misunderstanding of operations flow in your code: this.store.find always returns a promise object, not the actual data that you request. Detailed:
user = this.store.find('user', userId) // user - Em.RSVP.Promise object
console.log(user) // logs the Em.RSVP.Promise object
credits = user.get('credits') // gets property 'credits' of the Em.RSVP.Promise object (user)
console.log(credits) // always logs `undefined` because there is no property called 'credits' in Em.RSVP.Promise prototype
We must to rely on async nature of Promise and to rewrite this code like this:
this.store.find('user', userId).then(function(user) {
console.log(user) // logs the App.UserModel object with actual data
credits = user.get('credits') // gets property 'credits' of the App.UserModel instance (user)
console.log(credits) // logs real data from the model
});
There is another important part of getting properties from a model object, if you're using ember-data as data layer: you need to declare all fields of the model that you wish to get afterwards.

Ember.js Data how to clear datastore

I am experiementing with Ember.js and have setup a small app where users can login and logout. When the user logs out I want to clear all of the currently cached records in the Data Store.
Is there a way to do this or would I have to force the browser to reload the page?
I know this question is from 2013. But since Ember Data 1.0.0-beta.17 (May 10, 2015) there's a straightforward way of clearing the datastore:
store.unloadAll()
(More here: http://emberigniter.com/clear-ember-data-store/)
It looks like, as of today, there is still no generic way of fully forcing a store cleanup. The simplest workaround seems to loop through all your types (person, ...) and do:
store.unloadAll('person');
As seen here
Clearing the data store is not yet supported in Ember-Data. There is an open issue concerning this on the Github tracker.
A cleaner & generic approach. Just extend or reopen store & add a clear method like this.
DS.Store.extend({
clear: function() {
for(var key in this.typeMaps)
{
this.unloadAll(this.typeMaps[key].type);
}
}
});
There is:
App.reset();
But that does more than clear out the data store and we've occasionally seen errors where store.pushPayload tries to push data onto an object marked destroyed from calling App.reset();.
Been we've been using:
store.init();
Which just creates a new empty store and works great but unfortunately is a private method.
This can now be done with store.destroy(). It unloads all records, but it also available for immediate use in reloading new records. I have confirmed this as of 1.0.0-beta.15. It doesn't appear to be in the documentation, but it's been working for me.
The alternative would be iterating the store's typeMaps and running store.unloadAll(typeMap.typeName), but I'm not sure it's entirely necessary.
Deleting record by record in model. deleteOrgs of this jsBin:
deleteOrgs: function(){
var len;
while(len = this.get('model.length')) {
// must delete the last object first because
// this.get('model.length') is a live array
this.get('model').objectAt(len-1).deleteRecord();
}
this.get('store').commit();
}
( As of August 2013, there is currently a problem with lingering deleted data. )

How to make ArrayController ignore DS.Store's transient record

I have a list of clients displayed through a ClientsController, its content is set to the Client.find() i.e. a RecordArray. User creates a new client through a ClientController whose content is set to Client.createRecord() in the route handler.
All works fine, however, while the user fills up the client's creation form, the clients list gets updated with the new client record, the one created in the route handler.
What's the best way to make RecordArray/Store only aware of the new record until the record is saved ?
UPDATE:
I ended up filtering the list based on the object status
{{#unless item.isNew}} Display the list {{/unless}}
UPDATE - 2
Here's an alternative way using filter, however the store has to be loaded first through the find method, App.Client.find().filter() doesn't seem to behave the way the two methods behave when called separately.
// Load the store first
App.Client.find();
var clients = App.Client.filter(function(client){
console.info(client.get('name') + ' ' + client.get('isNew'));
return !client.get('isNew');
});
controller.set('content',clients);
Few ways to go about this:
First, it's very messy for a route/state that deals with a list of clients to have to go out of its way to filter out junk left over from another unrelated state (i.e. the newClient state). I think it'd be way better for you to delete the junk record before leaving the newClient state, a la
if(client.get("isNew")) {
client.deleteRecord();
}
This will make sure it doesn't creep into the clientIndex route, or any other client list route that shouldn't have to put in extra work to filter out junk records. This code would ideally sit in the exit function of your newClient route so it can delete the record before the router transitions to another state that'll called Client.find()
But there's an even better, idiomatic solution: https://gist.github.com/4512271
(not sure which version of the router you're using but this is applicable to both)
The solution is to use transactions: instead of calling createRecord() directly on Client, call createRecord() on the transaction, so that the new client record is associated with that transaction, and then all you need to do is call transaction.rollback() in exit -- you don't even need to call isNew on anything, if the client record was saved, it obviously won't be rolled back.
This is also a useful pattern for editing records: 1) create a transaction on enter state and add the record to it, e.g.
enter: function(router, client) {
this.tx = router.get("store").transaction();
this.tx.add(client);
},
then the same sort of thing on the exit state:
exit: function(router, client) {
this.tx.rollback();
},
This way, if the user completes the form and submits to the server, rollback will correctly/conveniently do nothing. And if the user edits some of the form fields but then backs out halfway through, your exit callback will revert the unsaved changes, so that you don't end up with some dirty zombie client popping up in your clientIndex routes display it's unsaved changes.
Not 100% sure, could you try to set the content of ClientsController with
Client.filter(function(client){
return !client.get('isNew'));
});
EDIT: In order to make this work, you have to first load the store with Client.find().