Skip to content

Instantly share code, notes, and snippets.

@jonnolen
Last active September 10, 2016 03:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonnolen/fff606247f24cae8e81d to your computer and use it in GitHub Desktop.
Save jonnolen/fff606247f24cae8e81d to your computer and use it in GitHub Desktop.
Diagnosing unknown provider issues that occur during minification

Using ng-strict-di directive to find and kill unkown provider errors in minified code.

At commit bb14dc409f7521cb79498 if you load up the CHI end wiht minification on you'll get the following error:

Error: [$injector:unpr] Unknown provider: eProvider <- e

Here is how I used the ng-strict-di directive to find the cause. In this case the page was loading and data was being retrieved so I was able to zero in on it being something non-critical for page function, this led me to look for directives in the actual templates. I commented out the %toaster-output directive in chi.index.histor.html.haml and the error went away. However, when I looked into that directive I couldn't find any missing dependencies in between the $inject = [] and the actual function definitions.

Here is where ng-strict-di helps.

First add ng-strict-di to the application element (here it is in source/chi/index.html.haml)

!!!5
%html{"ng-app" => "chi", "ng-strict-di"=>true}

  %head

When you run minified with this change you'll get a new error:

Error: [$injector:strictdi] e is not using explicit annotation and cannot be invoked in strict mode

The nice thing here is that this will fail now, even if minification is turned off, so I re-ran middleman with no_cssmin set and when I load the page, this is the new error:

Error: [$injector:strictdi] controller is not using explicit annotation and cannot be invoked in strict mode

This error only looks marginally helpful because the method name is controller, but it is helpful enough, in the definition for the toaster-output directive the controller method is named controller (that's a gross name). To verify that this is actually where the problem is happening, I updated the controller method name to be better:

(function(){

  'use strict';

  angular
  .module('common')
  .directive('toasterOutput', toasterOutput);

  toasterOutput.$inject = [];
  function toasterOutput(){

    return {
      restrict: 'E',
      templateUrl: '/templates/modules/chi/notification-banner.html',
      scope: {},
      controller: toasterOutputController,
      controllerAs: 'controller'
    };

    toasterOutputController.$inject = ['$scope', 'commonToaster'];
    function toasterOutputController($scope, commonToaster){
      var vm = this;
      vm.closeToast = closeToast;

      $scope.toasts = commonToaster.getToasts();
      activate();

      function activate(){
        $scope.$watch(
          function(){return commonToaster.getToasts().length;},
          function(){
            commonToaster.markAllAsShown();
            $scope.toasts = commonToaster.getToasts();
          });
      }

      function closeToast(id){
        commonToaster.removeToast(id);
      }
    }
  }
})();

Which now yields the error:

Error: [$injector:strictdi] toasterOutputController is not using explicit annotation and cannot be invoked in strict

So the problem is with the dependencies on the toasterOutputController, which are declared via $inject, so why isn't it working. The problem is that the line toasterOutputController.$inject = []; is an executable line, not a declarative line and it comes after the return statement for it's containing function, easy mistake to make in the Angular.

There was another problem on that page as well. When you click the Quick View button for a delivered CHI, you'd get an unknown tProvider <- t error. It happend right on click, and digging into the code reveals that the only thing that happens there is a state change, I dug into the state change and the controller that should have been responsible was never loading (added console.log to the method that is registered for the controller). Running with ng-strict-di yields the message: onEnter is not using explicit annotation and cannot be invoked in strict mode, so I started at the routes file and dug into dependencies from there. It turns out that statefulModalProvider defines an onEnter with $inject dependencies and the problem was the same. The onEnter.$inject = [...]; was happening after the return statement for the function.

Takeaways

  1. give your methods good names even if they are local, make them easy to grep.
  2. make sure you aren't trying to annotate methods with $inject in unreachable places.
  3. ng-strict-di will surface minification di errors before deploy in non-minified code (how we usually develop)
@mehl321
Copy link

mehl321 commented Jul 18, 2016

Thanks! It helped

@JL-Aus
Copy link

JL-Aus commented Sep 10, 2016

this is so helpful. I was about to curl up in a ball over this. Thanks for writing it. I changed my directive controller and put the inject line below the declaration. Error went away! good write up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment