"Unbekannter Anbieter: aProvider <- a" Wie finde ich den ursprünglichen Anbieter?

100

Wenn ich die minimierte (über UglifyJS) Version meiner AngularJS-Anwendung lade, wird in der Konsole der folgende Fehler angezeigt:

Unknown provider: aProvider <- a

Jetzt ist mir klar, dass dies auf das Mangeln von Variablennamen zurückzuführen ist. Die entwirrte Version funktioniert einwandfrei. Aber ich tun möchte Verwendung von Variablennamen Mangeln machen, wie es drastisch die Größe unserer JS Ausgabedatei reduziert.

Aus diesem Grund verwenden wir ngmin in unserem Erstellungsprozess, aber es scheint dieses Problem nicht zu lösen, obwohl es uns in der Vergangenheit gute Dienste geleistet hat.

Um dieses Problem zu beheben, habe ich Quellkarten in unserer uglify-Grunzaufgabe aktiviert. Sie entstehen nur gut und Chrome tut laden die Karten vom Server. Trotzdem erhalte ich immer noch die gleiche nicht hilfreiche Fehlermeldung, obwohl ich den Eindruck hatte, dass ich jetzt den ursprünglichen Namen des Anbieters sehen sollte.

Wie kann ich Chrome dazu bringen, anhand der Quellkarten zu ermitteln, welcher Anbieter hier das Problem ist, oder wie kann ich den Anbieter auf andere Weise ermitteln?

Der Hochstapler
quelle
Sie können versuchen, jeder JS-Quelldatei eindeutige Kommentare hinzuzufügen (falls dies nicht bereits der Fall ist), und die OptionerveComments von UglifyJS verwenden, um eine Vorstellung davon zu erhalten, welche Datei den falschen Code enthält.
JB Nizet
Verwenden Sie zufällig Dekorateure? Ich habe festgestellt, dass ngmin Dekorateure nicht richtig neu zu schreiben scheint, wenn ich es in der Vergangenheit verwendet habe, was zu Fehlern wie Ihrem führt.
Dherman
@JBNizet: Ich mag die Idee, aber das Hinzufügen dieser Direktive zu den Optionen scheint keine Wirkung zu haben.
Der Hochstapler
@ Dherman: Könnten Sie mir ein Beispiel für Dekorateure geben? Ich bin mir nicht sicher, was sie in diesem Zusammenhang sein würden.
Der Hochstapler
Siehe github.com/gruntjs/grunt-contrib-uglify (wenn Sie grunt verwenden). Der Wert der Option sollte "all" sein.
JB Nizet

Antworten:

193

Ich würde immer noch gerne wissen, wie ich den Ort in unserem Quellcode hätte finden können, der dieses Problem verursacht hat, aber seitdem konnte ich das Problem manuell finden.

Es wurde eine Controller-Funktion für den globalen Bereich deklariert, anstatt einen .controller()Aufruf für das Anwendungsmodul zu verwenden.

Es gab also so etwas:

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

Dies funktioniert gut für AngularJS, aber damit es beim Mangeln richtig funktioniert, musste ich es ändern in:

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

Nach weiteren Tests fand ich tatsächlich Instanzen von mehr Controllern, die ebenfalls Probleme verursachten. So habe ich die Quelle aller manuell gefunden :

Zunächst halte ich es für ziemlich wichtig, die Verschönerung der Ausgabe in den uglify-Optionen zu aktivieren. Für unsere Grunzaufgabe bedeutete das:

options : {
    beautify : true,
    mangle   : true
}

Ich habe dann die Projektwebsite in Chrome mit geöffneten DevTools geöffnet. Was dazu führt, dass ein Fehler wie der folgende protokolliert wird:

Geben Sie hier die Bildbeschreibung ein

Die Methode in der Anrufverfolgung, an der wir interessiert sind, ist die, die ich mit einem Pfeil markiert habe. Das ist providerInjectorininjector.js . Sie möchten einen Haltepunkt platzieren, an dem eine Ausnahme ausgelöst wird:

Geben Sie hier die Bildbeschreibung ein

Wenn Sie die Anwendung jetzt erneut ausführen, wird der Haltepunkt erreicht und Sie können den Aufrufstapel hochspringen. Es erfolgt ein Anruf von invokeininjector.js , erkennbar an der Zeichenfolge "Falsches Injektionstoken":

Geben Sie hier die Bildbeschreibung ein

Der localsParameter ( din meinem Code entstellt ) gibt eine ziemlich gute Vorstellung davon, welches Objekt in Ihrer Quelle das Problem ist:

Geben Sie hier die Bildbeschreibung ein

Ein kurzer Blick grepauf unsere Quelle findet viele Beispiele von modalInstance, aber von dort aus war es einfach, diesen Punkt in der Quelle zu finden:

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

Welches muss geändert werden zu:

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

Falls die Variable keine nützlichen Informationen enthält, können Sie auch weiter nach oben springen und einen Aufruf ausführen, invokeder zusätzliche Hinweise enthalten sollte:

Geben Sie hier die Bildbeschreibung ein

