web-dev-qa-db-fra.com

Dépendances facultatives dans AngularJS

J'essaie d'implémenter un contrôleur dans AngularJS qui est utilisé sur plusieurs pages. Il utilise certains services. Certains d'entre eux sont chargés sur toutes les pages, d'autres non. Je veux dire qu'il est défini dans différents fichiers, et ces fichiers sont chargés indépendamment. Mais si je ne charge pas ces services sur toutes les pages, j'ai une erreur:

Error: Unknown provider: firstOtionalServiceProvider <- firstOtionalService

J'ai donc besoin de charger des scripts sur toutes les pages. Puis-je déclarer la dépendance comme facultative dans Angular? Par exemple:

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();
    }

}]);
50
molaccha

Non, Angular ne prend pas encore en charge les dépendances optionnelles. Vous feriez mieux de mettre toutes vos dépendances dans un module et de le charger comme un fichier Javascript. Si vous avez besoin d'un autre ensemble de dépendances - envisagez de créer un autre module dans un autre JS et de placer toutes les dépendances courantes dans un JS commun.

Cependant, le comportement que vous avez décrit peut être obtenu avec le service $injector . Vous injectez simplement $injector À la place de toutes vos dépendances dans un contrôleur et en extrayez manuellement les dépendances, en vérifiant si elles existent. C'est ça:

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');
    }
});

Voir en direct ce plunk ! Essayez de jouer avec les balises <script> Et surveillez la sortie de la console.

P.S. Et, comme l'a dit @Problematic, vous pouvez utiliser $injector.has(), à partir d'AngularJS 1.1.5.

53
madhead

Apparemment, n'utilise pas l'injection automatique. Cependant, vous pouvez injecter l'injecteur et vérifier le service:

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

J'irais probablement avec la suggestion de @ Proplematic d'utiliser $ injector. Cependant, il existe une autre solution à laquelle je peux penser: enregistrer tous les services avec leurs valeurs par défaut (null par exemple) dans votre fichier bootstrap. Lorsque des fichiers supplémentaires sont chargés, définitions ultérieures remplacera les définitions par défaut, créant quelque peu l'effet souhaité.

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
});

Lien de démonstration

13
Buu Nguyen

Voici comment je l'ai résolu:

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');
    }
});
9
Kenneth Lynne

Essayez de cette façon ..

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

'requires' est un tableau de modules de dépendance.

5
simbu