Переименование сторонней угловой директивы с использованием $ обеспечить - не работает

Я использую превосходную загрузку Angular UI на большом сайте вместе со множеством моих собственных директив. Ради аккуратности я искал способ переименовать пару директив 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, но если я изменю хотя бы один символ, я получу большую ошибку инжектора в консоли. Итак, он определенно находит оригинальную директиву, но функция decorate не работает.

Я делаю что-то действительно глупое?

Редактировать: просто чтобы уточнить, я использую ng-annotate для упрощения внедрения зависимостей, поэтому в.config() нет массива зависимостей

Редактировать 2: Декоратор не запускается, если элемент не используется на странице, что я не понял. Таким образом, если вы используете старый элемент, тогда декоратор работает, но если вы используете новый псевдоним, то ничего не происходит вообще. Я начинаю думать, что этот код, который я обнаружил, мог никогда не сработать, если оригинальная директива не использовалась для запуска декоратора.

Изменить 3: я добавил награду к этому, потому что было бы неплохо найти решение. Даже если никто не сможет исправить пример кода, должны быть другие способы переименования сторонней директивы без ее изменения? Я нашел плагин под названием "псевдоним", который делает это, но я действительно не хочу использовать больше стороннего кода, и я хочу понять решение.

Изменить 4: я играл с кодом, который я позаимствовал, чтобы сделать это, что вы можете увидеть в следующем plunkr - http://plnkr.co/edit/3aDEed?p=preview Я думаю, что это работает только потому, что в демонстрационной версии используется оригинальная директива в HTML, за которой следует переименованная директива. Если вы удалите оригинал из HTML, они оба исчезнут. Я полагаю, это потому, что декоратор директивы запускается только при первом использовании директивы. Итак, теперь я хочу найти способ заставить декоратор работать без использования оригинальной директивы или найти лучший способ переименовать директиву третьей стороны...

Редактировать 5: я попробовал что-то еще, что похоже на то, что плагин псевдонимов, кажется, делает под капотом.

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

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

});

... но, к сожалению, я получаю следующую ошибку.

Ошибка: [$compile:multidir] Несколько директив [s4pPaging (module: s4p), uibPagination] запрашивает шаблон для:...

Я не понимаю, почему они оба просят шаблон. Я бы предположил, что s4p-пейджинг будет заменен на uib-нумерацию страниц, и все атрибуты будут скопированы в элемент uib-pagination. Я не понимаю, почему что-то простое не работает.

2 ответа

Решение

Исходя из моего старого решения другого вопроса, вам нужно будет использовать хотя бы один экземпляр директивы на странице, так как именно так работает угловой DI. Он лениво создает экземпляр любой фабрики, только когда он используется впервые.

Как я уже упоминал в своем комментарии ранее, вы можете получить любую конфигурацию директивы, получив фабрику директивы (имя директивы с добавлением слова "Директива")$injector.get('directiveNameDirective'),

Вот абстракция для решения проблемы (помните, что это также решит проблему конфликта имен из исходного потока, и это более модульно и легко настраивается с помощью всего одного вызова конфигурации в вашем приложении).

Вы можете получить модуль из моего хранилища

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

Вот плункер

Попробуй это:

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>

Это работает без необходимости сначала использовать оригинальную директиву на странице.

Смотри рабочий плункер

Другие вопросы по тегам