"Provider sconosciuto: aProvider <- a" Come trovo il provider originale?


100

Quando sto caricando la versione minimizzata (tramite UglifyJS) della mia applicazione AngularJS, ottengo il seguente errore nella console:

Unknown provider: aProvider <- a

Ora, mi rendo conto che ciò è dovuto alla modifica del nome della variabile. La versione sbrogliata funziona bene. Comunque, io non voglio fare uso del nome storpiatura variabili, in quanto riduce drasticamente le dimensioni del nostro file di output JS.

Per questo motivo, stiamo usando ngmin nel nostro processo di compilazione, ma non sembra risolvere questo problema, anche se ci è servito bene in passato.

Quindi, per eseguire il debug di questo problema, ho abilitato le mappe sorgente nella nostra brutta attività grugnita. Essi sono generati bene e Chrome fa caricare le mappe dal server. Tuttavia, ricevo ancora lo stesso messaggio di errore inutile, anche se avevo l'impressione che ora dovrei vedere il nome originale del provider.

Come faccio a far sì che Chrome utilizzi le mappe di origine per dirmi quale provider è il problema qui o, in alternativa, come posso trovare il provider in un altro modo?


Potresti provare ad aggiungere commenti distinti a ogni file sorgente JS (se non è già il caso) e utilizzare l'opzione preservComments di UglifyJS: questo ti darebbe un'idea di quale file contiene il codice errato.
JB Nizet

Ti capita di utilizzare decoratori? Ho scoperto che ngmin non sembra riscrivere correttamente i decoratori quando l'ho usato in passato, il che si traduce in errori come il tuo.
dherman

@ JBNizet: mi piace l'idea, ma l'aggiunta di quella direttiva alle opzioni non sembra avere alcun effetto.
Der Hochstapler

@dherman: potresti farmi un esempio di decoratori? Non sono sicuro di cosa sarebbero in questo contesto.
Der Hochstapler

Vedi github.com/gruntjs/grunt-contrib-uglify (se usi grunt). Il valore dell'opzione dovrebbe essere "all".
JB Nizet

Risposte:


193

Mi piacerebbe ancora sapere come avrei potuto trovare il posto nel nostro codice sorgente che ha causato questo problema, ma da allora sono stato in grado di trovare il problema manualmente.

C'era una funzione del controller dichiarata nell'ambito globale, invece di utilizzare una .controller()chiamata sul modulo dell'applicazione.

Quindi c'era qualcosa del genere:

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

Funziona benissimo per AngularJS, ma per farlo funzionare correttamente con il mangling, ho dovuto cambiarlo in:

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

Dopo ulteriori test, ho effettivamente trovato istanze di più controller che hanno anche causato problemi. Ecco come ho trovato manualmente l'origine di tutti loro :

Prima di tutto, considero piuttosto importante abilitare l'abbellimento dell'output nelle opzioni uglify. Per il nostro compito grugnito ciò significava:

options : {
    beautify : true,
    mangle   : true
}

Ho quindi aperto il sito Web del progetto in Chrome, con i DevTools aperti. Il che si traduce in un errore come quello seguente che viene registrato:

inserisci qui la descrizione dell'immagine

Il metodo nel tracciamento delle chiamate a cui siamo interessati è quello che ho contrassegnato con una freccia. Questo è providerInjectordentroinjector.js . Dovrai posizionare un punto di interruzione in cui genera un'eccezione:

inserisci qui la descrizione dell'immagine

Quando ora riesegui l'applicazione, il punto di interruzione verrà raggiunto e potrai saltare lo stack di chiamate. Ci sarà una chiamata da invokeininjector.js , riconoscibile dalla stringa "token di iniezione errato":

inserisci qui la descrizione dell'immagine

Il localsparametro (modificato dnel mio codice) dà un'idea abbastanza buona su quale oggetto nella tua sorgente è il problema:

inserisci qui la descrizione dell'immagine

Un rapido grepsulla nostra fonte trova molti casi di modalInstance, ma da lì, è stato facile trovare questo punto nella fonte:

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

Che deve essere modificato in:

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

