451 votes

Directives Angular - quand et comment utiliser compile, controller, pre-link et post-link

Lors de l'écriture d'une directive Angular, il est possible d'utiliser l'une des fonctions suivantes pour manipuler le comportement, le contenu et l'apparence du DOM de l'élément sur lequel la directive est déclarée :

  • compiler
  • contrôleur
  • pré-lien
  • post-link

Il semble y avoir une certaine confusion quant à la fonction à utiliser. Cette question porte sur les points suivants :

Principes de la directive

Nature de la fonction, ce qu'il faut faire et ce qu'il ne faut pas faire

Questions connexes :

167voto

Izhaki Points 5883

Dans quel ordre les fonctions directives sont-elles exécutées ?

Pour une seule directive

Sur la base des éléments suivants plunk Considérons le balisage HTML suivant :

<body>
    <div log='some-div'></div>
</body>

Avec la déclaration de directive suivante :

myApp.directive('log', function() {

    return {
        controller: function( $scope, $element, $attrs, $transclude ) {
            console.log( $attrs.log + ' (controller)' );
        },
        compile: function compile( tElement, tAttributes ) {
            console.log( tAttributes.log + ' (compile)'  );
            return {
                pre: function preLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (pre-link)'  );
                },
                post: function postLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (post-link)'  );
                }
            };
         }
     };  

});

La sortie de la console sera la suivante :

some-div (compile)
some-div (controller)
some-div (pre-link)
some-div (post-link)

Nous constatons que compile est exécuté en premier, puis controller entonces pre-link et enfin suivi par post-link .

Pour les directives imbriquées

Remarque : Ce qui suit ne s'applique pas aux directives qui rendent leurs enfants dans leur fonction de lien. Plusieurs directives Angular (comme ngIf, ngRepeat, ou n'importe quelle directive avec une fonction transclude ) le font. Ces types de directives auront nativement leur link fonction appelée avant leurs directives sur les enfants compile est appelé.

Le balisage HTML original est souvent constitué d'éléments imbriqués, chacun ayant sa propre directive. Comme dans le balisage suivant (voir plunk ) :

<body>
    <div log='parent'>
        <div log='..first-child'></div>
        <div log='..second-child'></div>
    </div>
</body>

La sortie de la console ressemblera à ceci (sans les commentaires) :

// The compile phase
parent (compile)
..first-child (compile)
..second-child (compile)

// The link phase   
parent (controller)
parent (pre-link)
..first-child (controller)
..first-child (pre-link)
..first-child (post-link)
..second-child (controller)
..second-child (pre-link)
..second-child (post-link)
parent (post-link)

On peut distinguer deux phases - la compiler et la phase enlace phase.

La phase de compilation

Lorsque le DOM est chargé, Angular commence la phase de compilation, où il parcourt le balisage de haut en bas, et appelle compile pour toutes les directives. Graphiquement, nous pourrions l'exprimer de la manière suivante :

An image illustrating the compilation loop for children

Il est peut-être important de mentionner qu'à ce stade, les modèles que la fonction de compilation obtient sont les modèles de source (et non les modèles d'instance).

La phase de liaison

Les instances DOM sont souvent le simple résultat du rendu d'un modèle source dans le DOM, mais elles peuvent être créées par ng-repeat ou introduites à la volée.

Chaque fois qu'une nouvelle instance d'un élément avec une directive est rendue dans le DOM, la phase de liaison commence.

Dans cette phase, Angular appelle controller , pre-link , itère les enfants, et appelle post-link sur toutes les directives, comme suit :

An illustration demonstrating the link phase steps

90voto

Izhaki Points 5883

Que se passe-t-il d'autre entre ces appels de fonction ?

Les différentes fonctions directives sont exécutées à l'intérieur de deux autres fonctions angulaires appelées $compile (lorsque la directive compile est exécuté) et une fonction interne appelée nodeLinkFn (lorsque la directive controller , preLink y postLink sont exécutées). Plusieurs choses se produisent au sein de la fonction angulaire avant et après l'appel des fonctions directives. L'élément le plus remarquable est sans doute la récursivité de l'enfant. L'illustration simplifiée suivante montre les étapes clés des phases de compilation et de liaison :

