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

时间:2015-07-09 04:43:25

标签: javascript angularjs

我正在编写一个将在多个独立网站中使用的服务。但是,在某些时候,它需要触发不同的代码,具体取决于它所使用的网站。我希望将每个网站代码与基本服务分开。

以下是一些示例代码,演示了我想要的设计(尽管它不起作用):

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服务,并允许在每个模块中配置它。这似乎是在其他模块中配置服务的标准方法。但是,我无法访问提供程序函数中的website1Servicewebsite2Service,因为无法在提供程序函数中访问服务。这在文档中注明:

  

在应用程序引导期间,在Angular创建所有服务之前,它会配置并实例化所有提供程序。我们称之为应用程序生命周期的配置阶段。在此阶段,服务无法访问,因为尚未创建服务。

解决此问题的另一个解决方案是使用angular.injector来查找正确的服务。但是,docs for angular.injector暗示您实际上只需要与第三方库进行交互。所以似乎有更好的方法。

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

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

1 个答案:

答案 0 :(得分:0)

我最终找到了一个相当不错的解决方案。我创建了一个名为"<serviceName>Settings"的服务,并为其添加了setup函数。然后我在我想要使用它的模块中的模块运行块中调用该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>