Nel caso in cui la variabile non contenga informazioni utili, puoi anche saltare più in alto nello stack e dovresti premere una chiamata a invokecui dovresti avere suggerimenti aggiuntivi:

inserisci qui la descrizione dell'immagine

Evita che ciò accada di nuovo

Ora che si spera che abbia trovato il problema, sento di dover menzionare come evitare al meglio che ciò accada di nuovo in futuro.

Ovviamente, potresti semplicemente usare l' annotazione dell'array inline ovunque, o l' $injectannotazione della proprietà (a seconda delle tue preferenze) e cercare semplicemente di non dimenticartene in futuro. Se lo fai, assicurati di abilitare la modalità di inserimento delle dipendenze rigorose , per rilevare errori come questo in anticipo.

Attento! Nel caso tu stia usando Angular Batarang, StrictDI potrebbe non funzionare per te, poiché Angular Batarang inietta codice non annotato nel tuo (cattivo Batarang!).

Oppure puoi lasciare che ng-annotate se ne occupi. Consiglio vivamente di farlo, poiché rimuove molti potenziali errori in quest'area, come:

  • Annotazione DI mancante
  • Annotazione DI incompleta
  • Annotazione DI nell'ordine sbagliato

Mantenere aggiornate le annotazioni è semplicemente un rompicoglioni e non dovresti farlo se può essere fatto automaticamente. ng-annotate fa esattamente questo.

Dovrebbe integrarsi perfettamente nel tuo processo di compilazione con grunt-ng-annotate e gulp-ng-annotate .


12
Questo è un fantastico articolo, scritto con cura. Mi sono appena imbattuto in questo problema, sembra essere un problema profondo in ngmin da qualche parte. I tuoi suggerimenti mi hanno aiutato a sapere dove cercare. Alla fine ho semplicemente "schierato" tutti i miei parametri angolari e il problema è andato via. Tutte le build precedenti sono state minimizzate correttamente e non è cambiato nulla di considerevole. Non ho aggiunto alcuna funzione globale - ha smesso di funzionare, misteriosamente, alterando qualche controller / direttiva / servizio / filtro?
zenocon

Questa è stata una grande fonte di aiuto. Non sapevo che dovevi usare la sintassi array (inline) anche per altre funzioni, come la risoluzione del router, .run, .config, ecc.
VDest

4
Nel mio caso era controllore in direttiva. Se nella variabile "d" vedrai $ attr, probabilmente è lo stesso problema. Dovresti racchiudere i parametri tra parentesi di matrice per il controller di direttiva interno. controller: ["$ scope", function ($ scope) {...}] invece di controller: function ($ scope) {...}
alex naumov

Grazie mille per il tuo articolo e per la soluzione che utilizza l'inserimento sicuro delle dipendenze / la notazione array per il riferimento alla funzione var. Anch'io ho avuto questo errore e grazie alla tua soluzione sono riuscito ad andare avanti. Sei forte!
Frankie Loscavio

1
Ogni volta che ho questo numero lo leggo di nuovo e desidero votare di nuovo a favore. A proposito, ecco come impostare la versione gulpuglify({ output : { beautify : true }})
Eugene Gluhotorenko

30

Il resoconto di Oliver Salzburg è stato fantastico. Votato.

Suggerimento per chiunque possa avere questo errore. Il mio è stato semplicemente causato dal dimenticare di passare un array per un controller direttiva:

MALE

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

BENE

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

2
Questo è stato così sfacciato ... Uglify non mi ha causato questo fino a un recente aggiornamento!
SamMorrowDrums

Il mio problema era lo stesso, ma risulta che quello che dovevo aggiungere era /* @ngInject */prima della funzione. Sembra fare la parte di iniezione complicata senza bisogno di digitare ogni modulo incluso (sto usando Yeoman)
Nicholas Blasgen

25

usa ng-strict-di con ng-app

Se stai usando angolare 1.3 si può risparmiare un mondo di dolore utilizzando ngStrictDi direttiva con ngApp:

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

Ora - pre-minificazione - tutto ciò che non usa annotazioni farà saltare in aria la tua console e puoi vedere il fottuto nome senza cercare tra le tracce di stack alterate.

