Как я могу динамически добавлять директиву в AngularJS?

у меня очень сводная версия того, что я делаю, что получает проблему.

у меня простой directive. Всякий раз, когда вы нажимаете на элемент, он добавляет другой. Однако сначала его нужно скомпилировать, чтобы правильно отобразить.

мои исследования привели меня к $compile. Но все примеры используют сложную структуру, которую я действительно не знаю, как применить здесь.

скрипки здесь: http://jsfiddle.net/paulocoelho/fBjbP/1/

и JS здесь:

var module = angular.module('testApp', [])
    .directive('test', function () {
    return {
        restrict: 'E',
        template: '<p>{{text}}</p>',
        scope: {
            text: '@text'
        },
        link:function(scope,element){
            $( element ).click(function(){
                // TODO: This does not do what it's supposed to :(
                $(this).parent().append("<test text='n'></test>");
            });
        }
    };
});

решение Джоша Дэвида Миллера: http://jsfiddle.net/paulocoelho/fBjbP/2/

7 ответов


У вас там много бессмысленного jQuery, но служба $ compile на самом деле супер просто в этом случае:

.directive( 'test', function ( $compile ) {
  return {
    restrict: 'E',
    scope: { text: '@' },
    template: '<p ng-click="add()">{{text}}</p>',
    controller: function ( $scope, $element ) {
      $scope.add = function () {
        var el = $compile( "<test text='n'></test>" )( $scope );
        $element.parent().append( el );
      };
    }
  };
});

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


в дополнение к идеальному примеру Riceball LEE о добавлении нового элемент-директива

newElement = $compile("<div my-directive='n'></div>")($scope)
$element.parent().append(newElement)

добавление нового


динамическое добавление директив на angularjs имеет два стиля:

добавить директиву angularjs в другую директиву

  • вставка нового элемента (директивы)
  • вставка нового атрибута (директивы) в элемент

вставка нового элемента (директивы)

это просто. И вы можете использовать в "link"или " compile".

var newElement = $compile( "<div my-diretive='n'></div>" )( $scope );
$element.parent().append( newElement );

вставка нового атрибута в элемент

это трудно, и сделать меня головная боль в течение двух дней.

использование "$ compile " вызовет критическую рекурсивную ошибку!! Возможно, он должен игнорировать текущую директиву при повторной компиляции элемента.

$element.$set("myDirective", "expression");
var newElement = $compile( $element )( $scope ); // critical recursive error.
var newElement = angular.copy(element);          // the same error too.
$element.replaceWith( newElement );

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

compile: (tElement, tAttrs, transclude) ->
   links = []
   myDirectiveLink = $injector.get('myDirective'+'Directive')[0] #this is the way
   links.push myDirectiveLink
   myAnotherDirectiveLink = ($scope, $element, attrs) ->
       #....
   links.push myAnotherDirectiveLink
   return (scope, elm, attrs, ctrl) ->
       for link in links
           link(scope, elm, attrs, ctrl)       

Теперь это хорошо работает.


function addAttr(scope, el, attrName, attrValue) {
  el.replaceWith($compile(el.clone().attr(attrName, attrValue))(scope));
}

принятый ответ Джоша Дэвида Миллера отлично работает, если вы пытаетесь динамически добавить директиву, которая использует встроенный template. Однако, если ваша директива использует templateUrl его ответ не будет работать. Вот что сработало для меня:

.directive('helperModal', [, "$compile", "$timeout", function ($compile, $timeout) {
    return {
        restrict: 'E',
        replace: true,
        scope: {}, 
        templateUrl: "app/views/modal.html",
        link: function (scope, element, attrs) {
            scope.modalTitle = attrs.modaltitle;
            scope.modalContentDirective = attrs.modalcontentdirective;
        },
        controller: function ($scope, $element, $attrs) {
            if ($attrs.modalcontentdirective != undefined && $attrs.modalcontentdirective != '') {
                var el = $compile($attrs.modalcontentdirective)($scope);
                $timeout(function () {
                    $scope.$digest();
                    $element.find('.modal-body').append(el);
                }, 0);
            }
        }
    }
}]);

Джош Дэвид Миллер прав.

PCoelho, в случае, если вам интересно, что $compile делает за кулисами и как вывод HTML генерируется из директивы, пожалуйста, посмотрите ниже

на $compile сервис компилирует фрагмент HTML("< test text='n' >< / test >"), который включает директиву("тест" в качестве элемента) и создает функцию. Затем эта функция может быть выполнена с областью, чтобы получить "HTML-вывод из директивы".

var compileFunction = $compile("< test text='n' > < / test >");
var HtmlOutputFromDirective = compileFunction($scope);

больше деталей с здесь примеры кода : http://www.learn-angularjs-apps-projects.com/AngularJs/dynamically-add-directives-in-angularjs


вдохновленный многими из предыдущих ответов, я придумал следующую директиву "Стромана", которая заменит себя любыми другими директивами.

app.directive('stroman', function($compile) {
  return {
    link: function(scope, el, attrName) {
      var newElem = angular.element('<div></div>');
      // Copying all of the attributes
      for (let prop in attrName.$attr) {
        newElem.attr(prop, attrName[prop]);
      }
      el.replaceWith($compile(newElem)(scope)); // Replacing
    }
  };
});

важно: зарегистрируйте директивы, которые вы хотите использовать с restrict: 'C'. Вот так:

app.directive('my-directive', function() {
  return {
    restrict: 'C',
    template: 'Hi there',
  };
});

вы можете использовать такой:

<stroman class="my-directive other-class" randomProperty="8"></stroman>

для этого:

<div class="my-directive other-class" randomProperty="8">Hi there</div>

советы. если вы не хотите использовать директивы, основанные на классах, вы можете изменить '<div></div>' Для что-то, что вам нравится. Е. Г. имеют фиксированный атрибут, который содержит имя нужной директивы вместо class.