Directives angulaires - quand et comment utiliser compile, controller, pre-link et post-link [closed]

8 réponses

dans quel ordre les fonctions de directive sont exécutées?

pour une directive unique

basé sur le suivant Pluk , considérez le balisage HTML suivant:

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

avec la déclaration 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:

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

Nous pouvons voir que compile est exécuté en premier, puis controller , puis pre-link et le dernier est post-link .

pour les directives imbriquées

Note: ce qui suit ne s'applique pas aux directives qui rendent leurs enfants dans leur fonction de lien. Plusieurs directives angulaires le font (comme ngIf, ngRepeat, ou toute autre directive avec transclude ). Ces directives auront nativement leur fonction link appelée avant les directives enfants compile sont appelées.

le balisage HTML original est souvent fait d'éléments imbriqués, chacun avec sa propre directive. Comme dans le markup 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:

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

nous pouvons distinguer deux phases ici - le compiler phase et le lien phase.

la phase de compilation

quand le DOM est chargé angulaire commence la phase de compilation, où il traverse le markup top-down, et appelle compile sur toutes les directives. Graphiquement, nous pourrions l'exprimer ainsi:

An image illustrating the compilation loop for children

il est peut-être important de mentionner qu'à ce stade, les templates que la fonction de compilation obtient sont les templates source (pas le template d'instance).

Le lien "de la phase 1519540920"

les instances DOM sont souvent simplement le résultat d'un modèle de source rendu au 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 au DOM, la phase de lien commence.

dans cette phase, les appels angulaires controller , pre-link , itère les enfants, et l'appel post-link sur toutes les directives, comme ceci:

An illustration demonstrating the link phase steps

166
répondu Izhaki 2016-06-03 20:06:31

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

les diverses fonctions de la directive sont exécutées à l'intérieur de deux autres fonctions angulaires appelées $compile (où la directive compile est exécutée) et une fonction interne appelée nodeLinkFn (où les directives controller , preLink et postLink sont exécutées). Diverses choses se produisent dans la fonction angulaire avant et après les fonctions de directive sont appelées. Peut-être le plus notamment est l'enfant de la récursivité. L'illustration simplifiée suivante montre les étapes clés dans les phases de compilation et de lien:

An illustration showing Angular compile and link phases

pour démontrer ces étapes, utilisons le balisage 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

L'API compile ressemble à cela:

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

souvent, les paramètres sont préfixés avec t pour signifier les éléments et attributs fournis sont ceux du modèle source, plutôt que celui de l'instance.

avant l'appel à compile contenu transcluse (le cas échéant) est supprimé, et le modèle est appliqué à la marge. Ainsi, l'élément fourni à la fonction compile ressemblera à ceci:

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

notez que le contenu transclusifié n'est pas réintroduit à ce point.

suite à l'appel à la directive .compile , Angular traversera tous les éléments enfants, y compris ceux qui peuvent avoir été introduits par la directive (les éléments de modèle, par exemple).

création D'Instance

dans notre cas, trois instances du modèle de source ci-dessus seront créées (par

). Ainsi, la séquence suivante sera exécutée trois fois, une fois par instance.

contrôleur

L'API controller implique:

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

entrant dans la phase de liaison, la fonction de liaison retournée via $compile est maintenant fournie avec une portée.

tout d'abord, la fonction link crée une portée enfant ( scope: true ) ou une portée isolée ( scope: {...} ) si demandé.

le contrôleur est alors exécuté, à condition que la portée de la instance de l'élément.

Pré-link

L'API pre-link ressemble à cela:

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

il ne se passe pratiquement rien entre l'appel à la fonction .controller de la directive et la fonction .preLink . Angular continue de fournir des recommandations quant à la façon d'utiliser chacune.

après l'appel .preLink , la fonction de lien traverse chaque élément enfant-appelant la fonction de lien correcte et lui attacher le champ d'application actuel (qui sert de champ parent pour les éléments enfants).

Post-link

L'API post-link est similaire à celle de la fonction pre-link :

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

peut-être vaut-il la peine de noter qu'une fois que la fonction .postLink d'une directive est appelée, le processus de lien de tous ses éléments enfants a terminé, y compris toutes les fonctions .postLink des enfants.

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

  • liaison de données
  • transclusion appliquée
  • domaine d'application joint

le modèle à ce stade ressemblera ainsi:

<my-element>
    <div class="ng-binding">
        "{{label}}"
        <div ng-transclude>                
            <div class="ng-scope">Inner content</div>
        </div>
    </div>
</my-element>
89
répondu Izhaki 2016-06-03 20:07:15

Comment déclarer les différentes fonctions?

compiler, Controller, Pre-link & Post-link

si l'on doit utiliser les quatre fonctions, la directive suivra ce formulaire:

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

Notice that compile retourne un objet contenant à la fois les fonctions pré-lien et post-lien; En jargon angulaire nous disons que la fonction compile retourne une template function .

Compiler, Controller & Post-link

