Aderbal Nunes Aderbal Nunes - 25 days ago 6
AngularJS Question

How to fix injector error after Angular minification build?

Before speaking, I read about it made ​​recommendations but still causing error. Look the short code:

function IndexController($scope, $route, $routeParams, $location){
$scope.sfv = project.version.name;
}
angular.module("TkwebMobile", ['ngRoute', 'ngCookies'])
.controller('IndexController', ['$scope', '$route', '$routeParams', '$location', IndexController]);


Only this and the error persists. I'm using grunt to "uglify", and I'm also using the "concat" to unite the codes in a "lib". Even I using "injection" recommended in the Angular documentation.

Uncaught Error: [$injector:modulerr] Failed to instantiate module TkwebMobile due to:
Error: [$injector:unpr] Unknown provider: a


Is it problem of grunt concat? (grunt-contrib-concat)

Answer

This is due to your minification, specifically options to minify and mangle your variable names.

Angular determines what value to inject into your functions from the name of the parameters. For example...

angular.factory('MyFactory', function($location) {...});

...will cause angular to look for whatever dependency is named '$location' and then call your function with the $location value passed as it's parameter.

When you minify your javascript, with an option called mangle turned on, then the variable names get mangled. The previous function will turn into this...

angular.factory('MyFactory', function(a) {...});

Angular no longer has the correct parameter name in your source code, as $location is now a. This saves on size of your javascript but totally destroys Angular's implicit dependency resolution. You can solve this in one of two ways.

The first is a feature that angular provides for you.

angular.factory('MyFactory', ['$location', function(a) {...}]);

You provide the names of the parameters in an array, with the last element of the array being the function to inject the parameters into. This way, it doesn't matter what you call your parameters in the code, and the minifier will never change a string literal so Angular always knows what you're wanting.

The other way if you don't want to lose the convenience of not having to use the array notation is to turn off the mangle setting on your minifier. This obviously means you don't minify to the same degree, but ask yourself if it's really worth those extra bytes.

A halfway house is to use something like ngMin, to allow annotation of the array notation into your code and then continue with the minification. This is the best of both world's imo, but increases the complexity of deploying your clientside js.

EDIT

The correct settings to turn off the mangle behaviour in grunt would be this...

uglify: {
  options: {
    report: 'min',
    mangle: false
  }
}

But the ngAnnotate package can avoid this. See here for more info. (ngAnnotate is the package that has taken over ngMin)