Verhindern Sie, dass dies erneut geschieht

Nachdem Sie das Problem hoffentlich gefunden haben, sollte ich erwähnen, wie Sie am besten verhindern können, dass dies in Zukunft erneut auftritt.

Natürlich können Sie einfach die Inline-Array-Annotation überall oder die (je nach Ihren Präferenzen) $injectEigenschafts-Annotation verwenden und einfach versuchen, sie in Zukunft nicht zu vergessen. Wenn Sie dies tun, stellen Sie sicher, dass Sie den strengen Abhängigkeitsinjektionsmodus aktivieren, um solche Fehler frühzeitig zu erkennen.

Achtung! Wenn Sie Angular Batarang verwenden, funktioniert StrictDI möglicherweise nicht für Sie, da Angular Batarang nicht kommentierten Code in Ihren Code einfügt (schlechter Batarang!).

Oder Sie können ng-annotate sich darum kümmern lassen. Ich kann dies nur empfehlen, da dadurch viel Fehlerpotential in diesem Bereich beseitigt wird, wie z.

  • DI-Anmerkung fehlt
  • DI-Anmerkung unvollständig
  • DI-Anmerkung in falscher Reihenfolge

Die Anmerkungen auf dem neuesten Stand zu halten, ist einfach eine Qual und Sie sollten es nicht tun müssen, wenn es automatisch möglich ist. ng-annotate macht genau das.

Es sollte sich mit grunt-ng-annotate und gulp-ng-annotate gut in Ihren Erstellungsprozess integrieren lassen .

Der Hochstapler
quelle
12
Dies ist eine fantastische Zusammenfassung, die mit Sorgfalt geschrieben wurde. Ich bin gerade auf dieses Problem gestoßen, scheint irgendwo ein Problem tief in ngmin zu sein. Ihre Tipps haben mir geholfen zu wissen, wo ich suchen muss. Am Ende habe ich nur alle meine Winkelparameter "Array-ified", und das Problem ging weg. Alle früheren Builds wurden problemlos minimiert, und es hat sich nichts Wesentliches geändert. Ich habe keine globalen Funktionen hinzugefügt - es hat einfach auf mysteriöse Weise aufgehört zu funktionieren, indem ein Controller / eine Direktive / ein Dienst / ein Filter beschädigt wurde?
Zenocon
Dies war eine großartige Hilfe. Ich wusste nicht, dass Sie Array (Inline) -Syntax auch für andere Funktionen verwenden müssen, wie Router-Auflösung, .run, .config usw.
VDest
4
In meinem Fall war es Controller in Direktive. Wenn in der Variablen 'd' $ attr angezeigt wird, ist dies wahrscheinlich das gleiche Problem. Sie sollten Parameter in Array-Klammern für den Controller der inneren Direktive einschließen. controller: ["$ scope", function ($ scope) {...}] anstelle von controller: function ($ scope) {...}
alex naumov
Vielen Dank für Ihre Beschreibung und Lösung mit der sicheren Abhängigkeitsinjektion / Array-Notation für die var-Funktionsreferenz. Auch ich hatte diesen Fehler und aufgrund Ihrer Lösung konnte ich mich weiterentwickeln. du rockst!
Frankie Loscavio
1
Jedes Mal, wenn ich dieses Problem habe, lese ich es erneut und möchte erneut dafür stimmen. Übrigens, hier ist, wie man die uglify({ output : { beautify : true }})
Schluckversion einrichtet
30

Oliver Salzburgs Artikel war fantastisch. Upvoted.

Tipp für alle, die diesen Fehler haben könnten. Meins wurde einfach dadurch verursacht, dass vergessen wurde, ein Array für einen Direktiven-Controller zu übergeben:

SCHLECHT

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

GUT

return {
    restrict: "E",
    scope: {                
    },
    controller: ["$scope", ExampleDirectiveController],
    templateUrl: "template/url/here.html"
};
Ash Clarke
quelle
2
Das war so frech ... Uglify hat mir das erst nach einem kürzlich durchgeführten Update verursacht!
SamMorrowDrums
Mein Problem war das gleiche, aber es stellte sich heraus, dass ich /* @ngInject */vor der Funktion hinzufügen musste . Es scheint den komplizierten Injektionsteil zu machen, ohne jedes enthaltene Modul
eingeben
25

benutze ng-strict-di mit ng-app

Wenn Sie Angular 1.3 verwenden, können Sie sich durch die Verwendung der ngStrictDi- Direktive mit ngApp eine Welt voller Verletzungen ersparen:

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

Jetzt - vor der Minimierung - wird alles, was keine Anmerkungen verwendet, Ihre Konsole in die Luft jagen und Sie können den verdammten Namen sehen, ohne durch verstümmelte Stapelspuren zu suchen.

Gemäß den Dokumenten:

Die Anwendung kann keine Funktionen aufrufen, die keine explizite Funktionsanmerkung verwenden (und daher für die Minimierung ungeeignet sind).

Eine Einschränkung : Es wird nur festgestellt , dass Anmerkungen vorhanden sind , nicht, dass die Anmerkungen vollständig sind.