si pre-link n'est pas nécessaire, la fonction compiler peut simplement retourner la fonction post-link au lieu d'un objet de définition, comme ceci:

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

parfois, on souhaite ajouter une méthode compile , après que la méthode link (post) 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, transcludeFn ) {
            // Compile code goes here.

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

    };  
});

Controller & Post-link

si aucune fonction de compilation n'est nécessaire, on peut passer complètement sa déclaration et fournir la fonction post-link sous la propriété 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, transcludeFn ) {
                // Post-link code goes here                 
        },          
    };  
});

Pas de contrôleur

dans l'un des exemples ci-dessus, on peut simplement supprimer la fonction controller si elle n'est pas nécessaire. Ainsi, par exemple, si seulement la fonction post-link est nécessaire, on peut utiliser:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        link: function postLink( scope, element, attributes, controller, transcludeFn ) {
                // Post-link code goes here                 
        },          
    };  
});
42
répondu Izhaki 2015-01-20 14:47:58

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

le fait que L'angle autorise la manipulation DOM signifie que le balisage d'entrée dans le processus de compilation diffère parfois de la sortie. En particulier, certains markup d'entrée peuvent être clonés quelques fois (comme avec ng-repeat ) avant d'être rendus au DOM.

la terminologie angulaire est un peu incohérente, mais il distingue encore entre deux types de majorations:

  • Source template - le balisage à cloner, si nécessaire. S'il est cloné, ce markup ne sera pas rendu au DOM.
  • Instance template - le markup réel à rendre au DOM. Si le clonage est impliqué, chaque instance sera un clone.

le balisage suivant démontre ceci::

<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 une directive ng-repeat , ce modèle source sera cloné (3 fois dans notre cas). Ces clones sont des modèles d'instance, chacun apparaîtra dans le DOM et sera lié à la portée pertinente.

30
répondu Izhaki 2014-07-07 16:14:58

compiler function

la fonction compile de chaque directive n'est appelée qu'une seule fois, lorsque des bootstraps angulaires.

officiellement, c'est l'endroit pour effectuer des manipulations (source) de gabarit qui n'impliquent pas la portée ou la liaison de données.

principalement, ceci est fait à des fins d'optimisation; considérons le markup suivant:

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

la directive <my-raw> rendra set de DOM markup. Donc nous pouvons soit:

  • permet à ng-repeat de dupliquer le modèle source ( <my-raw> ), puis de modifier le balisage de chaque modèle d'instance (en dehors de la fonction compile ).
  • modifier le modèle de source pour impliquer le balisage désiré (dans la fonction compile ), puis permettre à ng-repeat de le dupliquer.

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

:

  • manipule le markup pour qu'il serve de modèle aux instances (clones).

Ne pas

  • Joindre les gestionnaires d'événements.
  • inspecter les éléments pour enfants.
  • configurer les observations sur les attributs.
  • régler les montres sur la lunette.
23
répondu Izhaki 2015-12-03 18:28:31

Post-lien de la fonction

lorsque la fonction post-link est appelée, Toutes les étapes précédentes ont eu lieu - liaison, transclusion, etc.

c'est typiquement un endroit pour manipuler davantage le DOM Rendu.

:

  • manipule des éléments DOM (rendus, et donc instanciés).
  • Joindre les gestionnaires d'événements.
  • inspecter les éléments enfants.
  • configurer les observations sur les attributs.
  • réglez les montres sur la lunette.
19
répondu Izhaki 2015-12-03 18:23:07

fonction contrôleur

la fonction controller de chaque directive est appelée chaque fois qu'un nouvel élément connexe est instancié.

officiellement, la fonction controller est où un:

  • définit la logique (les méthodes) du contrôleur qui peut être partagée entre les contrôleurs.
  • initie les variables de champ.

encore une fois, il est important de se rappeler que si le la directive comporte un champ d'application isolé, les biens qui en héritent ne sont pas encore disponibles.

:

  • Définir la logique du contrôleur
  • Lancer la portée des variables

à Ne pas faire:

  • inspecter les éléments enfants (ils ne peuvent pas encore être rendus, liés à la portée, etc.).
19
répondu Izhaki 2015-12-03 18:36:43

Pré-lien de la fonction

la fonction pre-link de chaque directive est appelée chaque fois qu'un nouvel élément connexe est instancié.

comme nous l'avons vu précédemment dans la section ordre de compilation, les fonctions pre-link sont appelées" parent-puis-enfant", tandis que les fonctions post-link sont appelées child-then-parent .

la fonction pre-link est rarement utilisée, mais peut être utile dans des scénarios spéciaux; par exemple, lorsqu'un contrôleur enfant s'enregistre avec le contrôleur parent, mais l'enregistrement doit être parent-then-child ( ngModelController fait les choses de cette façon).

à Ne pas faire:

  • inspecter les éléments enfants (ils ne peuvent pas encore être rendus, liés à la portée, etc.).
15
répondu Izhaki 2015-12-03 18:28:56