48

Sto tentando di implementare un controller in AngularJS che viene utilizzato su più pagine. Fa uso di alcuni servizi. Alcuni di questi sono caricati su tutte le pagine, altri no. Voglio dire che è definito in diversi file, e questi file vengono caricati in modo indipendente. Ma se io non caricare questi servizi su tutte le pagine che ho ottenuto l'errore:Dipendenze opzionali in AngularJS

Error: Unknown provider: firstOtionalServiceProvider <- firstOtionalService 

Così, ho bisogno di caricare gli script su tutte le pagine. Posso dichiarare la dipendenza come facoltativa in Angular? E.g:

myApp.controller('MyController', ['$scope', 'firstRequiredService', 'secondRequiredService', 'optional:firstOptionalService', 'optional:secondOptionalService', function($scope, firstRequiredService, secondRequiredService, firstOptionalService, secondOptionalSerivce){ 

    // No need to check, as firstRequiredService must not be null 
    firstRequiredService.alwaysDefined(); 

    // If the dependency is not resolved i want Angular to set null as argument and check 
    if (firstOptionalService) { 
     firstOptionalService.mayBeUndefinedSoCheckNull(); 
    } 

}]); 

risposta

50

No, Angular non supporta ancora dipendenze opzionali fuori dalla scatola. Faresti meglio a mettere tutte le tue dipendenze in un modulo e caricarlo come un unico file Javascript. Se hai bisogno di un altro set di dipendenze, considera la possibilità di creare un altro modulo in un altro JS e di mettere tutte le dipendenze comuni al JS comune.

Tuttavia, il comportamento che hai descritto può essere raggiunto con $injector service. È sufficiente iniettare $injector invece di tutte le dipendenze su un controller e prelevare manualmente le dipendenze, controllando se esistono. Questo è tutto:

index.html:

<!DOCTYPE html> 
<html data-ng-app="myApp"> 
    <head> 
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js"></script> 
    <script src="app.js"></script> 
    <script src="1.js"></script> 
    <script src="2.js"></script> 
    <title>1</title> 
    </head> 
    <body data-ng-controller="DemoController"> 
    </body> 
</html> 

app.js:

var myApp = angular.module('myApp', []); 

myApp.service('commonService', function(){ 
    this.action = function(){ 
     console.log('Common service is loaded'); 
    } 
}); 

myApp.controller('DemoController', ['$scope', '$injector', function($scope, $injector){ 
    var common; 
    var first; 
    var second; 

    try{ 
     common = $injector.get('commonService'); 
     console.log('Injector has common service!'); 
    }catch(e){ 
     console.log('Injector does not have common service!'); 
    } 
    try{ 
     first = $injector.get('firstService'); 
     console.log('Injector has first service!'); 
    }catch(e){ 
     console.log('Injector does not have first service!'); 
    } 
    try{ 
     second = $injector.get('secondService'); 
     console.log('Injector has second service!'); 
    }catch(e){ 
     console.log('Injector does not have second service!'); 
    } 

    if(common){ 
     common.action(); 
    } 
    if(first){ 
     first.action(); 
    } 
    if(second){ 
     second.action(); 
    } 
}]); 

1.js:

myApp.service('firstService', function(){ 
    this.action = function(){ 
     console.log('First service is loaded'); 
    } 
}); 

2.js:

myApp.service('secondService', function(){ 
    this.action = function(){ 
     console.log('Second service is loaded'); 
    } 
}); 

Vedere dal vivo in this plunk! Prova a giocare con i tag <script> e guarda l'uscita della console.

P.S. E, come detto @Problematic, è possibile utilizzare $injector.has(), a partire da AngularJS 1.1.5.

+0

Grazie! Esattamente quello di cui ho bisogno. Sto usando l'ultimo angolare, quindi usa 'ha'! – molaccha

56

Apparentemente non si sta utilizzando l'iniezione automatica. Tuttavia, è possibile iniettare l'iniettore e controllare il servizio:

myApp.controller('MyController', [ 
    '$scope', '$injector', 'firstRequiredService', 'secondRequiredService', 
    function ($scope, $injector, firstRequiredService, secondRequiredService) { 
     if ($injector.has('firstOptionalService')) { 
      var firstOptionalService = $injector.get('firstOptionalService'); 
     } 
    } 
]); 
+0

Fantastico! Li voglio backport '$ injector.has' a 1.0.7! – madhead

+3

Bello! Questa è la risposta giusta dal mio punto di vista – Gabriel

+2

Mi piace questa risposta su @ madhead's perché usa '$ injector.has' invece di' try'/'catch' –

13

probabilmente sarei andare con @ suggerimento di Proplematic di utilizzare $ iniettore. Tuttavia, c'è un'altra soluzione che posso pensare: registrare tutti i servizi con i loro valori predefiniti (null per esempio) nel file di bootstrap. Quando vengono caricati file aggiuntivi, le definizioni successive sostituiranno le definizioni predefinite, creando in qualche modo l'effetto desiderato.

var app = angular.module('plunker', []); 

app.value("service1", null) 
    .value("service2", null) 
    .factory("service1", function() { return "hello"; }); 

app.controller('MainCtrl', function($scope, service1, service2) { 
    console.log(service1); // hello 
    console.log(service2); // null 
}); 

Demo link

+0

Questo è hacky, ma funziona. Lo sto usando dove ho più percorsi che condividono un controller, ma solo alcuni hanno bisogno di una risoluzione: {} proprietà iniettata. – httpete

+0

Questa è la migliore risposta per il mio progetto con parametri opzionali. – sean

+0

Questo è esattamente quello che volevo! Grazie! Ho bisogno di usare lo stesso controller come controller modale e anche come controller ng. E per il controller modale ho bisogno di resolver aggiuntivi. –

5

Prova questo modo ..

try { 
    angular.module('YourModule').requires.push('Optional dependency module'); 
} catch(e) { 
    console.log(e) 
} 

'necessita' è un array di moduli di dipendenza.

9

è così che ho risolto:

var deps = []; 

try { 
    //Check if optionalModule is available 
    angular.module('app').requires.push('optionalModule'); 
    deps.push('optionalModule'); 
} catch(e){ 
    console.log("Warn: module optionalModule not found. Maybe it's normal"); 
} 

angular.module('app', deps).factory('stuff', function($injector) { 
    var optionalService; 

    if($injector.has('optionalService')) { 
     optionalService = $injector.get('optionalService'); 
    } else { 
     console.log('No waffles for you, dear sir'); 
    } 
}); 
Problemi correlati