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

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.
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.";
}
}

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').

Related

No "status" in error hook

According to this page, you can have an "error" action in the actions hash in the application route which looks something like this:
actions: {
error: function(error, transition) {
if (error && error.status === 400) {
// error substate and parent routes do not handle this error
return this.transitionTo('modelNotFound');
}
// Return true to bubble this event to any parent route.
return true;
}
}
However, when the server returns a 403 (or presumably any other error status) the "error" argument does not have a "status" property. The only way I can get the status is like this:
actions: {
error: function(error, transition) {
var status = error.errors[0].status;
if(status == '403') {
return this.transitionTo('index');
}
// substate implementation when returning `true`
return true;
}
}
Not a big deal, but it seems wrong to do error.errors[0].status. Am I doing something wrong here?
As I understand, ember tries to pass errors in some 'general' format, suitable for all cases. And in some cases backend may respond with more than one error. I think that's the reason why we have an array of errors in error action.

ng-route does not make template requests

I have a django backed angular app that uses angular-route to resolve the urls
I have a login system that users a factory to verify the authorisation of a user, as mentioned here.
Here is a part of my app.js file
.factory('Auth', function (){
var user;
return {
setUser : function (u)
{
user = u;
},
isLoggedIn : function()
{
var User = false;
return User;
}
}
}
)
.config(
function($routeProvider)
{
$routeProvider
.when('/login', {
templateUrl : '/login',
controller:'LoginCtrl'
})
.otherwise('/')
}
)
.run(['$rootScope', '$location', 'Auth', function ($rootScope, $location, Auth) {
$rootScope.$on('$routeChangeStart', function (event) {
if (!Auth.isLoggedIn()) {
console.log('DENY');
event.preventDefault();
$location.path('/login');
}
else {
console.log('ALLOW');
$location.path('/home');
}
});
}])
I also have a watch set in my MainCtrl.
$scope.$watch(Auth.isLoggedIn,
function(value)
{
if(!value)
{
$location.path('/login')
}
else
{
$location.path('/home')
}
})
My issue is that, the /login template is never requested, even if I manually try /#/login, no errors in console as well.
Also, the location.$path('/login') is also not executed, as page stays right at requested
URL. However, DENY gets printed in the console log.
I suspect that it is due to the app.run attribute , as the template gets rendered if I remove the run attribute.
It is simple : "login" is a state, and you forbid access to any state if the user is not logged in !
You could just change this for example :
.run(['$rootScope', '$location', 'Auth', function ($rootScope, $location, Auth) {
$rootScope.$on('$routeChangeStart', function (event, toState, fromState) {
if (!Auth.isLoggedIn() && toState == 'login' ){
console.log('ALLOW');
}
else if (!Auth.isLoggedIn() && toState != 'login') {
console.log('DENY');
event.preventDefault();
location.path('/login');
}
else {
console.log('ALLOW');
$location.path('/home');
}
});
Also if you want to see a very complete auth system in angular.js, have a glance at this :
https://github.com/angular-app/angular-app/tree/master/client/src/common/security

Ember did not match any routes in your application

What's the best solution to handle 404 error from Ember App.
I tried to catch the error like this :
CL.ApplicationRoute = Ember.Route.extend({
actions: {
error: function(error, transition) {
debugger;
if (error && error.status === 400) {
// error substate and parent routes do not handle this error
return this.transitionTo('modelNotFound');
}
// Return true to bubble this event to any parent route.
return true;
},
}
});
But it seems not to work... :(
Have you any solution ?
I found the solution here : http://dinethmendis.com/blog/2013/7/30/handling-unsupported-urls-with-emberjs
I just added
this.route('missing', { path: '/*path' });
in the router map and then define the missing route.

how to catch HTTP error in Emberjs store.find method?

I am setting a model in CustomerRoute's setupController hook as follows:
var self_controller = controller;
var onSuccess = function(customer) {
var reloadSuccess = function(customer_reloaded) {
// customer is reloaded successfully
self_controller.set('model', customer_reloaded);
};
var reloadError = function(err_response_for_reload) {
if(err_response_for_reload.status == 401) {
terminate_session();
}
else {
HTTP_error();
self_controller.transitionTo('all_customers');
}
};
// reload Model to forcefully fetch from server
customer.reload().then(reloadSuccess).catch(reloadError);
};
var onError = function(reason) {
if(reason.status == 401) {
terminate_session();
}
else {
HTTP_error();
self_controller.transitionTo('all_customers');
}
};
var customer_promise = self_controller.store.find('customer', model.id);
customer_promise.then(onSuccess).catch(onError);
But I am unable to catch HTTP errors (401 unauthorized, 500 Internal Server Error, etc.).
ember-data provides a hook ajaxError to handle all error msgs in DS.RestAdapter. If you write it for ApplicationAdapter it will be used by all models based on RestAdapter. Here is how code looks(extracted from ember docs)
App.ApplicationAdapter = DS.RESTAdapter.extend({
ajaxError: function(jqXHR) {
var error = this._super(jqXHR);
if (jqXHR && jqXHR.status === 422) {
var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"];
return new DS.InvalidError(jsonErrors);
} else {
return error;
}
}
});
Link for same in ember docs http://emberjs.com/api/data/classes/DS.RESTAdapter.html#method_ajaxError

Error handling from adapter

how is possible handle restAdapter errors from store or adapter?
Now I am using this code:
App.ApplicationRoute = Ember.Route.extend({
model: function(){
var self = this;
return this.store.find('item').then(function(data){
return data;
}, function (error){
console.log('error');
return [];
});
},
});
It would be better something more general. Thanks
Until there is some more sophisticated error handling throughout ember data, you could do something like the following to handle network errors in a cross-cutting way:
Extend the RESTAdapter to parse errors from the xhr object
App.ApplicationAdapter = DS.RESTAdapter.extend({
ajaxError: function (jqXHR) {
jqXHR = this._super(jqXHR) || {status : 'unknown'};
var error;
if (jqXHR.status === 404) {
error = 'not_found';
} else if (...) {
...
} else {
error = 'dunno';
}
return error;
}
});
Extend the store to publish error events when bad things happen
App.Store = DS.Store.extend(Ember.Evented, {
recordWasError: function (record, reason) {
this._super.apply(this, arguments);
this.trigger('error', reason);
}
});
Catch the error in your Application Route
App.ApplicationRoute = Ember.Route.extend({
setupController: function () {
this.get('store').on('error', function (error) {
// Do something with the error
console.error(error);
});
},
...
});