12.28.2013

Flash Messages in Ember (or how to use a proxy object to update Controllers outside of the normal route based approach)

In many different frameworks there is the concept of a flash; a short message to alert a user that something happened on the server backend. This is usually display in the form of a visual alert ala http://getbootstrap.com/components/#alerts

The way I wanted to accomplish this was to pass an additional object in the JSON result of any request. In the form
{
flash:{
msg: 'You do not have permission to that user'
,flashType: 'warn'
}
...
}
// or
{
flashes:[
{
msg: 'The user you requested does not exist'
,flashType: 'info'
},
...
]
...
}
view raw gistfile1.txt hosted with ❤ by GitHub

However, because I want this functionality available anywhere in the App, not just in a flash route, I had to get a bit creative. The route (no pun intended) that worked the best is to use a proxy object set on the App and then bound to the controllers content property.
"use strict";
App.set('flashProxy', Ember.A());
App.FlashController = Ember.ArrayController.extend({
itemController: 'flashObject'
,contentBinding: 'App.flashProxy'
});
App.FlashObjectController = Ember.ObjectController.extend({
actions: {
dismiss: function(flash) {
var flashProxy = App.get('flashProxy')
,newList = _(flashProxy).reject(
function(obj){
var fId = flash.get('id')
,oId = obj.get('id')
;
return oId == fId;.
}
)
;
App.set('flashProxy', newList);
// this is a private method, but it does exactly what I want to happen
// ala, removes it from the local store and doesn't send a delete to the webserver
flash.unloadRecord();
}
}
});
view raw gistfile1.txt hosted with ❤ by GitHub

In the model definition I used the didLoad event to have the model place it's self into the proxy
"use strict";
App.Flash = DS.Model.extend({
msg: DS.attr('string')
,flashType: DS.attr('string')
,didLoad: function(){
this._super();
App.flashProxy.pushObject(this);
}
});
view raw gistfile1.txt hosted with ❤ by GitHub

Finally I created a route subclass that will add the controller to every route
"use strict";
App.BaseRoute = Ember.Route.extend({
renderTemplate: function () {
this._super();
this.loadAdditionalControllers();
}
,loadAdditionalControllers: function(){
this.render('flash',{
controller: 'flash'
,outlet: 'flash'
});
}
});
view raw gistfile1.txt hosted with ❤ by GitHub

Thats it, I hope it helps you out.