Closed frapontillo closed 11 years ago
I don't understand. Why does it "of course" fail? Because the code is minified and Config
is renamed? Try listening for $stateChangeError
, or using the error callback in the result of transitionTo()
.
Yes, because it's minified. If I don't correctly declare dependencies in a controller, an error is thrown so I can understand where the error is and how to fix it. It'd be a mess if things start failing silently.
I think that the resolve function is evaluated somewhere in a try block, but the error isn't properly handled, as the console does not display it as it should.
The error is propagated through the error callback of the promise returned by transitionTo()
. Throwing an error would break the model.
Maybe printing a message on the error stream would be considered acceptable? Just my 2 cents.
When you work with promises in angular, it would behoove you to handle the error callbacks in every case. And in such a callback you can log the error yourself. I for one find it annoying when libraries decide on my behalf that they will print something to the log.
edit: if that's what you meant by 'error stream', but now that I'm rereading I'm not sure if that's what you meant.
@frapontillo You should be able to achieve that for all promise rejections with $provide.decorator()
.
The error callback is one thing, a dependency error is another one. For example, the $http
and $resource
services do throw an error when there is a network one (say 404
). And they do return a $promise
.
@stu-salsbury yep, I meant the console.error
stream :)
Funny -- you mention the exact console error that drives me nuts!
I had a typo in one of my Services I was using inside of the resolve
block and was bit by this as well. I like @frapontillo's suggestion of adding a console.error in, around here I think:
I would say the api and concepts in this library are quite large and take time to fully grasp. Most developers probably starting like me with just a few states and grow it from there, not really thinking of handling errors until they have to spend an hour tracking down a silent failure. Duplicate tickets seems to indicate this isn't too uncommon.
I'd be in favor of a console.error entry for broken resolve.
The resolve just needs to be checked at initialization time.
Hi,
I must admit I don't understand the resolution of the issue. Could you please help me understand the status?
My original problem is that any exceptions in the resolver function are caught silently, and when any such exception happens it causes my code breaking silently. We investigated some misterious hangs which left no sign of error. Now I realize that the silent exceptions are the culprit and this is how I found #508 and #696 that are now marked dupes of this issue, which is why I ask here.
@nateabele, you mention: "The error is propagated through the error callback of the promise returned by transitionTo(). Throwing an error would break the model."
When an exception happens in my code in the resolver function, | want to see it in the console. Also, ... yes, it's broken. It will not work anyway since execution stopped at the point of the exception. There is nothing ui-router can do for me at this point.
Could you please help me understand (or point to docs or code) why throwing an error would necessarily break the model? I think this should not be necessary, and perhaps the code can be improved to avoid an exception all-catch which is in general, a very bad thing.
I have the same problem. My controller fails to initialize after I added resolve
and I have no idea why, because the console is silent.
By wrapping every resolve
function in try/catch with console logging in the catch, I get the expected error. But why isn't this automatic?
It should throw an error for missing dependency like so:
$stateProvider.state('main', {
resolve: { myResolve: function (doesNotExist) {} }
});
Error: [$injector:unpr] Unknown provider: doesNotExistProvider <- doesNotExist
@nilskp can you provide an example please? the lack of error catching is driving me nuts!
Something like this (but that obviously won't work for unresolved dependencies):
$stateProvider.state('main', {
resolve: {
myResolve: function() {
try {
// Do stuff
} catch (err) {
return console.log(err);
}
}
}
});
It does seem silly to have to add an event listener to catch errors that angular should obviously be catching.
$rootScope.$on( '$stateChangeError', function (event, toState, toParams, fromState, fromParams, error) {
console.log( 'Resolve Error: ', error);
});
^^^ that is a lot of code just to catch errors that should be thrown
I'm bumping this as another user that spent way too long hunting down a silently swallowed error. I understand that logging level is a bit controversial, but this is an error... maybe an in-between is tracking unresolved loops and logging/throwing the caught error after the timeout -- right now it just hangs indefinitely.
Thoughts?
:strawberry:
Also :+1: for catching errors by listening to the error event, however I'd agree that sometimes the development process doesn't always prioritize these things until later on. If ui-router's opinion is that handling errors like this is a first priority, maybe this strategy should live near the beginning of the documentation, so everyone gets setup with the error catcher before they hunt down any ghosts?
:cake:
Why isn't there a default handler for $stateChangeError that rethrows the error? Is that considered a more annoying default than swallowing?
@aaronyo since $stateChangeError is using angular's $broadcast mechanism, the event emiter and event listener are decoupled so ui-router doesn't know if the error was handled or not. Well you could iterate through the DOM, looking at the internal $$listeners properties on scopes... [1]
If there was an api where ui-state was aware of the listener registration, or if it used direct registration like stateProvider.onError = function () { ... } )
then it could know if you were handling the error or not.
I agree with @nicovalencia that if the lack of error message can't be fixed, it should be featured more prominently in the docs, where resolves
are introduced.
[1]
$('.ng-scope').filter(function () { return $(this).scope().$$listeners['$stateChangeError'] } ).length
+1 for @ratbeard suggestion
Just got bit by this while refactoring from ngRouter to uiRouter. Spent a good chunk of my morning trying to figure why the page is not loading while no errors are showing on console. So +1 for @nicovalencia suggestion.
A console.error never hurt anybody and can be easily done. People could find it annoying because the error was actually expected and was actually catched in some cases? Ok, but that annoyance is far less a deal than an error being silently swallowed! If figuring out if the error was being captured or not is complicated, there could be just a config variable to disable the logging in such cases.
Another thing: stateChangeError only works in transitions! but what if it's the first state you're entering to?
var app = angular.module('myApp', []);
app.run(['$rootScope', function ($rootScope) {
$rootScope.$on('$stateChangeError', function (event, toState, toParams, fromState, fromParams, error) {
throw error;
});
}]);
@AlicanC :+1: This is a must for any app using ui router.
@AlicianC that's the jam right there. Thanks buddy.
Please, log a console error! I don't care about esoteric arguments regarding promises, this has wasted the time of MANY developers and needs to be addressed!
@OndeVai $rootScope.$on("$stateChangeError", console.log.bind(console));
It is a cardinal sin for a library to swallow user errors! Expecting the user to need to try {} catch
or Promise.catch
the error assumes the user knows that there is an error in the first place.
Show me a promise-based library that surfaces any kind of errors outside of promise rejection.
@nateabele RX.js (reactive extensions) will surface an error if there is no handler to accept it.
Rx.js is observable-based, which has the benefit of being explicit about managing handlers and flow as first-class things. I can't think of a way to detect whether a $stateChangeError
handler is registered. The hooks system in 1.0 is a little different, so we could possibly address it then.
People are having this problem since 2013. You either have to fix your library or fix your documentation.
Throwing an error might not be a solution but putting a big fat red box in the documentation which warns users about this is a solution.
Also, does $rootScope.$on("$stateChangeError", console.log.bind(console));
even work if you put it in your state controller? I think this should be in app.run()
and if that is the case, that should also be specified.
its documented at http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$state#events they could be more clear though.
You either have to fix your library or fix your documentation.
I'm open to suggestions on how to do the latter.
It'll be highly recommend to check $stateChangeError by default, maybe we could just add a listener in the library ?
Yes, 1.0 will have a default error handler.
A missing dependency in a state resolve definition is a programming mistake and the library should help us catch these. This is a different from a resolve that fails (returns a rejected promise) which is a run-time error.
I would expect these mistakes to be logged out of the box (and possibly add a config option to suppress this logging for hacks that prefer to ignore errors). For now, I'm forced to do the following in my apps:
$rootScope.$on( '$stateChangeError', function (event, toState, toParams, fromState, fromParams, error) {
if (error.message.match(/\[\$injector:unpr\]/)) {
// Log missing 'resolve' dependency
// See: https://github.com/angular-ui/ui-router/issues/469
console.error("Invalid resolve in state '" + toState.name + "' - " + error.message);
}
});
I've published my workaround as https://github.com/nonplus/angular-ui-router-resolve-error.
Although I'd still prefer/expect this behavior to be built in...
Just wanted to say that this happens still, and that I support a functionality that throws
@nonplus 's workaround was still failing silently, so I simply did the right thing to do: explode at any error, no mercy.
(function (angular) {
'use strict';
angular
.module('YOUR-MODULE-NAME')
.run(throwErrors);
throwErrors.$inject = ['$rootScope'];
function throwErrors($rootScope) {
$rootScope.$on('$stateChangeError', function (event, toState, toParams, fromState, fromParams, error) {
throw error;
});
}
})(window.angular);
@AldoMX That's the correct expected behaviour
This is a horrible experience - I spent days trying to find an error in an unrelated piece of code because an injection error was swallowed. AldoMX's fix should be the default behaviour.
Say I have a
factory
calledConfig
and there is a state such as:The
resolve
function of course fails, but noError
is thrown! When the project is minified (which is the only case when you notice something is wrong) finding this error is a pain; I had to go by trial and error through a lot of files to notice this.