An illustration showing Angular compile and link phases

Pour illustrer ces étapes, utilisons le code HTML suivant :

<div ng-repeat="i in [0,1,2]">
    <my-element>
        <div>Inner content</div>
    </my-element>
</div>

Avec la directive suivante :

myApp.directive( 'myElement', function() {
    return {
        restrict:   'EA',
        transclude: true,
        template:   '<div>{{label}}<div ng-transclude></div></div>'
    }
});

Compiler

En compile L'API se présente comme suit :

compile: function compile( tElement, tAttributes ) { ... }

Les paramètres sont souvent précédés du préfixe t pour indiquer que les éléments et attributs fournis sont ceux du modèle source, plutôt que ceux de l'instance.

Avant l'appel à compile le contenu transclu (le cas échéant) est supprimé et le modèle est appliqué au balisage. Ainsi, l'élément fourni au modèle compile se présentera comme suit :

<my-element>
    <div>
        "{{label}}"
        <div ng-transclude></div>
    </div>
</my-element>

Remarquez que le contenu transclu n'est pas réinséré à ce stade.

Après l'appel à la fonction .compile Angular parcourra tous les éléments enfants, y compris ceux qui viennent d'être introduits par la directive (les éléments du modèle, par exemple).

Création d'une instance

Dans notre cas, trois instances du modèle source ci-dessus seront créées (par ng-repeat ). Ainsi, la séquence suivante sera exécutée trois fois, une fois par instance.

Contrôleur

En controller L'API implique :

controller: function( $scope, $element, $attrs, $transclude ) { ... }

En entrant dans la phase de liaison, la fonction de liaison est renvoyée par l'intermédiaire de $compile est désormais doté d'un champ d'application.

Tout d'abord, la fonction de lien crée un champ d'application enfant ( scope: true ) ou une portée isolée ( scope: {...} ) si elle est demandée.

Le contrôleur est alors exécuté, avec la portée de l'élément d'instance.

Pré-lien

En pre-link L'API se présente comme suit :

function preLink( scope, element, attributes, controller ) { ... }

Il ne se passe pratiquement rien entre l'appel à la directive .controller et le .preLink fonction. Angular fournit tout de même des recommandations quant à l'utilisation de chacune d'entre elles.

Suite à la .preLink la fonction de lien parcourt chaque élément enfant - en appelant la fonction de lien correcte et en lui attachant la portée actuelle (qui sert de portée parentale pour les éléments enfants).

Post-lien

En post-link est similaire à celle de l'API pre-link fonction :

function postLink( scope, element, attributes, controller ) { ... }

Il convient peut-être de noter qu'une fois que la directive .postLink est appelée, le processus de liaison de tous ses éléments enfants est terminé, y compris tous les éléments .postLink fonctions.

Cela signifie qu'au moment où .postLink est appelé, les enfants sont "vivants" et prêts. Cela comprend :

  • liaison de données
  • transclusion appliquée
  • champ d'application ci-joint

À ce stade, le modèle se présente donc comme suit :

<my-element>
    <div class="ng-binding">
        "{{label}}"
        <div ng-transclude>                
            <div class="ng-scope">Inner content</div>
        </div>
    </div>
</my-element>

43voto

Izhaki Points 5883

Comment déclarer les différentes fonctions ?

Compilation, Contrôleur, Pré-lien et Post-lien

Si l'on veut utiliser les quatre fonctions, la directive se présentera sous la forme suivante :

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        compile: function compile( tElement, tAttributes ) {
            // Compile code goes here.
            return {
                pre: function preLink( scope, element, attributes, controller ) {
                    // Pre-link code goes here
                },
                post: function postLink( scope, element, attributes, controller ) {
                    // Post-link code goes here
                }
            };
        }
    };  
});

Remarquez que compile renvoie un objet contenant à la fois les fonctions pre-link et post-link ; dans le jargon Angular, on dit que la fonction compile renvoie un objet de type fonction de modèle .

