Brandon
Brandon

Reputation: 3130

EmberJS / Ember-Data: 404's Not Getting Caught

I am trying to catch 404 errors in my ember app, and redirect to /not-found.

I have an errors action on my ApplicationController, and I have an RSVP.on('error') function too but the 404's aren't getting caught. I just get a 404 error thrown to my console from jQuery, but the error is not getting passed to the error handler.

Console Error

Errors initializer:

import Ember from 'ember';

var initialize = function(container) {
  var errorReporting = container.lookup("service:errorReporting");

  Ember.RSVP.on('error', function(err) {
    Ember.warn("Ember.RSVP error..... Logging error:");
    console.log(err);
    if (err.name && err.name === 'TransitionAborted') {
      Ember.debug("TransitionAborted error. Doesn't look like we should be catching these.");
    } else {
      container.lookup('route:application').send('error', err);
    }
  });

  window.onerror = function(err) { // window general errors.
    Ember.warn("Uncaught error (tripped window.onerror)..... Logging error:");
    console.log(err);
    errorReporting.report(err);
  };

};

export default {
  name: 'errors',
  initialize: initialize
};

The error action on my applicationRoute is huge (and I can post it), but it doesn't even seem to be getting called.

EDIT 1: Route Code

import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';

export default Ember.Route.extend(AuthenticatedRouteMixin, {

  titleToken: function(model) {
    return model.get('name');
  },

  model: function(params) {
    return this.store.find('location', params.location_id);
  }

});

EDIT 2: ApplicationRoute / Error handler

    error: function(err, transition) {
      if (!Ember.isNone(transition)) {
        transition.abort();
      }

      let errorHolder  = this._getErrorDataFrom(err);
      let errorMessage = this._getErrorMessageFrom(errorHolder);
      let isFourOhFour = (typeof(err.status) !== 'undefined' && err.status === 404) || errorHolder.reason === 'not_found';

      if (isFourOhFour) {
        return this.transitionTo('not-found');
      }

      let requireAuthentication = (errorHolder.reason === 'not_authenticated');
      if (requireAuthentication) {
        window.localStorage.setItem('toast-on-reload', errorHolder.message);
        return this.session.invalidate();
      }

      let isValidationError = ( errorHolder.reason === "validation_error" ||
                                ( !Ember.isNone(errorHolder.errors) && !Ember.isNone(errorHolder.message) ) );
      if (isValidationError) {
        this.toast.error(errorMessage);
        return;
      }

      let verificationRequired = (errorHolder.reason === "verification");
      if (verificationRequired) {
        this.toast.error(errorMessage);
        return this.transitionTo('verification');
      }

      let invalidRequest = (errorHolder.reason === 'unprocessable_entity');
      if (invalidRequest) {
        this.toast.error(errorMessage);
        return;
      }

      this.errorReporting.report(errorHolder);
      this.toast.error(errorMessage);
      return this.transitionTo('error');
    }
  },

  _getErrorDataFrom: function(obj) {
    if (!Ember.isNone(obj.responseJSON)) {
      return obj.responseJSON;
    } else if ( !Ember.isNone(obj.success) || !Ember.isNone(obj.errors))  {
      return obj;
    } else if (!Ember.isNone(obj.jqXHR) && !Ember.isNone(obj.jqXHR.responseJSON)) {
      return obj.jqXHR.responseJSON;
    } else {
      Ember.warn("No error handler available, using default ( {} ). Error:");
      console.log(obj);
      return {};
    }
  },

  _getErrorMessageFrom: function(errorHolder) {
    if ( typeof(errorHolder.errors) === 'object' && !Ember.isNone(errorHolder.errors.message) ) {
      return errorHolder.errors.message;
    } else if (!Ember.isNone(errorHolder.errors)) {
      return errorHolder.errors;
    } else if (!Ember.isNone(errorHolder.message)) {
      return errorHolder.message;
    } else {
      return "Sorry, something went wrong.";
    }
  }

Upvotes: 10

Views: 1774

Answers (1)

user663031
user663031

Reputation:

If you want to use the error event, then place its handler inside an actions hash in the application route.

Alternatively, consider the use of an error route. You can define this in pods/application/error, with templates, routes, and controllers just like any other route. See http://guides.emberjs.com/v1.10.0/routing/loading-and-error-substates/#toc_code-error-code-substates. The error code will be passed to that error route as its model.

Finally, in many cases it's most simple and reliable to catch the error from the find.

model: function(params, transition) {
    return this.store.find('location', params.location_id) .
        catch(err => this.send('ajaxError', err));
}

Then define the ajaxError action on your application route which does the same kinds of things you are doing in your error hook now. However, this will catch only ajax errors, not other sorts of errors that might occur during transitions, and be swallowed (or in your case reported by Ember.RSVP.on('error').

Upvotes: 11

Related Questions