obo obo - 1 month ago 14
Javascript Question

Emberjs : how to display loading spinner and notification messages on model operations

I am using ember.js 1.2 and facing a problem while trying to display loading spinner and notification messages during the crud operations on my models.

Here is the code :

var MyModelController = Ember.ObjectController.extend({
needs: ['application'],
application: Ember.computed.alias("controllers.application"),
actions: {
save: function() {
var _this = this;

// Display the spinner
this.get('application').get('loading').trigger(true);

this.get('model').save().then(function(response) {
// Hide the spinner
_this.get('application').get('loading').trigger(false);

// Display the success message
_this.get('application').get('flash').success('The model has been updated.');
}, function(response) {
// Hide the loading spinner
_this.get('application').get('loading').trigger(false);

// Display the error message
_this.get('application').get('flash').danger('Error updating the model.');
});
}
}
});


Two main problems here :


  • First : the spinner is displayed with a translation that takes 0.5s but the save operation is performed with less duration and the spinner is displayed and disappears immediately. Here I would like to set a 1s timer before the save operation is called on my model to ensure the animation is performed correctly. How could it be possible ?

  • Second : the success method on my flash object is binded to the specific {{view.message}} on the template. If I call this method outside the 'then' of the promise, the message is well displayed, but in my case it is not as if the binding is not done. Did I miss something in the way to use the promise ? How could it be possible to display this message ?


obo obo
Answer

Concerning the loading spinner : kingpin2k gave a way to a solution. The legacy LoadingRoute is called when a promise is taking to many time to return. So I wrote it this way :

var ApplicationRoute = Ember.Route.extend({
  actions: {
    loading: function() {
      var _app = this.controllerFor('application');
      _app.get('loading').trigger(true);
      this.router.one('didTransition', function() {
        _app.get('loading').trigger(false);
      });
    }
  }
});

Then in the routes I forced my promises to take at least 500ms before returning. So the animation spinner appears and the UI is correctly updated.

model: function() {
  var _this = this;
  return new Ember.RSVP.Promise(function(resolve) {
    setTimeout(function() {
      resolve(_this.get('store').find('model'));
    }, 500);
  });
},

This way works for fetching data through the model hook of the route. But for updating a model from a save button, I wrote a specific ModelController that implements the save action ; then all my controllers that manage a single resource extend it :

var ModelController = Ember.ObjectController.extend({
  needs: ['application'],
  updateOKMessage: null,
  updateKOMessage: null,
  application: Ember.computed.alias("controllers.application"),
  actions: {
    save: function() {
      var _this = this;
      var _app = _this.get('application');

      // Display the loading spinner
      _app.get('loading').trigger(true);

      // Wait during 500ms to let the animation occurs
      setTimeout(function() {

        // Save the model
        _this.get('model').save().then(function(response) {
          setTimeout(function() {
            // Set the message of the flash from a timeout
            // if not, the message does not appear
            _app.get('flash').success(_this.get('updateOKMessage'));
          }, 100);
        }, function(response) {
          setTimeout(function() {
            // Same issue (or maybe it is not an issue)
            _app.get('flash').danger(_this.get('updateKOMessage'));
          }, 100);
          if(response.status === 422) {
            _this.get('model').set('errors', response.responseJSON.errors);
          }
        }).then(function() {
          setTimeout(function() {
            _app.get('loading').trigger(false);
          }, 600);
        });
      }, 500);
    }
  }
});

I corrected the problem with the flash messages setting them from a timer callback instead of from the promise's return method.

Comments