dmathieu - EmberJS observe when an object has been committed

EmberJS observe when an object has been committed

Thursday, 29 March 2012 in Development Articles by Damien Mathieu Creative Commons License

I’ve been using emberjs quite a lot lately, and it’s awesome.
However, I’ve been bitten by a problem over the past few weeks, which I found quite disturbing to resolve.

The context

I have an application using EmberJS with Ember Data to persist the data.
In several parts of my application, I display a form, to update the content of an object.

I’m expecting to be able to redirect the user after the form has been validated and the object has been saved.
But I want to remain on the same page when the save has failed, whatever the reason (the most common being the validation have failed).

However, emberjs doesn’t natively provides anything to observe and get notified when the object has been successfully saved.
You can observe when the object is being sent to save. But that’s too soon.

The solution

So I’ve had to hack into the thing, and my solution (which is probably not the only one) is actually very simple.
I’m adding an internal attribute to all of my models lastCommitAt.

1
2
3
4
5
6
7
8
9
10
11
12
13
DS.Model.reopen({
  lastCommitAt: null,
  saving: false,
  observeSaving: (function() {
    if (this.get('isSaving')) return this.set('saving', true);
  }).observes('isSaving'),
  observeDirty: (function() {
    if (this.get('saving') && !this.get('isDirty')) {
      this.set('saving', false)
      return this.set('lastCommitAt', new Date());
    }
  }).observes('isDirty')
});

What does this do ?

In this code snippet, I reopen the DS.Model class (which is the one all your ember-data models should extend from).
There, I add two internal attributes : lastCommitAt and saving.

Then, I add an observes on the isSaving state. This state is set to true when the object is being sent to the server.
When that’s the case, I want to know it. So I’m setting the saving internal attribute to true.

Finally, I add a second observes on the isDirty state. This state is set to true when the object has any field which has been updated, but hasn’t yet been saved.
So it’ll be triggered with false once the object has been effectively saved.

In that observer, if the object is not dirty (it has been saved) and we were just saving it (the saving value is set to true), I set the value of lastCommitAt.

This way, this attribute is updated every time my model is saved in the database, and at the end of this update.

Then in the view managing my form, I can add the following observer :

1
2
3
4
5
redirectAfterSave: (function() {
  if (this.get('state') == 'inDOM') {
    return MyApp.routes.set('location', "/the/place/where/I/want/to/redirect");
  }
}).observes('content.lastCommitAt')

When my view’s object (the content attribute) is saved, it gets notified and can redirect wherever you want it to.
In the view’s observer, we need to make sure the current view is properly in the DOM.
If it was not, it would mean the view object is instantiated. But we’re currently not displaying it.
In that case, we let thoe displayed view manage the action to do after saving the object.