Question Directives angulaires - quand et comment utiliser compile, controller, pre-link et post-link [fermé]


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

  • compiler
  • manette
  • pré-lien
  • post-lien

Il semble y avoir une certaine confusion quant à la fonction à utiliser. Cette question couvre:

Principes de base de la directive

Fonction nature, faire et ne pas faire

Questions connexes:


440
2017-07-07 16:13


origine


Réponses:


Dans quel ordre les fonctions de la directive sont-elles exécutées?

Pour une seule directive

Basé sur les éléments suivants plunk, considérez 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:

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

On peut voir ça compile est exécuté en premier, puis controller, puis pre-link et le dernier est 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. Un certain nombre de directives angulaires le font (comme ngIf, ngRepeat, ou toute directive avec transclude). Ces directives auront nativement leur link fonction appelée avant leurs directives enfants compile est appelé.

Le balisage HTML d'origine est souvent composé d'éléments imbriqués, chacun avec 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:

// 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 la lien phase.

La phase de compilation

Lorsque le DOM est chargé, Angular démarre la phase de compilation, où il traverse le balisage de haut en bas et appelle compile sur toutes les directives. Graphiquement, nous pourrions l'exprimer comme ça:

An image illustrating the compilation loop for children

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

La phase de liaison

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

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

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

An illustration demonstrating the link phase steps


166
2017-07-07 16:16



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

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

An illustration showing Angular compile and link phases

Pour illustrer 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

le compile L'API ressemble à ceci:

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

Souvent les paramètres sont préfixés par 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 le contenu transcrit (le cas échéant) est supprimé et le modèle est appliqué au balisage. Ainsi, l'élément fourni à la compile La fonction ressemblera à ceci:

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

Notez que le contenu transcrit n'est pas réinséré à ce stade.

Suite à l'appel à la directive .compile, Angular va parcourir 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'instance

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

Manette

le controller L'API implique:

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

En entrant dans la phase de liaison, la fonction de lien renvoyée via $compile est maintenant fourni avec une portée.

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

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

Pré-lien

le pre-link L'API ressemble à ceci:

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

Pratiquement rien ne se passe entre l'appel à la directive .controller et le .preLink fonction. Angulaire fournit toujours des recommandations quant à la façon dont chacun devrait être utilisé.

Suivant le .preLink call, la fonction link parcourra chaque élément enfant - en appelant la fonction de lien correcte et en y attachant la portée actuelle (qui sert de portée parent pour les éléments enfants).

Post-lien

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

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

Peut-être intéressant de noter qu'une fois une directive .postLink fonction est appelée, le processus de liaison de tous ses éléments enfants a terminé, y compris tous les enfants .postLink les fonctions.

Cela signifie que le temps .postLink est appelé, les enfants sont «vivants» sont prêts. Ceci comprend:

  • liaison de données
  • transclusion appliquée
  • portée attachée

Le gabarit à ce stade ressemblera donc à ceci:

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

89
2017-07-07 16:17



Comment déclarer les différentes fonctions?

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

Si l'on veut 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
                }
            };
        }
    };  
});

Notez que la compilation retourne un objet contenant à la fois les fonctions de pré-lien et post-lien; dans le jargon angulaire, nous disons que la fonction de compilation renvoie un fonction de modèle.

Compiler, Contrôler et Post-lien

Si pre-link n'est pas nécessaire, la fonction de compilation peut simplement renvoyer la fonction post-lien 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 un compile méthode, après le (post) link méthode 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
        }

    };  
});

Contrôleur et post-lien

Si aucune fonction de compilation n’est nécessaire, on peut ignorer complètement sa déclaration et fournir la fonction post-link sous le link propriété 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                 
        },          
    };  
});

Aucun contrôleur

Dans l'un des exemples ci-dessus, on peut simplement supprimer le controller Fonction si pas nécessaire. Donc, par exemple, si seulement post-link la fonction 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
2017-07-07 16:14



Quelle est la différence entre un modèle source Et un modèle d'instance?

Le fait que Angular autorise la manipulation DOM signifie que le balisage d'entrée dans le processus de compilation diffère parfois de la sortie. En particulier, certaines balises d’entrée peuvent être clonées plusieurs fois (comme avec ng-repeat) avant d'être rendu dans le DOM.

La terminologie angulaire est un peu incohérente, mais elle distingue encore deux types de balises:

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

Le balisage suivant 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 un ng-repeat directive, 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 concernée.


30
2017-07-07 16:14



Fonction de compilation

Chaque directive compile la fonction est appelée une seule fois, quand bootstraps angulaire.

Officiellement, c'est l'endroit où effectuer des manipulations de modèles (sources) qui n'impliquent ni portée ni liaison de données.

Principalement, cela est fait à des fins d'optimisation; considérez le balisage suivant:

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

le <my-raw> directive rendra un ensemble particulier de balisage DOM. Nous pouvons donc soit:

  • Permettre ng-repeat dupliquer le modèle source (<my-raw>), puis modifiez le balisage de chaque modèle d’instance (en dehors du compile fonction).
  • Modifier le modèle source pour impliquer le balisage souhaité (dans le compile fonction), puis autoriser ng-repeat pour le dupliquer.

S'il y a 1000 objets dans le raws collection, la dernière option peut être plus rapide que la précédente.

Faire:

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

Ne pas

  • Joindre des gestionnaires d'événements.
  • Inspectez les éléments enfants.
  • Mettre en place des observations sur les attributs.
  • Mettre en place des montres sur la portée.

22
2017-07-07 16:18



Fonction post-lien

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

Ceci est typiquement un endroit pour manipuler davantage le DOM rendu.

Faire:

  • Manipuler des éléments DOM (rendus et instanciés).
  • Joindre des gestionnaires d'événements.
  • Inspectez les éléments enfants.
  • Mettre en place des observations sur les attributs.
  • Mettre en place des montres sur la portée.

19
2017-07-07 16:13



Fonction de contrôleur

Chaque directive controller La fonction est appelée chaque fois qu'un nouvel élément lié est instancié.

Officiellement, le controller La fonction est là où:

  • Définit la logique du contrôleur (méthodes) pouvant être partagée entre les contrôleurs.
  • Initie les variables de portée.

Encore une fois, il est important de se rappeler que si la directive implique une étendue isolée, les propriétés qui en héritent ne sont pas encore disponibles.

Faire:

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

Ne pas:

  • Inspectez les éléments enfants (ils peuvent ne pas encore être rendus, liés à la portée, etc.).

19
2017-07-07 16:20



Fonction de pré-lien

Chaque directive pre-link La fonction est appelée chaque fois qu'un nouvel élément lié est instancié.

Comme vu précédemment dans la section de l'ordre de compilation, pre-link les fonctions sont appelées parent-then-child, alors que post-link les fonctions sont appelées child-then-parent.

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

Ne pas:

  • Inspectez les éléments enfants (ils peuvent ne pas encore être rendus, liés à la portée, etc.).

15
2017-07-07 16:21