web-dev-qa-db-fra.com

"Fournisseur inconnu: aProvider <- a" Comment trouver le fournisseur d'origine?

Lorsque je charge la version simplifiée (via UglifyJS) de mon application AngularJS, le message d'erreur suivant s'affiche dans la console:

Unknown provider: aProvider <- a

Maintenant, je me rends compte que cela est dû à un nom variable. La version non démêlée fonctionne très bien. Cependant, je souhaite utiliser la modification de nom de variable, car elle réduit considérablement la taille de notre fichier de sortie JS.

Pour cette raison, nous utilisons ngmin dans notre processus de construction, mais cela ne semble pas résoudre le problème, même s'il nous a bien servi par le passé.

Donc, pour résoudre ce problème, j’ai activé les cartes source dans notre tâche de grogner uglify. Elles sont générées correctement et Chrome charge les cartes depuis le serveur. Pourtant, je reçois toujours la même erreur message d'erreur, même si j'avais l'impression que je devrais maintenant voir le nom d'origine du fournisseur.

Comment obtenir Chrome pour utiliser les mappes source afin de me dire quel fournisseur est le problème ici ou bien comment puis-je trouver le fournisseur d'une autre manière?

99
Der Hochstapler

J'aimerais toujours savoir comment j'aurais pu trouver l'endroit dans notre code source qui est à l'origine de ce problème, mais j'ai depuis pu le localiser manuellement.

Il y avait une fonction de contrôleur déclarée sur la portée globale, au lieu d'utiliser un appel .controller() sur le module d'application.

Donc, il y avait quelque chose comme ça:

function SomeController( $scope, i18n ) { /* ... */ }

Cela fonctionne très bien pour AngularJS, mais pour que cela fonctionne correctement avec le brassage, je devais le changer en:

var applicationModule = angular.module( "example" );
function SomeController( $scope, i18n ) { /* ... */ }
applicationModule.controller( "SomeController", [ "$scope", "i18n", SomeController ] );

Après d’autres tests, j’ai trouvé des instances de plusieurs contrôleurs qui posaient également des problèmes. Voici comment j'ai trouvé la source de tous manuellement :

Tout d’abord, j’estime qu’il est assez important de permettre l’embellissement de la sortie dans les options d’aggravation. Pour notre tâche difficile, cela signifiait:

options : {
    beautify : true,
    mangle   : true
}

J'ai ensuite ouvert le site Web du projet dans Chrome, avec DevTools ouvert. Il en résulte une erreur comme celle ci-dessous enregistrée:

enter image description here

La méthode dans la trace des appels qui nous intéresse est celle que j’ai marquée avec une flèche. C'est providerInjector dans injector.js . Vous allez vouloir placer un point d'arrêt où une exception est générée:

enter image description here

Lorsque vous relancez maintenant l'application, le point d'arrêt sera atteint et vous pourrez remonter la pile d'appels. Il y aura un appel de invoke dans injector.js , reconnaissable à partir de la chaîne "Jeton d’injection incorrect":

enter image description here

Le paramètre locals (modifié en d dans mon code) donne une assez bonne idée de quel objet de votre source est le problème:

enter image description here

Un rapide grep sur notre source trouve de nombreuses instances de modalInstance, mais à partir de là, il était facile de trouver cet endroit dans la source:

var ModalCreateEditMeetingController = function( $scope, $modalInstance ) {
};

Ce qui doit être changé en:

var ModalCreateEditMeetingController = [ "$scope", "$modalInstance", function( $scope, $modalInstance ) {
} ];

Si la variable ne contient pas d'informations utiles, vous pouvez également sauter plus haut dans la pile et lancer un appel à invoke, qui devrait comporter des astuces supplémentaires:

enter image description here

Empêcher que cela se reproduise

Maintenant que vous avez, espérons-le, trouvé le problème, je pense que je devrais mentionner la meilleure façon d'éviter que cela ne se reproduise à l'avenir.

Évidemment, vous pouvez simplement utiliser le annotation de tableau en ligne partout, ou le (selon vos préférences) $inject annotation de propriété et essayez simplement de ne pas l'oublier à l'avenir. Si vous le faites, assurez-vous d'activer mode d'injection de dépendance stricte , pour détecter les erreurs de ce type plus tôt.

Fais attention! Si vous utilisez Angular Batarang, StrictDI risque de ne pas fonctionner pour vous, car Angular Batarang injecte du code non annoté dans le vôtre (mauvais Batarang!).

Ou vous pouvez laisser ng-annotate en prendre soin. Je le recommande vivement, car cela supprime beaucoup de risques d’erreurs dans ce domaine, comme par exemple:

  • Annotation DI manquante
  • Annotation DI incomplète
  • Annotation DI dans le mauvais ordre

Garder les annotations à jour est tout simplement une tâche difficile et vous ne devriez pas avoir à le faire si cela peut être fait automatiquement. ng-annotate fait exactement cela.

Il devrait s'intégrer facilement dans votre processus de construction avec grunt-ng-annotate et gulp-ng-annotate .

191
Der Hochstapler

La rédaction d'Oliver Salzburg était fantastique. Upvote.

Astuce pour ceux qui pourraient avoir cette erreur. La mienne a simplement été provoquée par l'oubli de passer un tableau pour un contrôleur de directive:

