454 Stimmen

Angular-Direktiven - wann und wie man compile, controller, pre-link und post-link verwendet

170voto

Izhaki Punkte 22440

In welcher Reihenfolge werden die Direktivenfunktionen ausgeführt?

Für eine einzelne Direktive

Basierend auf dem folgenden Plunk, betrachten Sie die folgende HTML-Markierung:

Mit der folgenden Direktivdeklaration:

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 wird sein:

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

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

Für verschachtelte Direktiven

Hinweis: Das folgende gilt nicht für Direktiven, die ihre Kinder in ihrer Link-Funktion rendern. Viele Angular-Direktiven tun dies (wie ngIf, ngRepeat oder jede Direktive mit transclude). Diese Direktiven haben nativ ihre link-Funktion wird vor dem Aufruf der compile-Funktion ihrer Kinder aufgerufen.

Die ursprüngliche HTML-Markierung besteht oft aus verschachtelten Elementen, von denen jedes seine eigene Direktive hat. Wie in der folgenden Markierung (siehe plunk):

Die Konsolenausgabe wird wie folgt aussehen:

// Die Kompilierungsphase
parent (compile)
..first-child (compile)
..second-child (compile)

// Die Verknüpfungsphase   
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, bei der es die Markierung von oben nach unten durchläuft und compile auf alle Direktiven aufruft. Grafisch könnten wir es so ausdrücken:

Eine Abbildung, die die Kompilierungsschleife für Kinder veranschaulicht

Es ist vielleicht wichtig zu erwähnen, dass in diesem Stadium die Templates, die die Kompilierungsfunktion erhält, die Quelltemplates sind (nicht die Instanzvorlage).

Die Verknüpfungsphase

DOM-Instanzen sind oft einfach das Ergebnis davon, dass eine Quellvorlage in das DOM gerendert wird, aber sie können von ng-repeat erstellt oder dynamisch hinzugefügt werden.

Immer wenn eine neue Instanz eines Elements mit einer Direktive im DOM gerendert wird, startet die Verknüpfungsphase.

In dieser Phase ruft Angular controller, pre-link auf, durchläuft die Kinder und ruft post-link auf alle Direktiven, wie folgt:

Eine Darstellung der Schritte der Verknüpfungsphase

93voto

Izhaki Punkte 22440

Was passiert sonst noch zwischen diesen Funktionsaufrufen?

Die verschiedenen Direktivfunktionen werden innerhalb von zwei anderen Angular-Funktionen namens $compile (wo die Direktive compile ausgeführt wird) und einer internen Funktion namens nodeLinkFn (wo die Direktive controller, preLink und postLink ausgeführt werden) ausgeführt. Verschiedene Dinge passieren innerhalb der Angular-Funktion, bevor und nachdem die Direktivfunktionen aufgerufen werden. Vielleicht am bemerkenswertesten ist die Kind-Rekursion. Die folgende vereinfachte Darstellung zeigt die wichtigsten Schritte innerhalb der Kompilierungs- und Verknüpfungsphasen:

Eine Illustration, die die Angular-Kompilierungs- und Verknüpfungsphasen zeigt

Um diese Schritte zu demonstrieren, verwenden wir das folgende HTML-Markup:

        Innerer Inhalt

Mit der folgenden Direktive:

myApp.directive( 'myElement', function() {
    return {
        restrict:   'EA',
        transclude: true,
        template:   '{{label}}'
    }
});

Kompilierung

Die compile-API sieht so aus:

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

Oft werden die Parameter mit t vorangestellt, um anzugeben, dass die bereitgestellten Elemente und Attribute die des Quelltemplates sind und nicht die der Instanz.

Vor dem Aufruf von compile wird der transkudierte Inhalt (falls vorhanden) entfernt und das Template auf die Markierung angewendet. Das Element, das der Funktion compile übergeben wird, wird also so aussehen:

        "{{label}}"

Beachten Sie, dass der transkudierte Inhalt zu diesem Zeitpunkt nicht wieder eingefügt wird.

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

Instanzerstellung

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

Controller

Die controller-API beinhaltet:

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

Beim Betreten der Verknüpfungsphase wird der Link-Funktion, die über $compile zurückgegeben wird, jetzt mit einem Scope bereitgestellt.

Zuerst erstellt die Link-Funktion einen Kindscope (scope: true) oder einen isolierten Scope (scope: {...}), wenn angefordert.

Dann wird der Controller ausgeführt und erhält den Scope des Instanzelements.

Vor-Link

Die preLink-API sieht so aus:

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

Zwischen dem Aufruf der .controller-Direktive und der .preLink-Funktion passiert praktisch nichts. Angular gibt immer noch Empfehlungen, wie jede Funktion verwendet werden sollte.

Nach dem Aufruf von .preLink durchläuft die Link-Funktion jedes Kinderelements, ruft die richtige Verknüpfungsfunktion auf und hängt den aktuellen Scope daran (der als Elternscope für Kinderelemente dient).

Nach-Link

Die postLink-API ähnelt der Funktion preLink:

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

Vielleicht ist es erwähnenswert, dass nachdem eine Direktive .postLink-Funktion aufgerufen wurde, der Verknüpfungsprozess aller seiner Kinderelemente abgeschlossen ist, einschließlich aller .postLink-Funktionen der Kinder.

Dies bedeutet, dass zum Zeitpunkt des Aufrufs von .postLink die Kinder 'aktiv' und bereit sind. Dies beinhaltet:

  • Datenbindung
  • Transklusion angewendet
  • Scope angehängt

