手动引导和覆盖配置中的angular服务的问题

Issue with manual bootstrapping and overriding angular services in config

本文关键字:angular 服务 问题 配置 覆盖      更新时间:2023-09-26

我正试图通过覆盖服务使我的角度应用程序在实时模式和原型模式下工作。作为其中的一部分,当配置中打开原型模式时,我会停止引导进程,加载mock服务(js)文件并恢复引导。

下面是一个简化的源代码列表,用于准备演示:-

App.js

只有我的应用程序,用于模拟调用服务并显示结果。它还需要StubApp,其提供将用于覆盖服务

var app = angular.module('app', ['StubsApp'])
.run([ '$rootScope', 'DataService', function($scope, DataService){
   DataService.getData().then(function(data){
    $scope.name = data;
  });
}]);

DataService.js

只是在应用程序中注册的一项简单服务

function DataService($q){
  this.getData = function(){
       return $q.when('I am Real!!');
  }
}
DataService.$inject = ['$q'];
angular.module('app').service('DataService',DataService);

Driver.js

只是将设置mocking的配置注册。

angular.module('app').config(['$provide', 'stubServiceProvider', 'AppConfig', function($provide, stubProvider, AppConfig){
    if(AppConfig.StubEnabled){
       stubProvider.loadStubsInModule('plunker');
    }
}]);

StubProvider.js

这公开了一个类似于angular.module的接口来注册存根服务。它还查找具有通过停止引导加载的模拟服务列表的CCD_ 3。它还公开了一个提供,应用程序可以使用该提供来设置现有服务与stubs.json中的服务的过载

var Stubs = {},
    modules = [];
function module(moduleName) {
    return {
        mock: function (func) {
            modules.push(func);
        }, get: function () {
            return modules;
        }
    };
}
Stubs.module = module;
loadStubs();
function loadStubs() {
    window.name = "NG_DEFER_BOOTSTRAP!";
    var injector = angular.injector(['ng']);
    var $q = injector.get('$q');
    var $http = injector.get('$http');
    var scripts = [];
    $http.get('stubs.json').then(function (result) {
        scripts = result.data.map(function (src) {
            var script = document.createElement('script');
            script.src = src;
            script.async = true;
            document.head.appendChild(script);
            var defered = $q.defer();
            script.onload = function () {
                defered.resolve();
            };
            return defered.promise;
        });
        $q.all(scripts).finally(function () {
            angular.element().ready(function () {
                angular.resumeBootstrap();
            });
        });
    });
}
//This is the provider which actually will do the overriding
angular.module('StubsApp', []).provider('stubService', function ($provide) {
    ...... //Code in plunker
});

DataService Mock.js

这只是一个实际使用Stubs接口注册mock Stubs.module('app').mock(MockService)的mock,ctor有一个属性stubFor="serviceName",它告诉它实际模拟的是哪个服务

function MockService($q, $log){
this.getData = function(){
       return $q.when('I am Mock!!');
  }
}
MockService.$inject = ['$q', '$log'];
MockService.stubFor="DataService";
Stubs.module('app').mock(MockService);

stubs.json

只是一个简单的json文件,用于指定mock

["DataServiceMock.js"]

index.html

<script src="app.js"></script>
<script src="DataService.js"></script>
<script src="Driver.js"></script>
<script src="stubprovider.js"></script>

这很好用。现在的问题是,当我在服务注册之前移动Driver.js文件时,即DataService.js,它将不再模拟。在"StubProvider.js"中执行重写的代码的特定部分是

   Stubs.module(moduleName).get().forEach(function (mod) {
        var serviceName = mod.stubFor;
        var ctor = mod;
        if (serviceName) {
            $provide.service(serviceName, ctor);
        }
    });

这是一个Demo Plnkr如果您注释掉Driver.js中的行,您可以看到输出将来自真实服务,而不是来自模拟服务。为了在DataService.js之前以index.html模式Driver.js复制该问题,它不会用MockDataservice覆盖DataService。

  • 为什么配置注册的顺序很重要,配置阶段应该在服务实例化之前运行

  • 是否有更好的模式来确保在恢复引导进程之前加载所有脚本,而不是使用延迟模式。

使用AngularJS:的createElementappendChild DOM方法、script元素的srconload属性以及bootstrapelementinjector方法

    /* Create script element */
    var script = document.createElement('script');
    /* Set src */
    script.src = "https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular.min.js";
    /* Append to head */
    document.getElementsByTagName("head")[0].appendChild(script);
    
    function dothis()
      {
      //local datastore
      this.mvvm = {};
    
      //template string
      var html = "<div>ID: {{$id}}</div>".replace("|",'"',"g");
      //template object
      var template = angular.element(html);
      //template transformer
      var compiler = angular.injector(["ng"]).get("$compile");
      //template result
      var linker = compiler(template);
      //scope object
      var scope = angular.injector(["ng"]).get("$rootScope");
      //scope binding
      var result = linker(scope)[0];
    
      /* Append result to body */
      document.body.appendChild(result);
    
      /* Render */
      angular.bootstrap(document, ['ng']);
      }
    
    script.onload = dothis;