MAUVAIS

return {
    restrict: "E",
    scope: {                
    },
    controller: ExampleDirectiveController,
    templateUrl: "template/url/here.html"
};

BIEN

return {
    restrict: "E",
    scope: {                
    },
    controller: ["$scope", ExampleDirectiveController],
    templateUrl: "template/url/here.html"
};
30
Ash Clarke

utiliser ng-strict-di avec ng-app

Si vous utilisez Angular 1.3), vous pouvez vous épargner un monde de blessures en utilisant la directive ngStrictDi avec ngApp:

<html lang="en" ng-app="myUglifiablyGreatApp" ng-strict-di>

Maintenant - avant la minification - tout ce que n'utilise pas les annotations fera exploser votre console et , vous pouvez voir le nom du friggin sans traquer les traces de pile mutilées.

Selon les documents:

l'application n'appellera pas les fonctions qui n'utilisent pas d'annotation de fonction explicite (et ne conviennent donc pas à la minification)

Une mise en garde , il détecte seulement qu'il y a des annotations , mais pas que les annotations sont complètes.

Sens:

['ThingOne', function(ThingA, ThingB) { … }]

Ne comprendra pas que ThingB ne fait pas partie de l'annotation.

Le mérite de cette astuce va aux gens ng-annotate , ce qui est recommandé par rapport au ngMin maintenant obsolète.

25
Mark Fox

Pour minify angular tout ce que vous avez à faire est de changer votre déclaration en déclaration "tableau" "mode" ", par exemple:

De:

var demoApp= angular.module('demoApp', []);
demoApp.controller(function demoCtrl($scope) {
} );

To

var demoApp= angular.module('demoApp', []);
demoApp.controller(["$scope",function demoCtrl($scope) {
}]);

Comment déclarer les services de l'usine?

demoApp.factory('demoFactory', ['$q', '$http', function ($q, $http) {
    return {
          //some object
    };
}]);
11
Dalorzo

J'ai juste eu le même problème et je l'ai résolu en remplaçant simplement ngmin (maintenant obsolète) par ng-annotate pour ma tâche de création de grunt.

Il semble que yeoman angular a également été mis à jour pour utiliser ng-annotate à partir de ce commit: https://github.com/yeoman/generator-angular/commit/3eea4cbeb010eeaaf797c17604b4a3ab5371ecc

Cependant, si vous utilisez une version plus ancienne de yeoman angular comme moi, remplacez simplement ng-min par ng-annotate dans votre package.json:

-    "grunt-ngmin": "^0.0.3",
+    "grunt-ng-annotate": "^0.3.0",

courir npm install (puis éventuellement npm Prune), et suivez les modifications dans commit pour éditer Gruntfile.js.

8
Xuwen

afin de savoir quel était le nom de la variable d'origine, vous pouvez changer la façon dont uglify modifie les variables:

../node_modules/grunt-contrib-uglify/node_modulesuglify-js/lib/scope.js

SymbolDef.prototype = {
  unmangleable: [...],
  mangle: function(options) {
    [...]
    this.mangled_name = s.next_mangled(options, this)+"_orig_"+this.orig[0].name;
    [...]
  }
};

et maintenant l'erreur est beaucoup plus évidente

Error: [$injector:unpr] Unknown provider: a_orig_$stateProvider
http://errors.angularjs.org/1.3.7/$injector/unpr?p0=a_orig_%24stateProvider
at eval (eval at <anonymous> (http://example.com/:64:17), <anonymous>:3155:20)

MODIFIER

Si évident maintenant ...

Gruntfile.js

uglify: {
  example: {
    options: {
      beautify: true,
      mangle: true
    },
    [...]
  },
  [...]
}

../node_modules/grunt-contrib-uglify/node_modulesuglify-js/lib/scope.js

var numberOfVariables = 1;
SymbolDef.prototype = {
  unmangleable: [...],
  mangle: function(options) {
    [...]
    this.mangled_name = s.next_mangled(options, this)+"_orig_"+this.orig[0].name+"_"+numberOfVariables++;
    [...]
  }
};

maintenant chaque variable est mutilée à une valeur unique qui contient également l'original ... ouvrez simplement le javascript minifié et recherchez "a_orig_ $ stateProvider_91212" ou peu importe ... vous le verrez dans son contexte d'origine ...

ça ne pourrait pas être plus facile ...

7
user3338098

N'oubliez pas non plus la propriété resolve de la route. Il doit également être défini comme le tableau:

$routeProvider.when('/foo', {
    resolve: {
        bar: ['myService1', function(myService1) {
            return myService1.getThis();
        }],
        baz: ['myService2', function(myService2) {
            return myService2.getThat();
        }]
    }
});
4
Petr Felzmann

Avec générateur-gulp-angular:

   /** @ngInject */
    function SomeController($scope, myCoolService) {

}

Ecrivez / ** @ngInject * / avant chaque contrôleur, service, directive.

3
Maxim Danilov

Si vous n'avez pas besoin d'Uglify pour modifier/raccourcir les noms de vos variables, une solution rapide et incorrecte consiste à définir mangle = false dans votre fichier Grunt.

    uglify: {
        compile: {
            options: {
                mangle   : false,
                ...
            },
        }
    }
2
Parris Varney