Recently I was faced with unexpected behaviour when I did javascript minification in angularjs application with help of uglify for grunt . The issue appeared when it minificated my simple (on the first view) state provider and after that router didn't recognize any url changes.

There were no errors in console and no any ideas in my head about what had happened. And I started my own investigation. enlightened

So below you can see pseudo code which I had. We can see, that it's router configuration which defines controller and view for the /login url. Nothing new and it's quite simple.

angular
 .module('app', ['ui.router'])
 .config(appConfig);

appConfig.$inject = ['$stateProvider'];

function appConfig($stateProvider) {
 $stateProvider.state('login', {
  url: '/login',
  templateUrl: 'login/login.html',
  controller: 'LoginController',
  resolve: {
   someResult: function(SomeService) {
    return SomeService.getResult();
   }
  }
 });
}

What is interested us is resolve block. Take a look there is one dependency injection - SomeService. How do you think this code will look like after uglifying? Let me show you.

...

function appConfig(a) {
    a.state("login", {
        url: "/login",
        templateUrl: "login/login.html",
        controller: "LoginController",
        resolve: {
            someResult: function (a) {
                return a.getResult();
            }
        }
    })
}

Have you mentioned something strange? If no, congratulations! Your app doesn't work. Actually it's not very good, so lets find the issue. Uglify was run without mangle option enabled (to perform better compressing), so all the variables' names was cutted down and we can see, that $stateProvied became a and SomeService also became a.

Problem

Honestly the issue is hidden here. The anonymous function injects a provider instead of SomeService and inspite of we don't even have this provider inside the app's scope, angularjs won't tell you about that. You won't see any warnings or whatever. You will just get broken router. So, I think now you've got what's the problem I was faced with. Now let's talk about a solution.

Solution

In fact to make uglifyjs minificate this peace of code properly we need other way of dependency injection. As you know angular offers several ways of dependency injection for controllers, services etc. But there is nothing (have I looked for bad?) is said about DI in resolve block and the only way of doing this I knew I showed you above. But it turns out that angular very well recognizes the same several ways of DI in resolve methods. So to make code work we should rewrite DI:

angular
 .module('app', ['ui.router'])
 .config(appConfig);

appConfig.$inject = ['$stateProvider'];

function appConfig($stateProvider) {
 $stateProvider.state('login', {
  url: '/login',
  templateUrl: 'login/login.html',
  controller: 'LoginController',
  resolve: {
   someResult: ['SomeService', function(SomeService) {
    return SomeService.getResult();
   }]
  }
 });
}

And after uglifying you will be presented with:

...

function appConfig(a) {
    a.state("login", {
        url: "/login",
        templateUrl: "login/login.html",
        controller: "LoginController",
        resolve: {
            someResult: ['SomeService', function (a) {
                return a.getAgencies();
            }]
        }
    })
}

Now everything is fine, application works as expected.

Conclusion

  • Angularjs may not show some of warnings or errors
  • Check minified code when you are not sure if it was minified well
  • Use presented way of DI inside resolve blocks in your angularjs application