Расширение угловой директивы

114

Я хотел бы внести незначительные изменения в стороннюю директиву (в частности, в Angular UI Bootstrap ). Я просто хочу добавить в область действия paneдирективы:

angular.module('ui.bootstrap.tabs', [])
.controller('TabsController', ['$scope', '$element', function($scope, $element) {
  // various methods
}])
.directive('tabs', function() {
  return {
    // etc...
  };
})
.directive('pane', ['$parse', function($parse) {
  return {
    require: '^tabs',
    restrict: 'EA',
    transclude: true,
    scope:{
      heading:'@',
      disabled:'@' // <- ADDED SCOPE PROPERTY HERE
    },
    link: function(scope, element, attrs, tabsCtrl) {
      // link function
    },
    templateUrl: 'template/tabs/pane.html',
    replace: true
  };
}]);

Но я также хочу поддерживать Angular-Bootstrap в актуальном состоянии с Bower. Как только бегу bower update, перезапишу свои изменения.

Итак, как мне расширить эту директиву отдельно от этого компонента bower?

рукав моря
источник
2
Самый чистый способ - использовать $provide.decorator(), см. Мой ответ ниже.
Элиран Малка

Ответы:

96

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

Две директивы будут иметь общую область видимости, и вы можете получить доступ и изменить область действия сторонней директивы с помощью linkметода вашей директивы .

Вариант 2: вы также можете получить доступ к области действия сторонней директивы, просто поместив свою собственную директиву с произвольным именем в один и тот же элемент (при условии, что ни одна из директив не использует изолированную область). Все директивы неизолированной области видимости для элемента будут иметь общую область видимости.

Дополнительная литература: https://github.com/angular/angular.js/wiki/Dev-Guide%3A-Understanding-Directives

Примечание. Мой предыдущий ответ касался изменения сторонней службы, а не директивы.

Дэн
источник
3
спасибо @ sh0ber, это именно то, что мне нужно. И ваш предыдущий ответ тоже помог мне в отношении сторонних сервисов.
Kyle
Эй, это действительно хороший ответ, но я не могу найти никакой документации о свойстве "приоритет" для директив. Все, что я нашел, - это рекламное объявление, в котором говорится «вы можете использовать это», но я не могу найти никаких реальных примеров.
Ciel
2
@Ciel Информация об API директивы, по-видимому, была перенесена в $compileдокумент здесь
Дэн
60

TL; DR - дай мне демо!


     Big Demo Button     
 


Используйте $provide's, decorator()чтобы украсить директиву третьей стороны.

В нашем случае мы можем расширить область действия директивы следующим образом:

app.config(function($provide) {
    $provide.decorator('paneDirective', function($delegate) {
        var directive = $delegate[0];
        angular.extend(directive.scope, {
            disabled:'@'
        });
        return $delegate;
    });
});

Сначала мы просим украсить paneдирективу, передав ее имя, объединенное с Directiveпервым аргументом, затем мы извлекаем его из параметра обратного вызова (который представляет собой массив директив, соответствующих этому имени).

Как только мы его получим, мы сможем получить его объект области видимости и при необходимости расширить его. Обратите внимание, что все это нужно делать в configблоке.

Некоторые примечания

  • Было предложено просто добавить директиву с тем же именем, а затем установить ее уровень приоритета. Помимо несемантичности (это даже не слово , я знаю…), он создает проблемы, например, что, если уровень приоритета сторонней директивы изменится?

  • ДжитендраЧаухан заявил (хотя я его не тестировал), что это решение не будет работать в версии 1.13.

Элиран Малка
источник
1
Я предлагаю вам дать ответ @ sh0ber (создать другую директиву только для генерации событий).
Элиран Малка
2
Небольшое примечание об этом ответе (который отлично работает), «Директива» в «paneDirective» имеет цель ;-) Мне потребовалось некоторое время, прежде чем я понял это: stackoverflow.com/questions/19409017/… , см. Принятые ответ.
Рой Милдер
2
привет @EliranMalka, проверь мой плункер plnkr.co/edit/0mvQjHYjQCFS6joYJdwK, надеюсь, это кому-то поможет
Джитендра Чаухан
1
Ссылка на не decorator()работает (обновлено до docs.angularjs.org/api/auto/service/$provide#decorator )
Крис Браун
1
@EliranMalka: да, bindToControllerбыл представлен в версии 1.3. Но обратите внимание, что это не следует рассматривать как альтернативное решение, это только для конкретного случая, когда исходная директива была настроена со bindToControllerсвойством. Хорошая идея, я
отправлю
8

Хотя это не прямой ответ на ваш вопрос, вы, возможно, захотите узнать, что последняя версия (в главной) http://angular-ui.github.io/bootstrap/ добавила поддержку для отключения вкладок. Эта функция была добавлена ​​через: https://github.com/angular-ui/bootstrap/commit/2b78dd16abd7e09846fa484331b5c35ece6619a2

pkozlowski.opensource
источник
+1 за хедз-ап. хорошо знать. Я предполагаю, что angular-bootstrap bower и компонент начальной загрузки angular-ui не синхронизированы.
Kyle
6

Другое решение, в котором вы создаете новую директиву, которая расширяет ее без изменения исходной директивы.

Решение аналогично решению декоратора:

Создайте новую директиву и введите в качестве зависимости директиву, которую вы хотите расширить.

app.directive('extendedPane', function (paneDirective) {

  // to inject a directive as a service append "Directive" to the directive name
  // you will receive an array of directive configurations that match this 
  // directive (usually only one) ordered by priority

  var configExtension = {
     scope: {
       disabled: '@'
     }
  }

  return angular.merge({}, paneDirective[0], configExtension)
});

Таким образом, вы можете использовать исходную директиву и расширенную версию в одном приложении.

kidroca
источник
2
Это здорово, именно то, что мне нужно, чтобы расширить директиву изолированной области действия моими собственными переменными !! Я обнаружил, что angular.extend не выполняет глубокое копирование объектов, поэтому он заменяет объект области видимости paneDirective на этот. Альтернативой является angular.merge, который сохранит исходную область видимости из PaneDirective и добавит / объединит переменные, определенные здесь.
mathewguest
1
да, angular.mergeнадо было использовать,
обновлю
1

Вот еще одно решение для другого сценария расширения привязок к директиве, у которой есть bindToControllerсвойство.

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

Гилад Майани
источник