Bedeutung:

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

Wird nicht verstehen, dass ThingB nicht Teil der Annotation ist.

Der Tipp für diesen Tipp geht an die ng-annotate- Leute, was gegenüber dem jetzt veralteten ngMin empfohlen wird.

Mark Fox
quelle
Dies erfordert mehr Upvotes. Dies ist ideal zum Debuggen einer App, die niemals ngInject oder die String-Array-Syntax verwendet hat.
Michael Pearson
11

Um den Winkel zu minimieren, müssen Sie lediglich Ihre Deklaration in den Deklarationsmodus "Array" ändern, zum Beispiel:

Von:

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

Zu

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

Wie deklariere ich Fabrikdienstleistungen?

demoApp.factory('demoFactory', ['$q', '$http', function ($q, $http) {
    return {
          //some object
    };
}]);
Dalorzo
quelle
Ich weiß. Deshalb verwenden wir ngmin. Ich vermute, dass es ein Problem mit einem Teil unserer Quelle oder ihren Abhängigkeiten gibt. Deshalb versuche ich, diesem Problem auf den Grund zu gehen.
Der Hochstapler
1
Meine Empfehlung ist, dass Sie Ihren Code auf diese Weise erstellen. Sie können also jeden Minifier verwenden
Dalorzo
3
Ich bin zu schaffen , unseren Code auf diese Weise. Aber wir haben externe Abhängigkeiten, die dies nicht tun. ngmin hat dieses Problem in der Vergangenheit für uns gut gelöst. Ich gehe davon aus, dass eine kürzlich vorgenommene Änderung dieses Problem verursacht hat. Ich möchte jetzt die Quelle dieses Problems finden, damit ich es in unserem Code, unserer Abhängigkeit oder möglicherweise in ngmin selbst richtig beheben kann.
Der Hochstapler
Da das Problem wie eine sehr spezifische Komponente oder ein bestimmter Code klingt, ist es schwierig, eine Anleitung zu geben, zumindest von meinem Ende
Dalorzo
ngmin erfordert nicht, dass Sie den Array-Deklarationsmodus verwenden, sondern fügt viele nutzlose Deklarationen hinzu.
Nanocom
8

Ich hatte gerade das gleiche Problem und löste es, indem ich einfach ngmin (jetzt veraltet) durch ng-annotate für meine Grunz-Build-Aufgabe ersetzte.

Es scheint, dass yeoman angle ebenfalls aktualisiert wurde, um ng-annotate ab diesem Commit zu verwenden: https://github.com/yeoman/generator-angular/commit/3eea4cbeb010eeaaf797c17604b4a3ab5371eccb

Wenn Sie jedoch eine ältere Version von yeoman eckig wie ich verwenden, ersetzen Sie ng-min einfach durch ng-annotate in Ihrem package.json:

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

Führen Sie npm install(dann optional npm prune) aus und folgen Sie den Änderungen im zu bearbeitenden CommitGruntfile.js .

Xuwen
quelle
7

Um zu wissen, wie der ursprüngliche Variablenname lautete, können Sie ändern, wie uglify die Variablen entstellt:

../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;
    [...]
  }
};

und jetzt ist der Fehler viel offensichtlicher

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)

BEARBEITEN

So offensichtlich jetzt ...

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++;
    [...]
  }
};

Jetzt wird jede Variable auf einen eindeutigen Wert entstellt, der auch das Original enthält. Öffnen Sie einfach das minimierte Javascript und suchen Sie nach "a_orig_ $ stateProvider_91212" oder was auch immer ... Sie werden es in seinem ursprünglichen Kontext sehen ...

könnte nicht einfacher sein ...

user3338098
quelle
4

Vergessen Sie auch nicht die resolveEigenschaft der Route. Es muss auch als Array definiert werden:

$routeProvider.when('/foo', {
    resolve: {
        bar: ['myService1', function(myService1) {
            return myService1.getThis();
        }],
        baz: ['myService2', function(myService2) {
            return myService2.getThat();
        }]
    }
});
Petr Felzmann
quelle
Dies passierte mir, als ich meinen Routen eine Reihe von Auflösungen hinzufügte. Sie haben mir möglicherweise Stunden schmerzhaften Debuggens erspart, danke.
Paul McClean
3

Mit Generator-Schluck-Winkel:

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

}

Schreiben Sie / ** @ngInject * / vor jeden Controller, Dienst, jede Anweisung.

Maxim Danilov
quelle
2

Eine schnelle und schmutzige Lösung für dieses Problem, wenn Sie nicht benötigen, dass Uglify Ihre Variablennamen entstellt / verkürzt, besteht darin, mangle = false in Ihrer Grunt-Datei festzulegen

    uglify: {
        compile: {
            options: {
                mangle   : false,
                ...
            },
        }
    }
Parris Varney
quelle
Dies kann das Problem beheben, aber die resultierende Build-Größe ist größer, da Mangle deaktiviert ist.
NotABot
immer noch kleiner als gar nicht hässlich
mjwrazor