Подтвердить что ты не робот

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

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

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

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

Fiddles: 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 supposed to :(
                $(this).parent().append("<test text='n'></test>");
            });
        }
    };
});

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

4b9b3361

Ответ 1

У вас много бессмысленного 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 );
      };
    }
  };
});

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

Ответ 2

В дополнение к совершенному Riceball LEE примеру добавления новой директивы element

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

Добавление нового существующего элемента атрибута в существующий элемент можно выполнить следующим образом:

Предположим, вы хотите добавить "на лету" my-directive в элемент span.

template: '<div>Hello <span>World</span></div>'

link: ($scope, $element, $attrs) ->

  span = $element.find('span').clone()
  span.attr('my-directive', 'my-directive')
  span = $compile(span)($scope)
  $element.find('span').replaceWith span

Надеюсь, что это поможет.

Ответ 3

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

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

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

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

Это просто. И вы можете использовать в "ссылке" или "компилировать".

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 );

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

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)       

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

Ответ 4

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

Ответ 5

Принятый ответ Джоша Дэвида Миллера отлично работает, если вы пытаетесь динамически добавлять директиву, которая использует встроенный 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);
            }
        }
    }
}]);

Ответ 6

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

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

Ответ 7

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

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>

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