How to handle errors without jumping through hoops?


#1

I have a controller, which deletes a record:

export default Ember.Controller.extend({

  actions: {
    deleteGroup: function(group) {
      group.destroyRecord().then(function() {
        this.get('controller').transitionToRoute('groups');
      }.bind(this), function(error) {
        group.rollback();
        throw 'error';
      }.bind(this));
    }
  }
});

Related to that post: Handle errors with ember data I need to manually call rollback in order to tell ember data to explicitly rollback that failed server call, or the client gets out of sync with the server. However, my problem is with the next line, where explicitly an error is thrown. It is noted in the docs that these errors might be swallowed by the promise and one can use something like this to finally receive the notification:

Ember.RSVP.configure('onerror', function(error) {
  console.log('RSVP error: ' + error);
});

This works as it should, however I would like to display a notification to the user, so he knows that something went wrong. I use a controller to manage my notifications. Now, how do I access a controller from this function? I noticed that I both can’t use this.get('controller') and this.controllerFor('notification'). It appears it does not get picked up by an error route either. What would be the right way to handle errors?

After quite some googling I found this speakerdeck https://speakerdeck.com/elucid/ember-errors-and-you which claims that you can get the app controller from the main App variable by calling App.__container__.lookup('controller:application');. However, I don’t seem to have a __container__ property within the App variable (probably that’s caused by ember cli?). Anyways, there is a quite nice quote on stackoverflow related to the use of that global variable, which probably explains the amount of underscores used :slight_smile: [1]

One of the core developers said that whenever someone tries to use App.container, he would add another underscore.

Isn’t there such a thing as a generic error handler, which I can plug into after the server returned an error and display an appropriate message to the user, which allows me to delegate to a controller to handle the error? Ideally I would like to see a chain like: Did the model handle the error, if not does the route or the controller handle the error? If nothing handles it, is there a hook for the application?

[1] http://stackoverflow.com/questions/17839152/is-it-ok-to-use-app-container-lookup-to-access-instances


#2

I am actually a little surprised how less attention this post has gotten. I can figure out two reasons for that:

  • Either I am raising a question on a topic that is documented very well, and I just missed to find the right place in the docs. If so, I would really be glad if someone could point me to that part of the docs. It really sucks being new to ember and trying to put a good error handling together based on a few hints on stackoverflow and a speakerdeck which proposes to use private APIs.
  • This is a topic where ember fails to fulfil its promise of ‘convention over configuration’. If I look at the mentioned speakerdeck or what the debugger reveals, this more likely might be the case. If so, please lets discuss this and make ember better. There are way too many chances things on the web might break, and they will break. And a web application with poor error handling in the end will be a web application with a bad user experience. I have seen too many posts here on what should be done to get more developers to work with ember instead for instance angular and I am not really sure what ember should do to outrun Angulars marketing, however this is one of the things ember could do to stop people from turning their back on ember. So, please don’t take this lightly.

#3

A little progress: We can at least issue notifications by calling for instance a notification controller’s method from the adapter by doing something like this:

this.get('container').lookup('controller:notification').notify()

That way one could at least handle the display of an appropriate notification in case of an internal server error etc… Still, that seems kind of patchy…


#4

And another little piece to the big puzzle: In the route the error handler of the promise may send error actions which then bubble up to the application route:

actions: {
    delete: function(group) {
      group.destroyRecord().then(function() {
        // transition back to the groups route when delete is complete
        this.transitionTo('groups');
      }.bind(this), function(error) {
        group.rollback();
        // this will propagate the error
        this.send('error', error);
      }.bind(this));
    },
  }

So the key seems to be to manually rollback your changes and then send the error action:

   group.rollback();
   this.send('error', error);

That way you may either implement a specific error handler or have it just bubble up to your generic error handler.

I am still somewhat puzzled why this does not seem to be the default in case nothing else is implemented to handle the error on the model / store…