I'm still learning AngularJS, which means I can get stuff done but I'm dangerous. A few days ago I ran into a problem that drove me crazy. I was trying to use ngCordova in an existing AngularJS application and kept running into a problem injecting the library into my controller. The error message was less than helpful. Heck, it was useless. Let's look at an example.
I grabbed a copy of the AngularJS seed project (https://github.com/angular/angular-seed) to create a quick simple AngularjS application. (Before this I tried yeoman but after thirty minutes of fighting it and it hanging I decided to give up.) I then set the application to use the minified version of the AngularJS library. That's the key. Then I opened up one of the JavaScript files and modified the dependencies to include something bad (literally):
'use strict';
angular.module('myApp.view1', ['ngRoute','somethingbad'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html',
controller: 'View1Ctrl'
});
}])
.controller('View1Ctrl', [function() {
}]);
The error reported by AngularJS was this:
Error: [$injector:modulerr] http://errors.angularjs.org/1.2.28/$injector/modulerr?p0=myApp&p1=%5B%24injector%3Amodulerr%5D%20http%3A%2F%2Ferrors.angularjs.org%2F1.2.28%2F%24injector%2Fmodulerr%3Fp0%3DmyApp.view1%26p1%3D%255B%2524injector%253Amodulerr%255D%2520http%253A%252F%252Ferrors.angularjs.org%252F1.2.28%252F%2524injector%252Fmodulerr%253Fp0%253Dsomethingbad%2526p1%253D%25255B%252524injector%25253Anomod%25255D%252520http%25253A%25252F%25252Ferrors.angularjs.org%25252F1.2.28%25252F%252524injector%25252Fnomod%25253Fp0%25253Dsomethingbad%25250Az%25252F%25253C%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A6%25253A450%25250AYc%25252Fb.module%25253C%25252F%25253C%25252Fb%25255Be%25255D%25253C%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A20%25253A1%25250AYc%25252Fb.module%25253C%25252F%25253C%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A20%25253A1%25250Ae%25252F%25253C%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A33%25253A267%25250Ar%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A7%25253A288%25250Ae%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A33%25253A207%25250Ae%25252F%25253C%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A33%25253A284%25250Ar%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A7%25253A288%25250Ae%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A33%25253A207%25250Ae%25252F%25253C%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A33%25253A284%25250Ar%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A7%25253A288%25250Ae%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A33%25253A207%25250Aec%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A36%25253A309%25250Adc%25252Fc%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A18%25253A170%25250Adc%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A18%25253A387%25250AWc%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A17%25253A415%25250A%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A216%25253A78%25250Aa%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A146%25253A93%25250Ane%25252Fc%25252F%25253C%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A31%25253A223%25250Ar%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A7%25253A288%25250Ane%25252Fc%252540http%25253A%25252F%25252Flocalhost%25253A8000%25252Fapp%25252Fbower_components%25252Fangular%25252Fangular.min.js%25253A31%25253A207%25250A%250Az%252F%253C%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A6%253A450%250Ae%252F%253C%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A34%253A97%250Ar%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A7%253A288%250Ae%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A33%253A207%250Ae%252F%253C%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A33%253A284%250Ar%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A7%253A288%250Ae%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A33%253A207%250Ae%252F%253C%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A33%253A284%250Ar%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A7%253A288%250Ae%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A33%253A207%250Aec%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A36%253A309%250Adc%252Fc%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A18%253A170%250Adc%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A18%253A387%250AWc%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A17%253A415%250A%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A216%253A78%250Aa%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A146%253A93%250Ane%252Fc%252F%253C%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A31%253A223%250Ar%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A7%253A288%250Ane%252Fc%2540http%253A%252F%252Flocalhost%253A8000%252Fapp%252Fbower_components%252Fangular%252Fangular.min.js%253A31%253A207%250A%0Az%2F%3C%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A6%3A450%0Ae%2F%3C%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A34%3A97%0Ar%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A7%3A288%0Ae%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A33%3A207%0Ae%2F%3C%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A33%3A284%0Ar%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A7%3A288%0Ae%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A33%3A207%0Aec%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A36%3A309%0Adc%2Fc%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A18%3A170%0Adc%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A18%3A387%0AWc%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A17%3A415%0A%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A216%3A78%0Aa%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A146%3A93%0Ane%2Fc%2F%3C%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A31%3A223%0Ar%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A7%3A288%0Ane%2Fc%40http%3A%2F%2Flocalhost%3A8000%2Fapp%2Fbower_components%2Fangular%2Fangular.min.js%3A31%3A207%0A angular.min.js:6
Yeah, have fun with that. In case you don't feel like scrolling over the entire thing, I'll save you the trouble and say it basically is gibberish to anyone who doesn't have a computer as a brain. So I struggled with this for a while until a random StackOverflow post (sorry, forgot to bookmark it!) mentioned that using the minified version of the AngularJS library actually changes how errors are reported. That seemed.... odd. But for the heck of it I switched back. The result is incredibly improved:
Error: [$injector:modulerr] Failed to instantiate module myApp due to:
[$injector:modulerr] Failed to instantiate module myApp.view1 due to:
[$injector:modulerr] Failed to instantiate module somethingbad due to:
[$injector:nomod] Module 'somethingbad' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
http://errors.angularjs.org/1.2.28/$injector/nomod?p0=somethingbad
minErr/<@http://localhost:8000/app/bower_components/angular/angular.js:78:12
module/<@http://localhost:8000/app/bower_components/angular/angular.js:1677:1
ensure@http://localhost:8000/app/bower_components/angular/angular.js:1601:38
module@http://localhost:8000/app/bower_components/angular/angular.js:1675:1
loadModules/<@http://localhost:8000/app/bower_components/angular/angular.js:3877:22
forEach@http://localhost:8000/app/bower_components/angular/angular.js:325:9
loadModules@http://localhost:8000/app/bower_co angular.js:78
In my particular case, it was a bug in ngCordova's custom build code where the top level module couldn't load a lower level module. Switching to the non-minified version fleshed this out immediately.
I have to be honest - I can't imagine why error reporting is something you would get rid of in minification. I guess the hope is that you don't have errors like this in production but I know I never would have guessed this. I typically use the minified version of frameworks like this as I don't expect to need to inspect/debug the library itself. Does anyone know if other frameworks (Ember, etc) also do this?
Archived Comments
OT, but it's my blog post - I can be OT. ;) The Yeoman AngularJS scaffold I started about 30 minutes ago is still hung on grunt-contrib-imagemin. I suppose my goal to create an AngularJS application also needed image minification. And this is why I'm so bothered by tools like Yeoman and Bower making things 999% more complex then they need to be. Why can't I just scaffold an application that is just HTML, CSS and JS w/o anything else?
You need a good Grunt or Gulp workflow. You do a dev build which flushes out all of these problems with good error handling. All the extra kruft that checks for that stuff with good logging is ripped out for production builds for file size reasons. Things like the above shouldn't happen.
"I want to write some code." Run grunt dev
"I want to deploy my website." Run grunt build
Sure - for a "real" app, I can see the value of a Grunt task that would switch the lib from "full" to min when going live, but as I said, I'd never expect error handling to be something impacted by full/min.
Chrome doesn't know about Dependency Injection so Angular has to put in their own error logging. Let's put aside Angular could use more verbose error handling for a minute, and assume it does have SOME built in error handling via verbose throws. If that's the case, these are great to have for development as you've shown, but do NOT need to be in the runtime code once you ship for file size reasons. You shouldn't be debugging production miniified/uglified code. Chrome has formatting support, yes, but that's for bad situations where you don't have the original code.
There are a variety of other reasons, too. Uglify renames variables and functions. Suddenly your stack trace goes from:
cow called moo with 'someVar' and 3
moo called foo
foo called cheese
... to:
a called b with 'someVar'
b called a
c called b
And so on. You have 2 choices at this point. Deal with the fact most won't really care about verbose stack traces, or utilize the function's name parameter to give functions better names for stack traces. Strings, however, are uglify safe; meaning they add to file size. So even if you get a stack trace, it's still hard to read.
This isn't a problem with just uglification, though. That's why Q and Bluebird have the showVerboseStackTraces option; often various anon functions calling each other in this huge stack is completely worthless, so they strip out and make a useful stack for you.
... that doesn't answer why she's all on one line. I believe that's urlencoded perhaps for CSP security reasons.
Fair enough - I guess it is just one more thing that may not be expected but only makes sense after it bites you in the ass. (As it did me.)
And sadly I'll probably get bit in the ass by this again in a year and forget how I fixed it.
You need to:
1. get yourself a gulp or gruntfile you love
2. check that mofo into git
3. use it for every Angular project
That way you have a workflow that prevents the above and is tailored to how you code.