Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EmberJS, understanding DS Model life cycle

I am trying to monitor the state changes on my DS Models.

I have found interesting flags in the documentation:

  • isLoading
  • isReloading
  • isSaving

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?

like image 735
fguillen Avatar asked Feb 01 '16 20:02

fguillen


1 Answers

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.

The DS.Model Lifecycle

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
  • loading
  • loaded
    • created
      • uncommitted
      • inFlight
    • saved
    • updated
      • uncommitted
      • inFlight
  • deleted
    • saved
    • uncommitted
    • inFlight

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

like image 64
bmac Avatar answered Sep 24 '22 16:09

bmac