Das Template sieht zu diesem Zeitpunkt also so aus:

        "{{label}}"

            Innerer Inhalt

45voto

Izhaki Punkte 22440

Wie deklariert man die verschiedenen Funktionen?

Kompilieren, Controller, Pre-Link & Post-Link

Wenn alle vier Funktionen verwendet werden sollen, folgt die Direktive diesem Format:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller-Code kommt hier hin.
        },
        compile: function compile( tElement, tAttributes, transcludeFn ) {
            // Kompilierungscode kommt hier hin.
            return {
                pre: function preLink( scope, element, attributes, controller, transcludeFn ) {
                    // Pre-Link-Code kommt hier hin
                },
                post: function postLink( scope, element, attributes, controller, transcludeFn ) {
                    // Post-Link-Code kommt hier hin
                }
            };
        }
    };  
});

Beachten Sie, dass compile ein Objekt zurückgibt, das sowohl die pre-link- als auch die post-link-Funktionen enthält; in Angular-Sprache sagen wir, die compile-Funktion gibt eine Template-Funktion zurück.

Kompilieren, Controller & Post-Link

Wenn pre-link nicht erforderlich ist, kann die compile-Funktion einfach die post-link-Funktion zurückgeben, anstatt ein Definition-Objekt, wie folgt:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller-Code kommt hier hin.
        },
        compile: function compile( tElement, tAttributes, transcludeFn ) {
            // Kompilierungscode kommt hier hin.
            return function postLink( scope, element, attributes, controller, transcludeFn ) {
                    // Post-Link-Code kommt hier hin                 
            };
        }
    };  
});

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

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller-Code kommt hier hin.
        },
        compile: function compile( tElement, tAttributes, transcludeFn ) {
            // Kompilierungscode kommt hier hin.

            return this.link;
        },
        link: function( scope, element, attributes, controller, transcludeFn ) {
            // Post-Link-Code kommt hier hin
        }

    };  
});

Controller & Post-Link

Wenn keine compile-Funktion benötigt wird, kann man deren Deklaration vollständig überspringen und die post-link-Funktion unter der link-Eigenschaft des Direktivkonfigurationsobjekts bereitstellen:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller-Code kommt hier hin.
        },
        link: function postLink( scope, element, attributes, controller, transcludeFn ) {
                // Post-Link-Code kommt hier hin                 
        },          
    };  
});

Kein Controller

In einem der obigen Beispiele kann man einfach die controller-Funktion entfernen, wenn diese nicht benötigt wird. Wenn zum Beispiel nur die post-link-Funktion benötigt wird, kann man Folgendes verwenden:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        link: function postLink( scope, element, attributes, controller, transcludeFn ) {
                // Post-Link-Code kommt hier hin                 
        },          
    };  
});

32voto

Izhaki Punkte 22440

Was ist der Unterschied zwischen einer Quellvorlage und einer Instanzvorlage?

Die Tatsache, dass Angular DOM-Manipulation ermöglicht, bedeutet, dass die Eingabemarkierung im Kompilierungsprozess manchmal von der Ausgabe abweicht. Insbesondere kann einige Eingabemarkierung ein paar Mal geklont werden (wie mit ng-repeat), bevor sie im DOM gerendert wird.

Die Angular-Terminologie ist etwas inkonsistent, aber sie unterscheidet dennoch zwischen zwei Arten von Markierungen:

  • Quellvorlage - die Markierung, die bei Bedarf geklont werden soll. Wenn geklont, wird diese Markierung nicht im DOM gerendert.
  • Instanzvorlage - die tatsächliche Markierung, die im DOM gerendert werden soll. Wenn Klonen involviert ist, wird jede Instanz ein Klon sein.

Die folgende Markierung demonstriert dies:

    {{i}}

Der Quell-HTML definiert

    {{i}}

was als die Quellvorlage dient.

Aber da es innerhalb eines ng-repeat Direktive eingewickelt ist, wird diese Quellvorlage geklont (in unserem Fall 3 Mal). Diese Klone sind Instanzvorlage, von denen jede im DOM erscheinen und an den relevanten Scope gebunden werden wird.

24voto

Izhaki Punkte 22440

Kompilierungsfunktion

Jede Direktive compile Funktion wird nur einmal aufgerufen, wenn Angular startet.

Offiziell ist dies der Ort, um (Quell-)Template-Manipulationen durchzuführen, die keine Datenbindung betreffen.

Hauptsächlich wird dies aus Optimierungsgründen gemacht; betrachten Sie das folgende Markup:

Die Direktive wird ein bestimmtes Set von DOM-Markup rendern. Also können wir entweder:

  • Erlauben Sie ng-repeat, das Quelltemplate () zu duplizieren, und dann das Markup jedes Instanztemplates außerhalb der compile Funktion zu modifizieren.
  • Modifizieren Sie das Quelltemplate, um das gewünschte Markup einzubeziehen (in der compile Funktion), und erlauben Sie dann ng-repeat, es zu duplizieren.

Wenn es 1000 Elemente in der raws Sammlung gibt, kann die letztere Option schneller sein als die frühere.

Machen Sie:

  • Manipulieren Sie das Markup, damit es als Vorlage für Instanzen (Klone) dient.

Machen Sie nicht:

  • Fügen Sie Event-Handler hinzu.
  • Untersuchen Sie Kind-Elemente.
  • Richten Sie Beobachtungen auf Attribute ein.
  • Richten Sie Uhren auf den Scope ein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X