【问题标题】:Optional dependencies in AngularJSAngularJS 中的可选依赖项
【发布时间】:2013-08-30 21:56:39
【问题描述】:

我正在尝试在 AngularJS 中实现一个控制器,该控制器用于多个页面。它利用了一些服务。其中一些加载在所有页面上,一些 - 没有。我的意思是它在不同的文件中定义,并且这些文件是独立加载的。但是,如果我没有在所有页面上加载这些服务,我会收到错误:

Error: Unknown provider: firstOtionalServiceProvider <- firstOtionalService

所以,我需要在所有页面上加载脚本。我可以在 Angular 中将依赖项声明为可选吗?例如:

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

}]);

【问题讨论】:

    标签: javascript angularjs dependency-injection optional-parameters


    【解决方案1】:

    显然没有使用自动注入。但是,您可以注入注入器并检查服务:

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

    【讨论】:

    • 酷!我希望他们将 $injector.has 反向移植到 1.0.7!
    • 我比@madhead 更喜欢这个答案,因为它使用$injector.has 而不是try/catch
    • @Problematic 有没有办法使用它来将可选变量传递给控制器​​?我不想创建服务,而只是传递一个可选的布尔值或对象变量。这可能吗?
    • @JeremyMoritz 如果我理解您的要求,您不能将它与任意值一起使用,但您可以注册一个常量或值提供程序,并以与上述相同的方式使用它.
    【解决方案2】:

    不,Angular 还不支持开箱即用的可选依赖项。您最好将所有依赖项放入一个模块并将其作为一个 Javascript 文件加载。如果您需要另一组依赖项 - 考虑在另一个 JS 中创建另一个模块,并将所有常见的依赖项放在常见的 JS 中。

    但是,您描述的行为可以通过$injector service 实现。您只需将 $injector 注入控制器而不是所有依赖项,然后手动从中提取依赖项,检查它们是否存在。就是这样:

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

    this plunk 现场观看!尝试使用&lt;script&gt; 标签并注意控制台输出。

    附:而且,正如@Problematic 所说,您可以使用$injector.has(),从AngularJS 1.1.5 开始。

    【讨论】:

    • 谢谢!正是我需要的。我正在使用最新的 Angular,所以使用 'has'!
    【解决方案3】:

    我可能会接受@Proplematic 关于使用 $injector 的建议。但是,我可以想到另一种解决方案:在引导文件中使用其默认值(例如null)注册所有服务。加载其他文件时,以后的定义将覆盖默认定义,从而在一定程度上创建您想要的效果。

    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

    【讨论】:

    • 这很老套,但很有效。我在有多个路由共享一个控制器的情况下使用它,但只有少数需要解析:{} 属性注入。
    • 这是我的带有可选参数的项目的最佳答案。
    • 这正是我想要的!谢谢!我需要使用与模态控制器和 ng-controller 相同的控制器。对于模态控制器,我需要额外的解析器。
    【解决方案4】:

    我就是这样解决的:

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

    【讨论】:

      【解决方案5】:

      试试这个方法..

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

      'requires' 是一个依赖模块数组。

      【讨论】:

        猜你喜欢
        • 2015-04-20
        • 2015-11-15
        • 2021-09-17
        • 1970-01-01
        • 2011-09-08
        • 2016-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多