wake-up-neo.com

Angular Direktiven - wann und wie man Compile, Controller, Pre-Link und Post-Link benutzt

450
Izhaki

In welcher Reihenfolge werden die Direktivenfunktionen ausgeführt?

Für eine einzige Richtlinie

Berücksichtigen Sie basierend auf dem folgenden plunk das folgende HTML-Markup:

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

Mit folgender Richtliniendeklaration:

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

});

Die Konsolenausgabe lautet:

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

Wir können sehen, dass compile zuerst ausgeführt wird, dann controller, dann pre-link und zuletzt post-link.

Für verschachtelte Direktiven

Hinweis: Das Folgende gilt nicht für Anweisungen, die ihre untergeordneten Elemente in ihrer Verknüpfungsfunktion rendern. Einige Angular Direktiven tun dies (wie ngIf, ngRepeat oder jede Direktive mit transclude). Diese Direktiven haben von Haus aus die Funktion link, die aufgerufen wird , bevor ihre untergeordneten Direktiven compile aufgerufen werden.

Das ursprüngliche HTML-Markup besteht häufig aus verschachtelten Elementen mit jeweils eigenen Anweisungen. Wie im folgenden Markup (siehe plunk ):

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

Die Konsolenausgabe sieht folgendermaßen aus:

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

Wir können hier zwei Phasen unterscheiden - die Kompilierungsphase und die Verknüpfungsphase .

Die Kompilierungsphase

Wenn das DOM geladen ist, startet Angular die Kompilierungsphase, in der es das Markup von oben nach unten durchläuft und in allen Anweisungen compile aufruft. Grafisch könnten wir es so ausdrücken:

An image illustrating the compilation loop for children

Es ist vielleicht wichtig zu erwähnen, dass in diesem Stadium die Vorlagen, die die Kompilierungsfunktion erhält, die Quellvorlagen sind (keine Instanzvorlagen).

Die Verbindungsphase

DOM-Instanzen sind oft einfach das Ergebnis einer Quellvorlage, die im DOM gerendert wird. Sie können jedoch von ng-repeat erstellt oder im laufenden Betrieb eingeführt werden.

Immer wenn eine neue Instanz eines Elements mit einer Direktive in das DOM gerendert wird, beginnt die Verbindungsphase.

In dieser Phase ruft Angular controller, pre-link auf, iteriert untergeordnete Anweisungen und ruft post-link bei allen Anweisungen auf.

An illustration demonstrating the link phase steps

167
Izhaki

Was passiert sonst noch zwischen diesen Funktionsaufrufen?

Die verschiedenen Direktivenfunktionen werden innerhalb von zwei anderen angular Funktionen ausgeführt, die $compile heißen (wobei die compile der Direktive ausgeführt wird), und einer internen Funktion, die nodeLinkFn heißt (wobei die Direktive heißt) controller, preLink und postLink werden ausgeführt). Innerhalb der angular -Funktion passieren verschiedene Dinge, bevor und nachdem die Direktivenfunktionen aufgerufen werden. Am bemerkenswertesten ist vielleicht die Rekursion von Kindern. Die folgende vereinfachte Abbildung zeigt die wichtigsten Schritte in der Kompilierungs- und Verknüpfungsphase:

An illustration showing Angular compile and link phases

Verwenden Sie zur Veranschaulichung dieser Schritte das folgende HTML-Markup:

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

Mit folgender Anweisung:

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

Kompilieren

Die compile -API sieht folgendermaßen aus:

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

Oft wird den Parametern t vorangestellt, um zu kennzeichnen, dass die bereitgestellten Elemente und Attribute die der Quellvorlage und nicht die der Instanz sind.

Vor dem Aufruf von compile wird der übertragene Inhalt (falls vorhanden) entfernt und die Vorlage auf das Markup angewendet. Daher sieht das Element, das für die Funktion compile bereitgestellt wird, folgendermaßen aus:

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

Beachten Sie, dass der übertragene Inhalt an dieser Stelle nicht erneut eingefügt wird.

Nach dem Aufruf des .compile der Direktive durchläuft Angular alle untergeordneten Elemente, einschließlich derjenigen, die gerade von der Direktive eingeführt wurden (z. B. die Template-Elemente).

