I'm looking to implement some consistent error handling in my app through the error action. The problem I've run into is that it is only triggered when an exception is raised while in a transition, and not, for example, in a plain saveModelInResponseToUserClick action on a route.
I can sort of get something to work with the following, but it's a hack:
Ember.onerror = function(error) {
MyApp.__container__.lookup('router:main').send('handleError')
}
With that, I could have different error-handling logic in differently-nested routes.
EDIT: Another issue with using the method above is when an error occurs before the app has fully transitioned into its first route - another exception is raised in that case and everything blows up.
Is there a different approach I can take with this?
As you said, the error action will only trigger while in transition.
For errors that may arise from an action, you could try using the following:
Ember.RSVP.on('error', function(error) {
// handle error
}
This will catch ANY error that occurs as a result of a promise in your app. You can access the "error" object inside the function, which will give you some detail as to what exactly went wrong, particularly the "responseText" attribute. The code itself goes in your app.js file.
Ember docs on this: http://emberjs.com/guides/understanding-ember/debugging/#toc_errors-within-an-code-rsvp-promise-code
Related
I have a model hook on a route that ends by returning a Promise like:
return route.store.queryRecord(model, {username: params.username});
This works great except it doesn't trigger the "error" action that is anywhere in the chain. It is getting a 404 and logging the error in the console.
If I change the call to "find" with a id that doesn't exist it throws the same 404, but calls the transition to the error state. I cannot use the find for several reasons. Am I missing something simple?
Using ember 2.4.
Looks like queryRecord does not catch errors that are raised in the promise as we can see here: https://github.com/emberjs/data/blob/v2.5.3/addon/-private/system/store/finders.js#L194
What you can do in this case is to encapsulate the call to store.query in a Promise that you would return in your routes model hook, analyze the result of store.query and reject the encapsulating Promise if you get a 404.
Given a route it is easy enough to get Ember to navigate to an error route if the model promise rejects. Currently I have the simple method of having an error.hbs and /routes/error.js, and if the model promise errors it loads the error page.
I have a situation where the model resolves fine, but one of its relationships does not. This relationship is used in the routes template e.g.
{{#each model.addresses as |address id|}}
{{address.line1}}
{{/each}}
So myurl.com/api/addresses/1 returns a 500 error. I see this in the console window, but Ember does not automatically transition to the error page. How can I get it to do this for all promises that reject as a result of a template requesting more data?
One solution would be to return RSVP.Promise in route model() which loads all data (also relationships instead of lazy loading them later by template) and rejects if any of asynchronous requests fail.
Ember generates the error pages by it self if im not wrong, if ember depends on an DS.error object for the transition you have to fulfill the requirements in order to get Ember to recognize an valid error, in Ember 2.x the error code MUST be 422 and has to follow jsonapi http://jsonapi.org/format/#errors-processing
I wrote up a long post on using Route error substates here -> https://pixelhandler.com/posts/emberjs-handling-failure-using-route-error-substates
So ultimately, error handling happens as the result of knowing "what broke" and "who owns the fact that it broke". Generally, if X asked for Y and Y fails, its X's job to display the error. In that example, if FooRoute triggers an ajax, and it fails, FooRoute more or less should handle the error.
But when it comes to the template causing fetches, it actually becomes the responsibility of the template.
One thing one can do is, actually bind to the various boolean properties representing the async operation on the PromiseProxy (in your case, model.addresses). The properties are as follows
isPending - ongoing work is happening
isFulfilled - it is done, and succeeded
isRejected - it is done, but failed
isSettled, - it is done (failed or succeeded)
using these flags could be as follows
{{#if model.addresses.isPending}}
loading...
{{else if mode.addresses.isRejected}}
Sorry something went wrong, {{model.addresses.reason.message}}
{{else}}
{{#each model.addresses as |address| }}
...
{{/each}}
{{/if}}
When loading data for a page, I like to ask myself, what data is critical to show? That is, if there are three sections on the page -- say backed by foo, foo.bars, and foo.baz -- are all three sections of highest priority? Or can I let one of them fail and show the other two? Let's assume that without either foo or foo.bars, we show an error page, but foo.baz is optional. In that case, I would write that as
// controller:foo
model(params) {
return this.get('store').find('foo', params.id);
},
afterModel(foo) {
return foo.get('bars'); // block on foo.bars fetch
}
Then I might have a {{foo-baz}} component that renders Loading... at first, then fetches foo.baz in the background and shows the full content later.
I have an error route and controller defined in my Ember app and it generally works, but if some code throws a simple string, e.g. throw 'invalid argument', the model property on my error controller is always null.
I've created a JSBin example illustrating the problem.
Can anyone explain why throwing an object allows the model to be set but throwing a primitive like string does not? How can I capture the error context if I can't control the code that throws exceptions?
Additionally, it seems if I throw new Error('message') or throw new Ember.Error('message') the error is logged to the console but never triggers the error route transition. Is this intended?
I wrote some code using find method like below.
The adapter is RESTAdapter.
App.SessionManager = Em.Object.extend({
userIdChanged: function() {
App.User.find(this.get('userid')).then(
function(user) {
/* process something */
},
function(error) {
/* rejection handler */
/* I want to know what error returned by the server here.*/
/* but how? */
}
);
}.observes('userid'),
});
If my server returned some error(e.g. 401) the rejection handler called.
But it seems that the argument of handler doesn't have error information.
How can I know the error reason in rejection handler?
Handling of errors returned by the server is not yet fully implemented in ember data.
For reference, as stated in this blog post about ember-data:
We want to make error handling and dealing with client and server conflicts rock solid. A pull request from Paul Chavard is currently open and looks like a solid starting point for error handling. You should see much more development on this in the near future.
But there are some workaround you can do to get to that information in the meanwhile.
For example:
Ember.RSVP.configure('onerror', function(error) {
console.log(error.message);
console.log(error.stack);
});
Or use a dirty hack to get to that information. Since ember-data uses jQuery under the hood you can subscribe to ajaxError:
$(document).ajaxError(function(event, jqXHR, ajaxSettings, thrownError) {
// this will trigger anytime there is an error
});
Hope it helps.
you can also override this:
becameInvalid: function(record) {
// you can get errors just by doing record.errors, error keys must match
// the name of a field that you have defined in your model
}
and:
becameError: function(record) {
}
that's in case something happened in the server that caused an error. I just saw that you want to handle errors on promises, well, that's something different that I haven't dealt with. becameError might be fired.
UPDATE:
I just found something interesting that might help you: Ember-Data handling 401’s thanks to the new router, I haven't had the chance to use all those new features, but they look pretty cool.
I have a property that depends on another property. There is an error in my code that happens when the other property is present. The error is being swallowed by something, probably Ember. This makes debugging the error very hard. I have tried setting Ember.onerror to a function that just logs the error, it makes no difference.
Why is Ember swallowing this error, how can I make it not?
Code:
App.DashboardController = Ember.Controller.extend({
leaderboard: function() {
console.log("calling leaderboard");
var ces = this.get("engagements");
if (ces) {
console.log("before");
throw new Error("bad thing");
console.log("after");
}
console.log("done")
}.property("engagements")
})
Console log:
calling leaderboard
done
(setting engagements)
calling leaderboard
before
Version: Ember.js RC6, development
Turns out the property "engagements" was being set inside a promise fulfilment handler. This article explains how to catch errors that occur in those: http://blog.sensible.io/2013/06/10/promise-ate-my-homework-exception.html