¿Cómo puedo agregar dinámicamente una directiva en AngularJS?


Tengo una versión muy reducida de lo que estoy haciendo que transmite el problema.

Tengo un simple directive. Cada vez que hace clic en un elemento, agrega otro. Sin embargo, necesita ser compilado primero para renderizarlo correctamente.

Mi investigación me llevó a $compile. Pero todos los ejemplos usan una estructura complicada que realmente no sé cómo aplicar aquí.

Los violines están aquí: http://jsfiddle.net/paulocoelho/fBjbP/1 /

Y la JS es aquí:

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

Solución de Josh David Miller: http://jsfiddle.net/paulocoelho/fBjbP/2 /

Author: Flip, 2013-03-07

7 answers

Tiene un montón de jQuery sin sentido, pero el servicio compile compile es en realidad super simple en este caso:

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

Notarán que refactoricé su directiva también para seguir algunas de las mejores prácticas. Hazme saber si tienes preguntas sobre alguno de esos.

 252
Author: Josh David Miller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-07 18:51:22

Además del ejemplo perfecto de Riceball LEE de agregar un nuevo elemento -directiva

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

Agregar un nuevo atributo -directiva al elemento existente podría hacerse de esta manera:

Digamos que desea agregar sobre la marcha my-directive al elemento 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

Espero que eso ayude.

 72
Author: deadrunk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-21 16:12:43

La adición dinámica de directivas en angularjs tiene dos estilos:

Añadir una directiva angularjs en otra directiva

  • insertar un nuevo elemento (directiva)
  • insertar un nuevo atributo (directiva) al elemento

Insertar un nuevo elemento (directiva)

Es simple. Y u puede utilizar en "enlace" o "compilar".

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

Insertar un nuevo atributo en el elemento

Es difícil, y me da dolor de cabeza en dos días.

Usando "compile compile" elevará el error recursivo crítico!! Tal vez debería ignorar la directiva actual al volver a compilar elemento.

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

Por lo tanto, tengo que encontrar una manera de llamar a la directiva "enlace" función. Es muy difícil obtener los métodos útiles que se ocultan profundamente dentro de los cierres.

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)       

Ahora, funciona bien.

 43
Author: Riceball LEE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-16 09:28:53
function addAttr(scope, el, attrName, attrValue) {
  el.replaceWith($compile(el.clone().attr(attrName, attrValue))(scope));
}
 9
Author: user1212212,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-30 22:19:42

La respuesta aceptada por Josh David Miller funciona muy bien si usted está tratando de agregar dinámicamente una directiva que utiliza un inline template. Sin embargo, si su directiva se aprovecha de templateUrl su respuesta no funcionará. Esto es lo que funcionó para mí:

.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);
            }
        }
    }
}]);
 5
Author: ferics2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-25 07:15:08

Josh David Miller tiene razón.

PCoelho, En caso de que se pregunte qué hace $compile entre bastidores y cómo se genera la salida HTML a partir de la directiva, eche un vistazo a continuación

El servicio $compile compila el fragmento de HTML("< test text='n' >< / test >") que incluye la directiva("test" como elemento) y produce una función. Esta función se puede ejecutar con un ámbito para obtener la "salida HTML de una directiva".

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

Más detalles con ejemplos de código completo aquí: http://www.learn-angularjs-apps-projects.com/AngularJs/dynamically-add-directives-in-angularjs

 5
Author: Danial Lokman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-18 05:05:41

Inspirado en muchas de las respuestas anteriores, se me ocurrió la siguiente directiva "stroman" que se reemplazará por cualquier otra directiva.

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

Importante: Registre las directivas que desea utilizar con restrict: 'C'. Así:

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

Puedes usar así:

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

Para obtener esto:

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

Protip. Si no quieres usar directivas basadas en clases entonces puedes cambiar '<div></div>' a algo que te guste. P. ej. tener una se corrigió el atributo que contiene el nombre de la directiva deseada en lugar de class.

 4
Author: Gábor Imre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-14 17:34:41