web-dev-qa-db-fra.com

Comment puis-je ajouter dynamiquement une directive dans AngularJS?

J'ai une version très résumée de ce que je fais qui résout le problème.

J'ai un simple directive. Chaque fois que vous cliquez sur un élément, il en ajoute un autre. Cependant, il doit d'abord être compilé pour pouvoir le restituer correctement.

Mes recherches m'ont conduit à $compile. Mais tous les exemples utilisent une structure compliquée que je ne sais pas vraiment comment appliquer ici.

Les violons sont ici: http://jsfiddle.net/paulocoelho/fBjbP/1/

Et le JS est là:

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

Solution de Josh David Miller: http://jsfiddle.net/paulocoelho/fBjbP/2/

210
PCoelho

Vous avez beaucoup de jQuery inutiles, mais le service $ compile est en fait super simple dans ce cas:

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

Vous remarquerez que j'ai également refactoré votre directive afin de suivre certaines pratiques exemplaires. Faites-moi savoir si vous avez des questions sur ces questions.

256

En plus de perfectionner l'exemple de Riceball LEE d'ajouter une nouvelle directive-élément

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

L'ajout d'un nouvel élément attribut-directive à un élément existé peut être effectué de la manière suivante:

Supposons que vous souhaitiez ajouter my-directive à la volée à l'élément 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

J'espère que ça t'as aidé.

75
deadrunk

L'ajout dynamique de directives sur angularjs a deux styles:

Ajouter une directive angularjs dans une autre directive

  • insertion d'un nouvel élément (directive)
  • insertion d'un nouvel attribut (directive) dans l'élément

insertion d'un nouvel élément (directive)

c'est simple. Et vous pouvez utiliser dans "lien" ou "compiler".

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

insérer un nouvel attribut à l'élément

C'est dur et me donne mal à la tête en deux jours.

Utiliser "$ compile" va générer une erreur récursive critique !! Peut-être devrait-il ignorer la directive actuelle lors de la recompilation d'un élément.

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

Donc, je dois trouver un moyen d'appeler la fonction de directive "link". Il est très difficile d’obtenir les méthodes utiles qui sont profondément cachées dans les fermetures.

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)       

Maintenant, ça marche bien.

45
Riceball LEE
function addAttr(scope, el, attrName, attrValue) {
  el.replaceWith($compile(el.clone().attr(attrName, attrValue))(scope));
}
9
user1212212

La réponse acceptée par Josh David Miller fonctionne très bien si vous essayez d’ajouter de manière dynamique une directive utilisant un inline template. Cependant, si votre directive profite de templateUrl, sa réponse ne fonctionnera pas. Voici ce qui a fonctionné pour moi:

.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
ferics2

Josh David Miller a raison.

PCoelho, Si vous vous demandez ce que $compile fait en coulisse et comment la sortie HTML est générée à partir de la directive, veuillez regarder ci-dessous

Le service $compile compile le fragment de HTML ("< test text='n' >< / test >") qui inclut la directive ("test" en tant qu'élément) et produit une fonction. Cette fonction peut ensuite être exécutée avec une portée pour obtenir la "sortie HTML d'une directive".

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

Plus de détails avec des exemples de code complets ici: http://www.learn-angularjs-apps-projects.com/AngularJs/dynamically-add-directives-in-angularjs

5
Danial Lokman

Inspiré de nombreuses réponses précédentes, je suis arrivé à la directive "stroman" suivante qui va se remplacer par toute autre directive.

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

Important: Enregistrez les directives que vous souhaitez utiliser avec restrict: 'C'. Comme ça:

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

Vous pouvez utiliser comme ceci:

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

Pour obtenir ceci:

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

Protip. Si vous ne souhaitez pas utiliser de directives basées sur des classes, vous pouvez remplacer '<div></div>' par quelque chose que vous préférez. Par exemple. avoir un attribut fixe qui contient le nom de la directive souhaitée au lieu de class.

4
Gábor Imre