本文介绍了基于 AngularJS 中的属性在指令中装饰服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

披露:我是 angular 的新手,所以如果我正在做一些看起来很奇怪或完全错误的事情,请随时指出.

我有一个指令 vpMap 我想像这样使用:

我有一个 Map 服务,我想根据以 with

开头的指令的属性有条件地装饰它

看起来像这样:

angular.module('myApp.map',[]).service('地图',someServiceFunction).directive('myMap',['Map','$provide',function(Map,$provide) {返回 {控制器:函数($scope,$element,$attrs){angular.forEach($attrs,function(val,key) {if(key.match(/^with/)) {开关(键){案例'withTileLayers':$provide.decorator(Map,someDecoratorFunction);休息;}}});}};}]);

此时我发现我无法访问指令中的 $provide 服务,尽管我不确定为什么.根据文档,您可以将任何内容注入指令,我认为 $provide 是像 $http

这样的全局角度服务之一

这是糟糕的架构吗?我不明白哪些规则?

解决方案

Providers 旨在供 $injector 在配置阶段使用.

来自提供者指南:

配置阶段结束后,与提供者的交互是不允许,然后开始创建服务的过程.我们称之为应用程序生命周期的一部分,即运行阶段.

所以你必须使用配置块来装饰.

尝试修复

有条件地装饰一个服务,您可以在装饰器的函数中使用一些谓词,类似于以下内容(未测试):

//定义一个谓词来检查对象是否以键开头函数 keyInObject(obj, key) {var propKeys = Object.keys(obj),索引 = propKeys &&propKeys.length ||0;而(索引--){if (propKeys[index].indexOf(key) === 0) {返回真;}}返回假;}app.config(功能($提供){//嗯,装饰一下$provide.decorator('Map', function($delegate) {var service = $delegate;//只有在谓词满足时才扩展服务keyInObject(service, 'with') &&angular.extend(服务,{//无论如何,伙计});返回 $delegate;});});

然而,即使这样也无济于事,因为我们需要在链接阶段获取密钥,这是在服务注入之后(和装饰).

为了确保您完全理解这里装饰的含义;我们使用装饰来永久改变对象(服务)结构.装饰指的是对象的元数据,而不是它的状态.

解决方案

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

//我随意切换了 key/val 参数的位置//为了语义...angular.forEach($attrs, function(key, val) {if (val.match(/^with/)) {开关(值){案例'withTileLayers':Map.useTileLayers();//在服务中使用特定的 API休息;}}});

Disclosure: I'm new to angular, so if I'm doing something that appears strange or just plain wrong, feel free to point that out.

I have a directive vpMap that I want to use like this:

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

I have a Map service that I want to decorate conditionally, based on the attributes of the directive that begin with with

It looks something like this:

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;
               }
             }
          });

        }
      };

   }]);

It's at this point that I discover I can't access the $provide service in my directive, although I'm not sure why. According to the documentation you can inject anything into directives and, I thought $provide was one of those global angular services like $http

Is this bad architecture? What rules am I not understanding?

解决方案

Providers are meant to be used by the $injector in the configuration phase.

From the providers guide:

So you would have to use the config block for decorating.

Attempted Fix

To decorate a service conditionally, you may use some predicate inside the decorator's function, something along the lines of this (not tested):

// 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;
    });
});

However, even this won't help us, as we need to get the keys in the linking phase, which is well after the service have been injected (and decorated).

To make sure you fully understand the meaning of decoration here; we use decoration to permanently alter the object (the service) structure. Decoration refers to the object's meta data, not its state.

Solution

Given the above, the best choice would be to simply create a service with all the required functionality, and abandon decoration altogether. Later, while inside link you can use the iteration to decide which of the methods to call, e.g.:

// 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;
        }
    }
});

这篇关于基于 AngularJS 中的属性在指令中装饰服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-24 00:34