I am trying to monitor the state changes on my DS Models.
I have found interesting flags in the documentation:
And they work properly in the templates, I can play with the {{#if model.isLoading}}Is Loading{{/if}}
, for example.
But what is very disturbing is that the observers in these flags have a very random behavior. For example I have found that the observer over the flat isSaving
doesn't work at all. But the observer over the flag isReloading
works perfectly.
Here I have a full example of what I'm have tried until now. I have included this Mixin into my Model:
// app/models/mixins/loading-monitoring
import Ember from 'ember';
export default Ember.Mixin.create({
isReloadingDidChange: Ember.observer('isReloading', function() {
console.log('LOG: isReloadingDidChange', this.get('id'), this.get('isReloading'));
}),
isSavingDidChange: Ember.observer('isSaving', function() {
console.log('LOG: isSavingDidChange', this.get('id'), this.get('isSaving'));
}),
isLoadingDidChange: Ember.observer('isLoading', function() {
console.log('LOG: isLoadingDidChange', this.get('id'), this.get('isLoading'));
}),
isLoadedDidChange: Ember.observer('isLoaded', function() {
console.log('LOG: isLoadedDidChange', this.get('id'), this.get('isLoaded'));
}),
onInit: Ember.on('init', function() {
console.log('LOG: init', this.get('id'));
}),
onDidLoad: Ember.on('didLoad', function() {
console.log('LOG: onDidLoad', this.get('id'));
}),
});
And I try to invoke all these observers but only some ones are triggered:
> $E.toString()
"<reports-dashboard-client-app@route:application::ember1553>"
> var report; $E.store.findRecord('report', 14).then(function(r) { report = r });
Promise {_id: 438, _label: undefined, _state: undefined, _result: undefined, _subscribers: Array[0]}
LOG: init 14
XHR finished loading: GET "http://myapi.com/reports/14".
LOG: onDidLoad 14
> report.toString()
"<reports-dashboard-client-app@model:report::ember1554:14>"
> report.get('title')
"new title"
> report.set('title', 'title modified')
"title modified"
> report.save()
Class {__ember1453996199577: null, __ember_meta__: Meta}
XHR finished loading: PUT "http://myapi.com/reports/14".send
> report.reload()
LOG: isReloadingDidChange 14 true
Class {__ember1453996199577: null, __ember_meta__: Meta}
XHR finished loading: GET "http://myapi.com/reports/14".send
LOG: isReloadingDidChange 14 false
What am I missing?
What is the way to observe the state of the Model and react to changes: loading, ready?
The 'isLoading', 'isLoaded' and 'isSaving' properties are all implemented as computed properties. In Ember, computed properties are lazy and do not compute their state until they are requested for the first time using .get
or displayed in a template. So despite the fact that conceptually we as developers may know the value of isLoading
is changing and will return a different value when record.get('isLoading')
is called, as far as Ember is concerned the value of isLoading
is undefined until some code explicitly asks Ember for that value and Ember takes the time to compute its state.
As a result, when you use Ember.observer
to watch a computed property it will not be able to detect changes in the computed property until some other code has attempted to access the value of the computed property.
Observers also have a few other gotcha that you may want to be aware of. @stefanpenner has a good talk about their issues https://www.youtube.com/watch?v=vvZEddrClAQ and why they should be used sparingly.
In order to get your mixin to fire the observer events you will need to access those properties some where. The best place to do that looks to be in your onInit
function. If you add this.getProperties('isReloading', 'isSaving', 'isLoading', 'isLoaded');
to the onInit
function then Ember will compute these values and the observers will correctly fire when the values of these properties are changed.
Under the hood, Ember Data maintains a state machine for reach record. This state machine is used to track the record's relationship with the server. At a high level these are the states an Ember Data record can be in:
empty
Is the state where each record starts its life. Other then that fact empty
is not very important.
loading
Is the state a record is put in when Ember Data is requesting data from a server for the first time.
loaded
Is the state a record is put in once Ember Data has received some info from the server about the record. It has a number of sub states that are used to track if there are changed made to the record locally that have not been saved to the server. If a record is created on the client side, it will be placed directly into the loaded.created.uncommitted
state.
deleted
is the sate for deleted records. The deleted.saved
substate means the server has acknowledged a delete of this record. The deleted.uncommitted
substate means there record has been deleted locally but record.save()
has not been called.
A record's current state is exposed by the currentState
property on a record. isLoading
, isLoaded
and isSaving
are all aliases for currentState.isLoading
, currentState.isLoaded
, currentState.isSaving
as each state in the state machine has predefined values for all of these properties.
I hope this explanation of the DS.Model's state machine helps.
As an additional thing to check out is the lifecycle events hooks that are exposed on DS.Model
instances.
http://emberjs.com/api/data/classes/DS.Model.html#event_becameError
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With