Compilation, contrôleur et post-lien

Si pre-link n'est pas nécessaire, la fonction de compilation peut simplement renvoyer la fonction post-link au lieu d'un objet de définition, comme suit :

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        compile: function compile( tElement, tAttributes ) {
            // Compile code goes here.
            return function postLink( scope, element, attributes, controller ) {
                    // Post-link code goes here                 
            };
        }
    };  
});

Parfois, on souhaite ajouter un compile après la méthode (post) link a été définie. Pour cela, on peut utiliser :

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        compile: function compile( tElement, tAttributes ) {
            // Compile code goes here.

            return this.link;
        },
        link: function( scope, element, attributes, controller ) {
            // Post-link code goes here
        }

    };  
});

Contrôleur et post-lien

Si aucune fonction de compilation n'est nécessaire, il est possible de ne pas la déclarer et de fournir la fonction de post-liaison sous la rubrique link de l'objet de configuration de la directive :

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        link: function postLink( scope, element, attributes, controller ) {
                // Post-link code goes here                 
        },          
    };  
});

Pas de contrôleur

Dans tous les exemples ci-dessus, il suffit de supprimer la mention controller si elle n'est pas nécessaire. Ainsi, par exemple, si seulement post-link est nécessaire, on peut utiliser :

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        link: function postLink( scope, element, attributes, controller ) {
                // Post-link code goes here                 
        },          
    };  
});

31voto

Izhaki Points 5883

Quelle est la différence entre un modèle et un exemple de modèle?

Le fait que Angulaire permet de manipulation du DOM signifie que l'entrée de balisage dans le processus de compilation diffèrent parfois de la sortie. En particulier, certaines d'entrée de balisage peut être cloné un peu de temps (comme avec ng-repeat) avant d'être rendus à la DOM.

Angulaire de la terminologie est un peu incohérent, mais il a encore fait la distinction entre deux types de balises:

  • Modèle Source - le balisage pour être cloné, si nécessaire. Si cloné, cette majoration ne sera pas rendu à la DOM.
  • Exemple de modèle - la majoration réelle pour être rendus à la DOM. Si le clonage est impliqué, chaque instance sera un clone.

Le balisage suivant illustre cela:

<div ng-repeat="i in [0,1,2]">
    <my-directive>{{i}}</my-directive>
</div>

La source html définit

    <my-directive>{{i}}</my-directive>

qui sert de modèle source.

Mais comme il est enveloppé dans un ng-repeat directive, ce modèle de source sera cloné (3 fois dans notre cas). Ces clones sont en instance de modèle, chaque apparaîtra dans les DOM et à être lié à la portée pertinente.

23voto

Izhaki Points 5883

Nature de la fonction, ce qu'il faut faire et ce qu'il ne faut pas faire

Compiler

Chaque directive est assortie d'une compile n'est appelée qu'une seule fois, lors du démarrage d'Angular.

Officiellement, c'est l'endroit où l'on peut effectuer des manipulations de modèles (source) qui n'impliquent pas la portée ou la liaison de données.

Il s'agit avant tout d'une question d'optimisation ; considérez le balisage suivant :

<tr ng-repeat="raw in raws">
    <my-raw></my-raw>
</tr>

En <my-raw> rendra un balisage DOM particulier. Nous pouvons donc soit :

  • Autoriser ng-repeat pour dupliquer le modèle source ( <my-raw> ), puis de modifier le balisage de chaque modèle d'instance (en dehors de la section compile ).
  • Modifiez d'abord le modèle source pour y inclure les balises souhaitées (dans la section compile ), ce n'est qu'ensuite que l'on autorise la fonction ng-repeat la dupliquer.

S'il y a 1000 raws, la dernière option peut être plus rapide que la première.

À faire

  • Manipuler les balises pour qu'elles servent de modèle aux instances (clones).

Dont's

  • Attachez des gestionnaires d'événements.
  • Inspecter les éléments enfants.
  • Observations sur les attributs.
  • La mise en place des montres sur la lunette.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X