caoruiy‘s blog

Wisdom outweighs any wealth

Angular开发者指南-Decorator(装饰器)

原文:/ Developer Guide / Decorators

什么是装饰器?

装饰器是一种设计模式,用于在不修改原始源代码的情况下分离类的修改或装饰。 在AngularJS中,装饰器是允许在使用之前修改服务(service),指令(directive)或过滤器(filter)的功能。

如何使用装饰器:

有两种方式:
– $provide.decorator
– module.decorator

在传递给需要它的服务之前,每个装饰器都提供了一个$delegate,它是实例化的服务/指令/过滤器。

$provide.decorator

装饰器函数有一个 $delegate 参数,他是服务的实例:

angular.module('myApp', [])

.config([ '$provide', function($provide) {

  $provide.decorator('$log', [
    '$delegate',
    function $logDecorator($delegate) {

      var originalWarn = $delegate.warn;
      $delegate.warn = function decoratedWarn(msg) {
        msg = 'Decorated Warn: ' + msg;
        originalWarn.apply($delegate, arguments);
      };

      return $delegate;
    }
  ]);
}]);

在$log服务被实例化之后,装饰器被触发。 装饰器函数有一个注入的 $delegate 对象来提供对装饰器中与选择器匹配的服务的访问。 这个 $delegate 将是您正在装饰的服务。 提供给装饰器的功能的返回值将代替装饰的服务,指令或过滤器。

$delegate 可以被修改或者完全替换,下面给出一个 myService 服务,同时他具有一个 someFn 方法:

完全替换$delegate:

angular.module('myApp', [])

.config([ '$provide', function($provide) {

  $provide.decorator('myService', [
    '$delegate',
    function myServiceDecorator($delegate) {

      var myDecoratedService = {
        // new service object to replace myService
      };
      return myDecoratedService;
    }
  ]);
}]);

部分修改$delegate:

angular.module('myApp', [])

.config([ '$provide', function($provide) {

  $provide.decorator('myService', [
    '$delegate',
    function myServiceDecorator($delegate) {

      var someFn = $delegate.someFn;

      function aNewFn() {
        // new service function
        someFn.apply($delegate, arguments);
      }

      $delegate.someFn = aNewFn;
      return $delegate;
    }
  ]);
}]);

添加 $delegate:

angular.module('myApp', [])

.config([ '$provide', function($provide) {

  $provide.decorator('myService', [
    '$delegate',
    function myServiceDecorator($delegate) {

      function helperFn() {
        // an additional fn to add to the service
      }

      $delegate.aHelpfulAddition = helperFn;
      return $delegate;
    }
  ]);
}]);

请注意,装饰器功能返回的任何内容都将替换正在装饰的功能。例如,一个缺少的return语句将擦除整个被装饰的对象。

装饰者对不同的服务有不同的规则。 这是因为服务以不同的方式注册。 服务按名称进行选择,但通过将 FilterDirectie 附加到名称的末尾来选择当前装饰的是过滤器还是指令。 提供的 $delegate 由服务类型决定。

Service Type Selector $delegate
Service serviceName The object or function returned by the service
Directive directiveName + ‘Directive’ An Array.1
Filter filterName + ‘Filter’ The function returned by the filter

1 : 多个指令可以注册到同一个选择器/名称

注意:开发人员应该注意如何以及为什么要修改服务的$delegate。 不仅应该保留使用者对服务的预期功能(不应该大刀阔斧的修改原服务的使用方法,在调整功能时,API不应该修改),而且某些功能(例如指令注册)并不是在装饰之后发生的,而是在最初服务的创建/注册期间发生的。 这意味着,例如,诸如将指令对象推送到指令$delegate的操作可能会导致意外的行为。 此外,当装载核心服务,指令或过滤器时,应该非常小心,因为这可能会意外地影响框架的功能。

module.decorator

此函数与$provide.decorator函数相同,除了通过模块API公开。 这允许您将您的装饰器模式与模块配置块分开。

像$provide.decorator一样,module.decorator函数在应用程序的配置阶段运行。 这意味着您可以在定义装饰服务 之前 定义一个module.decorator。

由于您可以应用多个装饰器,值得注意的是,装饰应用程序始终遵循声明的顺序:

  • 如果一个服务由$provide.decorator和module.decorator两者进行装饰,则装饰器按顺序应用:
angular.module('theApp', [])
.factory('theFactory', theFactoryFn)
.config(function($provide) {
  $provide.decorator('theFactory', provideDecoratorFn); // 第一个运行
})
.decorator('theFactory', moduleDecoratorFn); // 第二个运行
  • 如果服务已被多次声明,装饰器将装饰最后声明的服务:
angular
.module('theApp', [])
.factory('theFactory', theFactoryFn)
.decorator('theFactory', moduleDecoratorFn)
.factory('theFactory', theOtherFactoryFn);

// `theOtherFactoryFn` 被当做 'theFactory' provider 并且他被 `moduleDecoratorFn` 装饰.

Example Applications

更多的示例请参照原文,本处只做简单的演示:

  1. 修改一个服务:
var app = angular.module('test',['ngAnimate']);

app.decorator('src', ['$delegate', function($delegate){

    // 查看下$delegate
    console.log($delegate);
    // 重写服务中的方法
    $delegate.src = function(){
        console.log('new src fun');
    }
}]);

app.service('src', function(){
    this.src = function(){
        return 'src';
    }
});

返回服务中定义的各种方法所组成的一个对象,可以直接修改某些方法。

  1. 重新定义一个指令的部分:
var app = angular.module('test',['ngAnimate']);

app.config([ '$provide', function($provide) {
    $provide.decorator('helloDirective', ['$delegate',function($delegate) {
        // 如果你不知道对于指令来说,$delegate是什么,可以console一下,看看结果
        console.log($delegate);
        // 替换hello指令的模板
        $delegate[0]['template'] = '你好 {{name}}'
        return $delegate;
    }
    ]);
}]);

app.directive('hello', function(){
    return {
        restrict : 'E',
        scope : {
            name :'='
        },
        template : 'hello {{name}}'
    }
});

指令返回的$delegate是指令中定义的各个属性所组成的一个数组对象,该对象属性可能包含,restrict, scope, telemate, compile, link等等。
并且在下标0下,可以拿到该对象。

  1. 重写下过滤器的部分:
var app = angular.module('test',['ngAnimate']);

app.decorator('trueHtmlFilter', ['$delegate','$sce',function($delegate, $sce){
    console.log($delegate);
    $delegate = function(html){
        console.log('rewrite this filter');
        return $sce.trueHtml(html);
    }
    return $delegate;
}]);

app.filter('trueHtml',['$sce',function($sce){
    return function(html){
        return $sce.trustAsHtml(html);
    }
}]);

过滤器的$delegate是过滤器定义函数,你可以重写该方法,用来重新定义过滤器,或者对原方法进行一些修改。

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注