基于AngularJS中的属性在指令中装饰服务

时间:2014-03-11 13:11:29

标签: angularjs angularjs-directive

披露:我是棱角分明的新人,所以如果我做一些看似奇怪或完全错误的事情,请随意指出。

我有一个我想要使用的指令vpMap

<div my-map with-tile-layers with-geolocator></div>

根据以Map

开头的指令属性,我有条件装饰with服务

它看起来像这样:

angular.module('myApp.map',[])
   .service('Map',someServiceFunction)
   .directive('myMap',['Map','$provide',function(Map,$provide) {

      return {
        controller: function($scope,$element,$attrs) {

          angular.forEach($attrs,function(val,key) {
             if(key.match(/^with/)) {
               switch(key) {
                 case 'withTileLayers':
                 $provide.decorator(Map,someDecoratorFunction);
                 break;
               }
             }
          });

        }
      };

   }]);

此时我发现我无法在我的指令中访问$ offer服务,尽管我不确定原因。根据文档,您可以将任何内容注入指令中,我认为$ provide是其中一种全局角度服务,如$ http

这是糟糕的架构吗?我不理解什么规则?

1 个答案:

答案 0 :(得分:2)

供应商应在配置阶段由$injector使用。

来自providers guide

  

配置阶段结束后,与提供商的互动就是   禁止并开始创建服务的过程。我们称之为   应用程序生命周期的一部分运行阶段。

所以你必须使用配置块进行装饰。

尝试修复

要有条件地装饰服务 ,您可以在装饰器的功能中使用一些谓词,这与此类似(未经测试):

// define a predicate that checks if an object starts with a key 
function keyInObject(obj, key) {
    var propKeys = Object.keys(obj),
        index = propKeys && propKeys.length || 0;
    while (index--) {
        if (propKeys[index].indexOf(key) === 0) {
            return true;
        }
    }
    return false;
}

app.config(function($provide) {

    // uhmm, decorate
    $provide.decorator('Map', function($delegate) {
        var service = $delegate;

        // only extend the service if the predicate fulfills        
        keyInObject(service, 'with') && angular.extend(service, {
            // whatever, man
        });

        return $delegate;
    });
});

但是,即使这样也无法帮助我们,因为我们需要在链接阶段中获取密钥,这在服务注入后很久(和装饰)。

确保你完全明白装饰的含义;我们使用装饰永久地改变对象(服务)结构。 装饰是指对象的元数据,而不是其状态

解决方案

鉴于上述情况,最好的选择是简单地创建具有所有必需功能的服务,并完全放弃装饰。稍后,在link内,您可以使用迭代来决定调用哪种方法,例如:

// I took the liberty of switching the positions of key/val arguments
// for semantics sake...
angular.forEach($attrs, function(key, val) {
    if (val.match(/^with/)) {
        switch (val) {
            case 'withTileLayers':
                Map.useTileLayers(); // use specific API in the service
                break;
        }
    }
});