ngDirective – директива для загрузки директив

Суть:

директива типа “атрибут”, значением идет имя поддирективы, которую необходимо динамически подгрузить. Может быть удобно в случае, когда в зависиомти от условия мы должны выводить различные компоненты.

Предистория:

В проекте (на AngularJS) стала задача вывести список сущностей, каждая из которых имеет свойства и представление (директивы). Но сложность заключается в том, что сущности могут буть разных типов, совершенно разных: с различным представлением и поведением. Вопрос: “что делать, если хочется все вывести через ng-repeat?”. Одну директиву в духе god-antipattern, где будет сразу все возможные варианты поведения и отображения, лепить не хотелось. Хотелось использовать разные, чтобы потом еще можно было делать новые и они автоматически подхватывались. Но как их подгружать?

Решение:

Для динамической сборки директивы нам нужен сервис $compile. В итоге получилось такое( выкладываю упрощенный вариант):

html:

<div ng-repeat="item in items">
  <itemDirective></itemDirective>
</div>

а внутри директивы itemDirective:

element.append($compile(angular.element('<' + $scope.item.type + '/>'))($scope));

После чего пришла мысль “а почему бы это не оформить в виде директивы, которая будет подгружать в себя в зависимости от параметра другую директиву“.

И код преобразился следующим образом:

<div ng-repeat="item in items">
  <itemDirective ng-directive="{{item.type}}"></itemDirective>
</div>

А в директиве:

element.append($compile(angular.element('<' +  attrs.ngDirective + '/>'))($scope));
Так же не помешает наблюдение за состояние атрибута, чтобы было в духе Ангуляра, поэтому добавим $observe:
attrs.$observe('ngDirective', function(directive) {
    element.html('');
    element.append($compile(angular.element('<' + directive + '/>'))($scope));
});

 

Вот код и демо.

Очень бы хотелось услышать конструктивную критику.

Идея пришла под вдохновлением от прочтения этой статьи.

 

UPD: как подсказали в сообществе:

  1. лучше не смешивать логику с представлением(хранить имя директивы в контролере)
  2. следить за утечками памяти, ибо в данном случае новая директива создает много вотчеров, которые потом никто не зачищает
  3. использовать для данной задачи ngSwitch