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 ?
You'll want to implement a loading route
http://emberjs.com/guides/routing/loading-and-error-substates/
https://gist.github.com/machty/6944577
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.
my solution was quite simple and not the 'ember' way i suppose. but it works nice and doesn't spawn too many new files/helpers:
app/routes/application.js
import Ember from 'ember';
export default Ember.Route.extend(Ember.Evented, {
actions: {
loading: function() {
// enable/disable spinner when model is loading slow
Ember.$('#blur').css('-webkit-filter', 'blur(3px)')
Ember.$('#spinner').css('display', 'flex')
this.router.one('didTransition', function() {
Ember.$('#blur').css('-webkit-filter', 'blur(0px)')
Ember.$('#spinner').css('display', 'none')
});
}
}
});