Instanzerstellung

In unserem Fall werden drei Instanzen der obigen Quellvorlage erstellt (von ng-repeat). Daher wird die folgende Sequenz dreimal ausgeführt, einmal pro Instanz.

Regler

Die controller API beinhaltet:

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

Beim Eintritt in die Verbindungsphase wird die über $compile zurückgegebene Verbindungsfunktion nun mit einem Gültigkeitsbereich versehen.

Zunächst erstellt die Verknüpfungsfunktion auf Anforderung einen untergeordneten Bereich (scope: true) oder einen isolierten Bereich (scope: {...}).

Die Steuerung wird dann ausgeführt und mit dem Gültigkeitsbereich des Instanzelements versehen.

Pre-Link

Die pre-link -API sieht folgendermaßen aus:

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

Zwischen dem Aufruf der Direktive .controller und der Funktion .preLink passiert praktisch nichts. Angular gibt weiterhin Empfehlungen zur Verwendung der einzelnen Optionen.

Nach dem Aufruf von .preLink durchläuft die Verknüpfungsfunktion jedes untergeordnete Element. Dabei wird die richtige Verknüpfungsfunktion aufgerufen und der aktuelle Bereich (der als übergeordneter Bereich für untergeordnete Elemente dient) angehängt.

Post-Link

Die post-link -API ähnelt der pre-link -Funktion:

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

Vielleicht ist es erwähnenswert, dass nach dem Aufrufen der Funktion .postLink einer Anweisung der Verknüpfungsprozess aller untergeordneten Elemente abgeschlossen ist, einschließlich aller untergeordneten Funktionen .postLink.

Dies bedeutet, dass die Kinder zum Zeitpunkt des Aufrufs von .postLink "live" sind. Das beinhaltet:

  • datenbindung
  • transklusion angewendet
  • umfang beigefügt

Die Vorlage sieht zu diesem Zeitpunkt folgendermaßen aus:

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

Wie deklariere ich die verschiedenen Funktionen?

Compile, Controller, Pre-Link und Post-Link

Wenn alle vier Funktionen verwendet werden sollen, folgt die Direktive dieser Form:

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

Beachten Sie, dass compile ein Objekt zurückgibt, das sowohl die Pre-Link- als auch die Post-Link-Funktion enthält. in Angular lingo sagen wir, dass die Kompilierungsfunktion ein Template-Funktion zurückgibt.

Kompilieren, Steuern & Nachbearbeiten

Wenn pre-link nicht erforderlich ist, kann die Kompilierungsfunktion einfach die Nachverknüpfungsfunktion anstelle eines Definitionsobjekts zurückgeben, wie folgt:

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

Manchmal möchte man eine compile -Methode hinzufügen, nachdem die (post) link -Methode definiert wurde. Dafür kann man verwenden:

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 & Nachverknüpfung

Wenn keine Kompilierungsfunktion benötigt wird, kann die Deklaration übersprungen und die Nachverknüpfungsfunktion unter der Eigenschaft link des Konfigurationsobjekts der Direktive bereitgestellt werden:

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

Kein Controller

In jedem der obigen Beispiele kann die Funktion controller einfach entfernt werden, wenn sie nicht benötigt wird. Wenn beispielsweise nur die Funktion post-link benötigt wird, kann Folgendes verwendet werden:

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

Was ist der Unterschied zwischen einer Quellvorlage und einer Instanzvorlage ?

Die Tatsache, dass Angular die DOM-Manipulation ermöglicht, bedeutet, dass die Eingabemarkierung im Kompilierungsprozess manchmal von der Ausgabe abweicht. Insbesondere können einige Eingabemarkups einige Male geklont werden (z. B. mit ng-repeat), bevor sie in das DOM gerendert werden.

Die Angular-Terminologie ist ein bisschen inkonsistent, unterscheidet jedoch immer noch zwischen zwei Arten von Markups:

  • Quellvorlage - das Markup, das bei Bedarf geklont werden soll. Wenn geklont, wird dieses Markup nicht in das DOM gerendert.
  • Instanzvorlage - das tatsächliche Markup, das in das DOM gerendert werden soll. Wenn das Klonen beteiligt ist, ist jede Instanz ein Klon.

Das folgende Markup zeigt dies:

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

