Dependencias opcionales en AngularJS


Estoy tratando de implementar un controlador en AngularJS que se utiliza en varias páginas. Hace uso de algunos servicios. Algunos de ellos se cargan en todas las páginas, otros - no. Quiero decir que se define en diferentes archivos, y estos archivos se cargan de forma independiente. Pero si no cargo estos servicios en todas las páginas tengo error:

Error: Unknown provider: firstOtionalServiceProvider <- firstOtionalService

Por lo tanto, necesito cargar scripts en todas las páginas. Puedo declarar la dependencia como opcional en Angular? Por ejemplo:

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

}]);
Author: T J, 2013-08-31

5 answers

No, Angular todavía no soporta dependencias opcionales de forma inmediata. Será mejor que ponga todas sus dependencias en un módulo y lo cargue como un archivo Javascript. Si necesita otro conjunto de dependencias - considere crear otro módulo en otro JS y poner todas las dependencias comunes en JS común.

Sin embargo, el comportamiento que ha descrito se puede lograr con $injector servicio . Simplemente inyecta $injector en lugar de todas sus dependencias a un controlador y extrae dependencias a partir de ella manualmente, comprobando si existen. Eso es todo:

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

Verlo en vivo en este plunk ! Intente jugar con las etiquetas <script> y busque la salida de la consola.

P.d. Y, como @Problematic dijo, puede usar $injector.has(), a partir de AngularJS 1.1.5.

 50
Author: madhead,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-30 22:03:02

Aparentemente no usando inyección automática. Sin embargo, puede inyectar el inyector y verificar el servicio:

myApp.controller('MyController', [
    '$scope', '$injector', 'firstRequiredService', 'secondRequiredService', 
    function ($scope, $injector, firstRequiredService, secondRequiredService) {
        if ($injector.has('firstOptionalService')) {
            var firstOptionalService = $injector.get('firstOptionalService');
        }
    }
]);
 59
Author: Problematic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-30 20:28:10

Probablemente iría con la sugerencia de @Proplematic de usar inj injector. Sin embargo, hay otra solución que se me ocurre: registrar todos los servicios con sus valores predeterminados (null por ejemplo) en su archivo de bootstrap. Cuando se cargan archivos adicionales, las definiciones posteriores anularán las definiciones predeterminadas, creando de alguna manera el efecto que desea.

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

Enlace de demostración

 13
Author: Buu Nguyen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-30 22:12:20

Así es como lo resolví:

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
Author: Kenneth Lynne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-27 12:28:27

Intente de esta manera..

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

'requires' es una matriz de módulos de dependencias.

 5
Author: simbu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-27 12:41:33