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.
Related
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').
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.
I am building a mixin for different routes to handle a save action. Sometimes after saving I want to do extra things, so I want to send a saveCallback action back to the route.
Is it possible to ask to a route if a action is defined? Or else: can I retrieve all functions of a route, so I could check myself if a route is defined?
Code mixin:
export default Ember.Mixin.create({
actions: {
save: {
var self = this;
this.get('model').save()
.then(function(result) {
//do stuff
//Something like if(self.hasDefined('saveCallBack')) {
self.send('saveCallback')
}
}, function(errorReason) {
//error handling
});
}
}
}
I can't just fire the send action, as a Nothing handled the action 'saveCallback' error is thrown if the action is not defined.
I can't find the issue now, but I read an issue on Github at some point about adding a canSendAction (or something similar) function for this reason. For now, just define the same action on your mixin, and have it bubble.
export default Ember.Mixin.create({
actions: {
save: function() {
var self = this;
this.get('model').save().then(function() {
self.send('saveCallback');
});
},
saveCallback: function() {
return true;
}
}
});
EDIT: I'm being dumb. Just return true and the action should bubble up the hierarchy and will be caught by another action if it exists.
Is there any way to handle 500 status error, when creating a new model e.g
var model = this.store.createRecord('user');
model.save().then(function() {
}, function(error) {
// this callback will not be executed if 500 status response
});
However, I can catch it using becameError event on model, but in this case an error object will not be available.
Thanks for your answers.
It looks like a 500 makes it back to the reject route
http://emberjs.jsbin.com/OxIDiVU/159/edit
You can override DS.Model save and assign values from the error hash to your models
App.Model = DS.Model.extend({
save: function() {
var _this = this;
return this._super().then(function(obj) {
return new Ember.RSVP.Promise(function(resolve) {
return resolve(obj);
});
}, function(error) {
//Do something with error here
_this.set('error_status', error.status)
return new Ember.RSVP.Promise(function(resolve, reject) {
return reject(error);
});
});
}
});
Note. becameError event called before error function in save method, so 'error_status' isn't set when becameError called.
var model = this.store.createRecord('user');
model.save().catch(function(error) {
error.status; // status code
});
I'm using Embers findQuery method and wonder how to catch 404 errors when there are no results?
this.store.findQuery('customer', { hasProjects: true, getArchivedProjects: archived }).then(function(customers) {
});
If the query is empty, the code inside this then function doesn't get fired, so I can't even check the type of customers.
Example:
this.store.findQuery('customer', { hasProjects: true, getArchivedProjects: archived }).then(function(customers) {
console.log('foo')
});
If the query returns a 404, console.log doesn't be fired.
The findQuery function returns a promise. You may then provide two functions to the then(), the first being the success path, the second being the failure path... for example:
this.store.findQuery('customer', { hasProjects: true, getArchivedProjects: archived }).then(function(customers) {
console.log('foo')
}, function(error) { /* do something with error */ });
Alternative answer:
Add a error hook to the corresponding route:
App.CustomersIndexRoute = Ember.Route.extend({
actions: {
error: function(reason) {
if (reason.status === 404) {
// do something ...
}
}
}
})
See: http://emberjs.com/guides/routing/asynchronous-routing/#toc_when-promises-reject.