配置服务(通过其提供商)以使用其他服务

时间:2014-03-31 22:01:28

标签: javascript angularjs

我有一项服务,我想通过传递一个函数来配置它来使用它。只要此函数返回我的服务可以使用的内容,它就不关心它如何获取数据,也不关心同步/异步。以下是我希望能够做的事情的样本,但由于显而易见的原因无效:

.config(function(MyServiceProvider, OtherService) {

  MyServiceProvider.setSomeMethod( OtherService.someMethod );

})

那会很棒,但似乎没有一种方法可以引用" OtherService"来自配置功能。我知道这是因为OtherService可能尚未配置,因此它的实例还不存在,但那么在这种情况下该做什么呢?

(有焦虑)是否适合在运行区内进行此关联?

.run(function(MyService, OtherService) {

  MyService.setSomeMethod( OtherService.someMethod );

})

2 个答案:

答案 0 :(得分:1)

使用$injector服务稍后解决依赖关系,当引导程序已经解决时。

例如:

angular.module('myApp').config(function(myProvider) {
  myProvider.setMethod('myUberMethod');
});

// Somewhere in myProvider

var theUberMethod, theUberMethodName;

function setMethod(dependencyName) {
  theUberMethodName = dependencyName;
}

function performTheMethod() {
  theUberMethod = theUberMethod || $injector.get(theUberMethodName);

  // Magic

  theUberMethod();
}

您甚至可能想看一下 $ injector invoke 方法,因为这样可以将参数注入到被注入的函数方法中。 / p>

修改

我必须更好地解释它。

您遇到的问题与注入模块未在配置阶段初始化的事实有关,因此您无法将服务注入配置功能。仅允许提供商。在配置阶段之后,你必须推迟任何依赖解析。这可以通过在提供程序上使用$ get函数或注入$ injector服务并使用它来进行解析来完成。

可以像任何其他对象一样注册函数以进行依赖注入。因此,为提供者提供函数的名称就足以让它在以后解析并执行它。

myApp.provider('myFunctionCallee', function() {

  var self = this;

  return {
    hookMyFunction: function(value /* string: name of the function */ ) {
      self.myFunction = value;
    },
    $get: function($injector) {
      return {
        executeMyFunction: function() {
          return $injector.get(self.myFunction)();  
        }
      };
    }
  };
});

现在我们需要一种方法来通知注射器该功能。

示例一

这是最简单的......只需用注射器直接注册一个函数(对象)。我们将通过请求'myFunction'来解决该问题。

myApp.value('myFunction', function() { 
  return 'hello injected function!';
});

myApp.config(function(myFunctionCalleeProvider) {
  myFunctionCalleeProvider.hookMyFunction('myFunction');
});

示例二

这种方法看起来很像前一种方法。我们将直接注册一个函数,但我们也会在目标服务中注入它。

myApp.value('myFunction', function() {
  return 'hello injected service function 1';
});

myApp.factory('myService', function(myFunction) {
  return {
    myServiceFunction: myFunction
  };
});

myApp.config(function(myFunctionCalleeProvider) {
  myFunctionCalleeProvider.hookMyFunction('myFunction');
});

示例三

有时候以前的方法是不可能的。也许代码是由其他人编写的,他/她没有这个伟大的愿景。但是,我们可以在任何可注入对象上公开任何函数:

myApp.factory('myService', function() {
  return {
    // We want to target this beast
    myServiceFunction: function() {
      return 'hello injected service function 2';
    }
  };
});

// Inject the target service here ...
myApp.factory('myFunction', function(myService) {
  // ... and expose the beast.
  return myService.myServiceFunction;
});

myApp.config(function(myFunctionCalleeProvider) {
  myFunctionCalleeProvider.hookMyFunction('myFunction');
});

你可以在plunker中看到,感受和舔它。

答案 1 :(得分:1)

这两个答案都是@ null的答案的衍生物。为了完整和清晰起见,我保留这里。我不认为这个话题在其他地方已经得到了很好的讨论,而且可能很难让人满脑子(就像我的情况一样)。

在下面我不太理想的第二种方法中,我使用$q.when()来包装作为主题的已配置函数。我建议在这种情况下始终使用它,以便配置的功能可以安全地同步或异步。为了降低复杂性,我将其从第一个例子中删除。

创建服务并按名称(字符串)传递对该服务的引用。

使用引用其他服务的字符串名称配置提供程序 - 这种方式可能不是您实际尝试访问的服务:

.config(function(SomeServiceProvider) {
  SomeServiceProvider.someServiceName = 'ThisService';
})

ThisService将被SomeService称为函数。 ThisService可以将多个其他服务注入其中,对它们执行某些操作并返回结果。

.factory('ThisService', function(FooService, BarService) {
  var service = function() {
    var x = FooService.whatever();
    var y = BarService.blah();
    return x+y;
  };
  return service;
})

在您配置的SomeService中,使用$injector获取ThisService

.provider('SomeService', [
  function() {

    var config = this;

    this.someServiceName = null;

    this.$get = [
      '$injector',
      factory
    ];

    function factory($injector) {

      var service = function() {
        //do stuff
        //call the configured function
        var fn = $injector.get(config.someServiceName);
        var myData = fn();
        //etc
      };

      return service;

    }

  }
])

我目前正在通过使用默认函数扩展这一点,如果在配置中没有给出服务名称,将会调用该函数:

.provider('SomeService', [
  function() {

    var config = this;

    this.defaultFn = function() {
      //default stuff
    };

    this.someServiceName = null;

    this.$get = [
      '$injector',
      factory
    ];

    function factory($injector) {

      var service = function() {
        //do stuff

        //use defaultFn if no service name was given
        var fn = config.someServiceName
          ? $injector.get(config.someServiceName)
          : config.defaultFn
        ;
        var myData = fn();
        //etc
      };

      return service;

    }

  }
])

这种方法的一个小缺点就是它创建了另一个命名的角度组件(服务),但这确实不是问题,除非你在命名或非常不幸的情况下非常糟糕。我只是将“Config”附加到服务名称的开头,以对命名空间进行排序并对其进行标记。

另一个选项:将$ injector传递给在配置中创建的函数。

这是我的老答案,我赞成上述解决方案。

(这里使用$q与问题无关。我将其包含在内,因为它与功能非常相关 - 配置的服务可以使用同步或异步功能。)

.config([
  'MyServiceProvider',
  function(MyServiceProvider) {

    MyServiceProvider.setSomeMethod(function($injector) {
      var OtherModule = $injector.get('OtherModule');
      OtherModule.someMethod();
    });

  }
])

.provider('MyService', [
  function() {

    var config = this;

    this.someMethod = function() { };

    this.setSomeMethod = function(fn) {
      this.someMethod = fn;
    };

    this.$get = [
      '$q',
      '$injector',
      factory
    ];

    function factory($q, $injector) {

      var service = function() {
        var loaded = config.someMethod($injector);
        $q.when(loaded).then(function() {

        });
      };

      return service;

    }

  }
])