使用 $provide 重命名第 3 方 Angular 指令 - 不工作

Renaming 3rd party Angular Directive using $provide - not working

我在一个大型站点中使用了优秀的 Angular UI bootstrap 以及许多我自己的指令。为了整洁起见,我寻找了一种重命名几个 uib 指令而不触及它们的代码的方法,并遇到了几个 SO 问题和一个看起来不错的解决方案。问题是它对我不起作用。这是我的代码。

angular.module('s4p').config(function($provide, $compileProvider){

    console.log("I am logged in the console");

    $provide.decorator('uibPaginationDirective', function($delegate){

        console.log("I am not logged");

        $compileProvider.directive('s4pPaging', function(){
           return $delegate[0];
        });

        return function() { return angular.noop };

    });
});

我在那里放了几个 console.logs 看看它能走多远,我可以看到它甚至没有 运行 装饰器。起初我认为这可能是 uiPaginationDirective 中的一个简单的拼写错误,但如果我更改了一个字符,那么我会在控制台中收到一个很大的注入器错误。所以,它确实找到了原始指令,但修饰函数不是 运行ning。

我是不是在做傻事?

编辑:澄清一下,我使用 ng-annotate 来简化依赖注入,因此 .config()

中没有依赖数组

编辑 2:装饰器不会 运行 如果该元素未在页面上使用,我没有意识到这一点。因此,如果您使用旧元素,那么装饰器会执行 运行,但如果您使用新别名,则什么也不会发生。我开始认为我发现的这段代码可能永远不会起作用,除非使用原始指令来启动装饰器。

编辑 3:我为此添加了赏金,因为找到解决方案会很好。即使没有人可以修复示例代码,也一定有其他方法可以在不更改的情况下重命名第三方指令吗?我找到了一个名为 "alias" 的插件,它可以执行此操作,但我真的不想使用更多的第 3 方代码,我想了解解决方案。

编辑 4:我一直在玩我借来的代码,你可以在下面的 plunkr 中看到它 - http://plnkr.co/edit/3aDEed?p=preview 我认为它只有效因为演示使用 HTML 中的原始指令,后跟重命名的指令。如果您从 HTML 中删除原来的那个,那么它们都会消失。我假设这是因为指令装饰器只是 运行 第一次使用指令。所以,我现在的任务是找到一种方法让装饰器达到 运行 而无需使用原始指令,或者寻找一种更好的方法来重命名第 3 方指令...

编辑 5:我尝试了一些其他的东西,这似乎是别名插件在幕后所做的事情。

angular.module('s4p').directive('s4pPaging', function(){

    return {
        restrict: 'EA',
        replace: true,
        template: '<uib-pagination class="s4pPaging"></uib-pagination>'
    }

});

...但不幸的是我收到以下错误。

Error: [$compile:multidir] Multiple directives [s4pPaging (module: s4p), uibPagination] asking for template on:...

我不明白为什么他们都要求模板。我本以为 s4p-paging 会被 uib-pagination 替换,并且所有属性都复制到 uib-pagination 元素上。显然我不明白为什么像这样简单的东西不起作用。

试试这个:

var app = angular.module('plunker', ['ui.bootstrap', 'myDirectives']);

angular.module('myDirectives', [])
.directive('myPagination', function($injector) {
  return angular.copy($injector.get('uibPaginationDirective'))[0];
});

app.controller('PaginationDemoController', function($scope) {});

<body ng-app="plunker">
  <div ng-controller="PaginationDemoController">
    <my-pagination total-items="20" ng-model="currentPage"></my-pagination>
  </div>
</body>

无需先在页面上使用原始指令即可工作。

working plunker

根据我的 old solution for another question,您需要在页面上至少使用一个指令实例,因为那是 angular DI 的工作方式。它仅在第一次使用时懒惰地实例化任何工厂。

正如我之前在评论中提到的,您可以通过获取指令工厂(指令名称后缀 "Directive")$injector.get('directiveNameDirective').

来获取任何指令配置

这是解决问题的抽象(请记住,这也将解决原始线程中的命名冲突问题,而且它更加模块化,只需在您的应用程序中调用一次配置即可轻松设置)。

你可以get the module from my repository

angular.module('renameDirectiveUtil', [])
.provider('renameDirective', ['$provide' , '$compileProvider' , function($provide, $compileProvider){
    var directiveSet;

    this.setConfig = function setConfig(config){
      directiveSet = config;

       angular.forEach(directiveSet, function iterator(targetDir, sourceDir){
          sourceDir +=  'Directive';
          //Set up decorators
          $provide.decorator(sourceDir, function decorate($delegate){

             //Used to register a directive. $delegate is the original directive definition.
             $compileProvider.directive(targetDir, function(){
                return $delegate[0];
             });

              //This will remove the original directive definition, if not just return the delegate as is.
              return function() { return angular.noop };
          });
      });
    };

    this.$get  = ['$injector', function renameDirectiveService($injector){
      return { 
        rename : function rename(){
          angular.forEach(directiveSet, function(_,dir){
             var sourceDir = dir + 'Directive';
            //Just return the original directive definition which will trigger the decorator and redefine itself while renaming the new one.
            $injector.get(sourceDir);
          });
        }
      }
    }];
}]).run(['renameDirective',function(renameDirective){
  renameDirective.rename();
}]);

您现在需要做的就是将其作为依赖项包含在您的模块中。

 angular.module('plunker', ['renameDirectiveUtil']);

并通过提供格式为 {sourceDirectiveName : targetDirectiveName }.

的配置对象来配置它
app.config(['renameDirectiveProvider',function(renameDirectiveProvider){
  renameDirectiveProvider.setConfig({
    'property' : 'cmProperty'
  });
}]);

这是一个plunker