Secondo i documenti:

l'applicazione non riuscirà a richiamare le funzioni che non utilizzano l'annotazione di funzione esplicita (e quindi non sono adatte per la minificazione)

Un avvertimento , rileva solo che ci sono annotazioni, non che le annotazioni siano complete.

Senso:

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

Non rileverà che ThingB non fa parte dell'annotazione.

Il merito di questo suggerimento va alle persone ng-annotate , che è consigliato rispetto all'ormai deprecato ngMin.


Questo richiede più voti positivi. Questo è ottimo per il debug di un'app che non ha mai utilizzato ngInject o la sintassi dell'array di stringhe.
Michael Pearson

11

Per minimizzare angolare, tutto ciò che devi fare è cambiare la tua dichiarazione nella modalità "dichiarazione" array "", ad esempio:

A partire dal:

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

Per

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

Come dichiarare i servizi di fabbrica?

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

Lo so. Ecco perché usiamo ngmin. Sospetto che abbia un problema con una parte della nostra fonte o delle sue dipendenze. Ecco perché sto cercando di arrivare alla radice di questo problema.
Der Hochstapler

1
Il mio consiglio è di creare il codice in questo modo. Quindi puoi usare qualsiasi minificatore
Dalorzo

3
Io sto creando il nostro codice in questo modo. Ma abbiamo dipendenze esterne che non lo fanno. ngmin ha risolto bene questo problema in passato. Presumo che una modifica recente abbia creato questo problema. Ora vorrei trovare l'origine di questo problema in modo da poterlo risolvere correttamente nel nostro codice, nella nostra dipendenza o possibilmente in ngmin stesso.
Der Hochstapler

Poiché il problema sembra molto specifico per un particolare componente o codice è difficile fornire una guida, almeno dalla mia parte
Dalorzo

ngmin non richiede l'utilizzo della modalità di dichiarazione dell'array, aggiunge molte dichiarazioni inutili.
Nanocom

8

Ho appena avuto lo stesso problema e l'ho risolto semplicemente sostituendo ngmin (ora deprecato) con ng-annotate per la mia attività di compilazione grugnita.

Sembra che anche yeoman angular sia stato aggiornato per utilizzare ng-annotate a partire da questo commit: https://github.com/yeoman/generator-angular/commit/3eea4cbeb010eeaaf797c17604b4a3ab5371eccb

Tuttavia, se stai usando una versione precedente di yeoman angular come me, sostituisci semplicemente ng-min con ng-annotate nel tuo package.json:

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

eseguire npm install(quindi facoltativamente npm prune) e seguire le modifiche nel commit da modificare Gruntfile.js.


7

per sapere qual era il nome originale della variabile puoi cambiare il modo in cui uglify manipola le variabili:

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

e ora l'errore è molto più evidente

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)

MODIFICARE

Così ovvio ora ...

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

ora ogni variabile è alterata in un valore univoco che contiene anche l'originale ... basta aprire il javascript minimizzato e cercare "a_orig_ $ stateProvider_91212" o qualsiasi altra cosa ... lo vedrai nel suo contesto originale ...

non potrebbe essere più facile ...


4

Inoltre, non dimenticare la resolveproprietà del percorso. Deve anche essere definito come array:

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

Questo è successo a me quando ho aggiunto una serie di risoluzioni alle mie rotte. Mi hai potenzialmente risparmiato ore di doloroso debug, grazie.
Paul McClean

3

Con generatore-gulp-angolare:

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

}

Scrivi / ** @ngInject * / prima di ogni controller, servizio, direttiva.


2

Una soluzione rapida e sporca per questo se non si richiede a Uglify di manipolare / accorciare i nomi delle variabili è impostare mangle = false nel file Grunt

    uglify: {
        compile: {
            options: {
                mangle   : false,
                ...
            },
        }
    }

Questo potrebbe risolvere il problema, ma la dimensione della build risultante sarà maggiore poiché mangle è disabilitato.
NotABot

ancora più piccolo di non brutto affatto
mjwrazor
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.