在angularjs中,如何从基模块中设置一个服务,以调用使用基模块的模块中服务上的函数

How to setup a service from a base module to call a function on a service in a module that uses the base module in angularjs

本文关键字:模块 服务 调用 函数 一个 angularjs 设置      更新时间:2023-09-26

我正在编写一个用于多个独立网站的服务。然而,在某些情况下,它需要触发不同的代码,这取决于它所使用的网站。我想保持每个网站的代码从基础服务分开。

下面是一些示例代码,展示了我想要的设计(尽管它不工作):

var baseModule = angular.module('baseModule', []);
baseModule.service('baseService', function() {
    this.func = function() {
        return ["first", 
                /* TODO somehow get from appropriate 
                service in website module */ 
                "FIXME", 
                "end"];
    };
});
var website1 = angular.module('website1', ['baseModule']);
website1.service('website1Service', function() {
    this.someCustomValue = function() { 
        // Note that while this is a constant value, in 
        // the real app it will be more complex,
        // so replacing this service with a constant provider won't work.
        return "someValue"; 
    }
});
// TODO : somehow link website1Service.someCustomValue to baseService
var website2 = angular.module('website2', ['baseModule']);
website2.service('website2Service', function() {
    this.anotherValue = function() { return "anotherValue"; }
});
// TODO : somehow link website2Service.anotherValue to baseService
// Testing code:
function makeTestController(expected) {
    return ['$scope', 'baseService', function($scope, baseService) {
      var result = baseService.func();
  
      if (angular.equals(result, expected)) {
          $scope.outcome = "Test Passed!";
      } else {
          $scope.outcome = 'Test failed...'n' + 
            "Expected: " + angular.toJson(expected) + ''n' +
            "But got : " + angular.toJson(result);
      }
    }];
  }
website1.controller('TestController1', 
                    makeTestController(['first', 'someValue', 'end']));
website2.controller('TestController2', 
                    makeTestController(['first', 'anotherValue', 'end']));
// since this test uses multiple angular apps, bootstrap them manually.
angular.bootstrap(document.getElementById('website1'), ['website1']);
angular.bootstrap(document.getElementById('website2'), ['website2']);
    
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<h3>Website 1</h3>
<div id='website1'>
  <div ng-controller='TestController1'>
    <pre>{{outcome}}</pre>
  </div>
</div>
<div id='website2'>
  <h3>Website 2</h3>
  <div ng-controller='TestController2'>
    <pre>{{outcome}}</pre>
  </div>
</div>

我已经想到了一些解决方案,但似乎没有一个是最佳的。

最明显的方法是用提供者替换baseService服务,并允许在每个模块中配置它。这似乎是在其他模块中配置服务的标准方式。但是无法访问provider函数中的website1Servicewebsite2Service,因为无法访问provider函数中的业务。这在文档中有说明:

在应用启动期间,在Angular开始创建所有服务之前,它会配置并实例化所有的提供商。我们将此称为应用程序生命周期的配置阶段。在此阶段,服务是不可访问的,因为它们还没有被创建。

解决这个问题的另一个解决方案是使用angular.injector来找到正确的服务。然而,angular.injector的文档暗示,您实际上只需要与第三方库进行交互。看来还有更好的办法。

最后,我可以在baseModule中为不存在的服务(例如"baseServiceActions")添加依赖项,并要求在website1website2中实现具有该名称的服务。当使用baseService时,依赖注入应该将它们绑定在一起。然而,这是一个非常奇怪的工作方式,如果baseServiceActions模块没有在使用baseModule模块的新网站中实现,将导致糟糕的错误消息。

有更好的方法吗?如果是这样,是否有可能改变我发布的示例代码,以获得所有的测试通过?理想情况下,不应该更改任何测试代码。

我最终想出了一个相当好的解决方案。我创建了一个名为"<serviceName>Settings"的服务,并向其添加了一个setup函数。然后,我在我想要使用它的模块中的模块运行块中调用该设置函数。最后,我有一个在服务中调用的validate方法,该方法使用设置来确保它已设置,如果没有设置,则抛出一个不错的错误消息。这解决了我使用其他解决方案时遇到的所有问题。

这是我的例子问题的解决方案:

var baseModule = angular.module('baseModule', []);
baseModule.service('baseService', ['baseServiceSettings', function(baseServiceSettings) {
    baseServiceSettings.validate();
    this.func = function() {
        return ["first", 
                baseServiceSettings.getValue(),
                "end"];
    };
}]);
baseModule.service('baseServiceSettings', function() {
   this.setup = function(getter) {
      this.getValue = getter;
   };
  this.validate = function() {
    if (!this.getValue) {
      throw "baseServiceSettings not setup! Run baseServiceSettings.setup in a module run block to fix";
    }
  };
});
var website1 = angular.module('website1', ['baseModule']);
website1.run(['baseServiceSettings', 'website1Service', function(baseServiceSettings, website1Service) {
  baseServiceSettings.setup(website1Service.someCustomValue);
}]);
  
  
website1.service('website1Service', function() {
    this.someCustomValue = function() { 
        // Note that while this is a constant value, in 
        // the real app it will be more complex,
        // so replacing this service with a constant provider won't work.
        return "someValue"; 
    }
});
var website2 = angular.module('website2', ['baseModule']);
website2.service('website2Service', function() {
    this.anotherValue = function() { return "anotherValue"; }
});
website2.run(['baseServiceSettings', 'website2Service', function(baseServiceSettings, website2Service) {
  baseServiceSettings.setup(website2Service.anotherValue);
}]);
// Testing code:
function makeTestController(expected) {
    return ['$scope', 'baseService', function($scope, baseService) {
      var result = baseService.func();
  
      if (angular.equals(result, expected)) {
          $scope.outcome = "Test Passed!";
      } else {
          $scope.outcome = 'Test failed...'n' + 
            "Expected: " + angular.toJson(expected) + ''n' +
            "But got : " + angular.toJson(result);
      }
    }];
  }
website1.controller('TestController1', 
                    makeTestController(['first', 'someValue', 'end']));
website2.controller('TestController2', 
                    makeTestController(['first', 'anotherValue', 'end']));
// since this test uses multiple angular apps, bootstrap them manually.
angular.bootstrap(document.getElementById('website1'), ['website1']);
angular.bootstrap(document.getElementById('website2'), ['website2']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<h3>Website 1</h3>
<div id='website1'>
  <div ng-controller='TestController1'>
    <pre>{{outcome}}</pre>
  </div>
</div>
<div id='website2'>
  <h3>Website 2</h3>
  <div ng-controller='TestController2'>
    <pre>{{outcome}}</pre>
  </div>
</div>