Das Quell-HTML definiert

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

welches als Quellvorlage dient.

Da es sich jedoch um eine Anweisung ng-repeat handelt, wird diese Quellvorlage geklont (in unserem Fall dreimal). Diese Klone sind Instanzvorlagen, die jeweils im DOM angezeigt werden und an den entsprechenden Bereich gebunden sind.

30
Izhaki

Kompilierfunktion

Die compile -Funktion jeder Direktive wird nur einmal aufgerufen, wenn Angular bootstraps.

Offiziell ist dies der Ort, an dem (Quell-) Vorlagenmanipulationen durchgeführt werden können, bei denen weder Umfang noch Datenbindung erforderlich sind.

Dies geschieht in erster Linie zu Optimierungszwecken. Betrachten Sie das folgende Markup:

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

Die Direktive <my-raw> gibt einen bestimmten Satz von DOM-Markups wieder. Also können wir entweder:

  • Erlauben Sie ng-repeat, die Quellvorlage (<my-raw>) zu duplizieren, und ändern Sie dann das Markup jeder Instanzvorlage (außerhalb der Funktion compile).
  • Ändern Sie die Quellvorlage so, dass sie das gewünschte Markup enthält (in der Funktion compile), und erlauben Sie dann ng-repeat, es zu duplizieren.

Wenn die Sammlung raws 1000 Elemente enthält, ist die letztere Option möglicherweise schneller als die vorherige.

Machen:

  • Bearbeiten Sie das Markup so, dass es als Vorlage für Instanzen (Klone) dient.

Unterlassen Sie

  • Event-Handler anhängen.
  • Untersuchen Sie untergeordnete Elemente.
  • Richten Sie Beobachtungen zu Attributen ein.
  • Richten Sie die Uhren am Zielfernrohr ein.
23
Izhaki

Controller-Funktion

Die controller -Funktion jeder Direktive wird immer dann aufgerufen, wenn ein neues verwandtes Element instanziiert wird.

Offiziell ist die Funktion controller die folgende:

  • Definiert die Controller-Logik (Methoden), die von den Controllern gemeinsam genutzt werden können.
  • Initiiert Bereichsvariablen.

Auch hier ist zu beachten, dass, wenn die Direktive einen isolierten Bereich umfasst, alle darin enthaltenen Eigenschaften, die vom übergeordneten Bereich erben, noch nicht verfügbar sind.

Machen:

  • Steuerungslogik definieren
  • Bereichsvariablen initiieren

Unterlassen Sie:

  • Untersuchen Sie untergeordnete Elemente (sie sind möglicherweise noch nicht gerendert, an den Umfang gebunden usw.).
19
Izhaki

Post-Link-Funktion

Wenn die Funktion post-link aufgerufen wird, haben alle vorherigen Schritte stattgefunden - Bindung, Transklusion usw.

Dies ist normalerweise ein Ort, an dem das gerenderte DOM weiter bearbeitet werden kann.

Machen:

  • Manipulieren Sie DOM-Elemente (gerendert und damit instanziiert).
  • Event-Handler anhängen.
  • Untersuchen Sie untergeordnete Elemente.
  • Richten Sie Beobachtungen zu Attributen ein.
  • Richten Sie die Uhren am Zielfernrohr ein.
19
Izhaki

Pre-Link-Funktion

Die pre-link -Funktion jeder Direktive wird immer dann aufgerufen, wenn ein neues verwandtes Element instanziiert wird.

Wie bereits im Abschnitt zur Kompilierungsreihenfolge erwähnt, werden pre-link -Funktionen als Parent-Then-Child-Funktionen bezeichnet, während post-link -Funktionen als child-then-parent -Funktionen bezeichnet werden.

Die Funktion pre-link wird selten verwendet, kann jedoch in bestimmten Szenarien hilfreich sein. Wenn sich beispielsweise ein untergeordneter Controller beim übergeordneten Controller registriert, die Registrierung jedoch auf parent-then-child Weise erfolgen muss (ngModelController erledigt dies auf diese Weise).

Unterlassen Sie:

  • Untersuchen Sie untergeordnete Elemente (sie sind möglicherweise noch nicht gerendert, an den Umfang gebunden usw.).
15
Izhaki