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

Wenn Sie eine Angular-Direktive schreiben, können Sie eine der folgenden functionen verwenden, um das DOM-Verhalten, den Inhalt und das Aussehen des Elements zu beeinflussen, für das die Direktive deklariert ist:

  • kompilieren
  • Regler
  • Vorlink
  • Post-Link

Es scheint einige Verwirrung darüber zu geben, welche function man verwenden sollte. Diese Frage umfasst:

Grundlagen der Richtlinie

  • Wie deklarieren Sie die verschiedenen functionen?
  • Was ist der Unterschied zwischen einer Quellvorlage und einer Instanzvorlage ?
  • In welcher Reihenfolge werden die statementsfunktionen ausgeführt?
  • Was passiert noch zwischen diesen functionsaufrufen?

function Natur, Do’s und Dont’s

  • Kompilieren
  • Regler
  • Vorlink
  • Postlink

Verwandte Fragen:

  • Richtlinie: Link vs Compile vs Controller .
  • Unterschied zwischen den functionen “controller”, “link” und “compile” bei der Definition einer angular.js-statement .
  • Was ist der Unterschied zwischen Compilier- und Link-function in angularjs?
  • Unterschied zwischen dem Pre-Compile- und Post-Compile-Element in AngularJS-Direktiven? .
  • Angular JS Directive – Vorlage, kompilieren oder verlinken? .
  • Postlink vs Pre-Link in Angular-js-statementen .

   

In welcher Reihenfolge werden die statementsfunktionen ausgeführt?

Für eine einzelne Richtlinie

Berücksichtigen Sie anhand des folgenden Plunks das folgende HTML-Markup:

 

Mit der folgenden Direktivenerklärung:

 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 compile zuerst ausgeführt wird, dann controller , dann pre-link und zuletzt ist post-link .

Für verschachtelte statementen

Hinweis: Folgendes trifft nicht auf statementen zu, die ihre untergeordneten Elemente in ihrer Verknüpfungsfunktion rendern. Einige Angular-Direktiven tun dies (wie ngIf, ngRepeat oder irgendeine Direktive mit transclude ). Diese Direktiven werden nativ ihre link aufrufen, bevor ihre untergeordneten statementen compile .

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

  

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 Compile- Phase und die Link- Phase.

Die Kompilierphase

Wenn das DOM geladen wird, startet Angular die Kompilierphase, wo es das Markup von oben nach unten durchläuft und Aufrufe für alle Direktiven compile . Grafisch könnten wir es so express:

Ein Bild, das die Kompilationsschleife für Kinder darstellt

Es ist vielleicht wichtig zu erwähnen, dass die Templates, die die Kompilierfunktion in diesem Stadium erhält, die Quellvorlagen sind (keine Instanzschablonen).

Die Verbindungsphase

DOM-Instanzen sind oft einfach das Ergebnis einer Quellvorlage, die für das DOM gerendert wird, aber sie können durch ng-repeat oder im laufenden Betrieb eingeführt werden.

Immer wenn eine neue Instanz eines Elements mit einer Direktive für das DOM gerendert wird, beginnt die Link-Phase.

In dieser Phase ruft Angular den controller , pre-link , iteriert untergeordnete Elemente und ruft post-link für alle Direktiven auf:

Eine Illustration, die die Verbindungsphasenschritte zeigt

Was passiert noch zwischen diesen functionsaufrufen?

Die verschiedenen statementsfunktionen werden innerhalb von zwei anderen angularfunktionen ausgeführt, die $compile (wo die $compile der Direktive ausgeführt wird) und eine interne function, die nodeLinkFn heißt (wobei der controller der controller , preLink und postLink ausgeführt werden). Innerhalb der angularfunktion passieren verschiedene Dinge, bevor und nachdem die statementsfunktionen aufgerufen werden. Vielleicht am bemerkenswertesten ist die Kinderrekursion. Die folgende vereinfachte Abbildung zeigt die wichtigsten Schritte innerhalb der Kompilierungs- und Link-Phasen:

Eine Illustration, die angulare Kompilierungs- und Verbindungsphasen zeigt

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

 
Inner content

Mit der folgenden Richtlinie:

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

Kompilieren

Die compile API sieht so aus:

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

Häufig wird den Parametern ein Präfix mit t vorangestellt, um zu signalisieren, dass die bereitgestellten Elemente und Attribute denen der Quellenvorlage und nicht der der Instanz entsprechen.

Vor dem Aufruf compile Inhalte (falls vorhanden) wird entfernt und die Vorlage wird auf das Markup angewendet. Das für die compile bereitgestellte Element sieht also folgendermaßen aus:

  
"{{label}}"

Beachten Sie, dass der übertragene Inhalt zu diesem Zeitpunkt nicht erneut eingefügt wird.

Nach dem Aufruf der .compile statement der .compile wird Angular alle .compile Elemente durchlaufen, einschließlich derjenigen, die möglicherweise gerade von der Richtlinie eingeführt wurden (z. B. die Vorlagenelemente).

Instanzerstellung

In unserem Fall werden drei Instanzen der obigen Quellenvorlage erstellt (durch ng-repeat ). Somit wird die folgende Sequenz dreimal pro Instanz ausgeführt.

Regler

Die controller API umfasst Folgendes:

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

In der Link-Phase wird nun die über $compile Link-function mit einem Bereich versehen.

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

Der Controller wird dann ausgeführt und mit dem Bereich des Instanzelements versehen.

Vorlink

Die pre-link API sieht folgendermaßen aus:

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

Praktisch passiert nichts zwischen dem Aufruf des .controller der statement und der function .preLink . Angular gibt immer noch Empfehlungen, wie jeder verwendet werden sollte.

Nach dem Aufruf .preLink die Verknüpfungsfunktion jedes .preLink Element, ruft die richtige Verknüpfungsfunktion auf und fügt den aktuellen Bereich an (der als übergeordneter Bereich für untergeordnete Elemente dient).

Postlink

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

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

Vielleicht ist es bemerkenswert, dass sobald die function .postLink einer statement .postLink wird, der Link-process aller seiner .postLink Elemente abgeschlossen ist, einschließlich aller .postLink functionen der Kinder.

Das bedeutet, dass zu dem Zeitpunkt, an dem .postLink aufgerufen wird, die Kinder “live” bereit sind. Das beinhaltet:

  • Datenbindung
  • Transklusion angewendet
  • scope beigefügt

Die Vorlage in diesem Stadium sieht also so aus:

  
"{{label}}"
Inner content

Wie deklarieren Sie die verschiedenen functionen?

Kompilieren, Controller, Pre-Link & Post-Link

Wenn man alle vier functionen verwenden möchte, 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 die Kompilierung ein Objekt zurückgibt, das sowohl die Pre-Link- als auch die Post-Link-functionen enthält. Im Angular-Jargon sagen wir, dass die Kompilierfunktion eine Template-function zurückgibt.

Kompilieren, Controller & Post-Link

Wenn pre-link nicht erforderlich ist, kann die Compilerfunktion einfach die Post-Link-function anstelle eines Definitionsobjekts wie folgt zurückgeben:

 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 hinzufügen, nachdem die (Post-) link Methode definiert wurde. Dazu 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 & Post-Link

Wenn keine Kompilierfunktion benötigt wird, kann die Deklaration komplett übersprungen werden und die Post-Link-function 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 man die controller function einfach entfernen, wenn sie nicht benötigt wird. Wenn zum Beispiel nur die post-link function benötigt wird, kann man verwenden:

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

Was ist der Unterschied zwischen einer Quellvorlage und einer Instanzvorlage ?

Die Tatsache, dass Angular eine DOM-Manipulation erlaubt, bedeutet, dass die Eingabemarkierung in den Kompilierungsprozess manchmal von der Ausgabe abweicht. Insbesondere können einige Eingabemarke einige Male geklont werden (wie bei ng-repeat ), bevor sie in das DOM gerendert werden.

Angular Terminologie ist ein bisschen inkonsistent, aber es unterscheidet immer noch zwischen zwei Arten von Markups:

  • Quellvorlage – das zu klonende Markup, falls erforderlich. Wenn es geklont wird, wird dieses Markup nicht für das DOM gerendert.
  • Instanzvorlage – das tatsächliche Markup, das für das DOM gerendert werden soll. Wenn das Klonen beteiligt ist, wird jede Instanz ein Klon sein.

Das folgende Markup demonstriert dies:

 
{{i}}

Die Quelle HTML definiert

  {{i}} 

welches als Quellenvorlage dient.

Da es jedoch in eine ng-repeat Direktive eingeschlossen ist, wird diese Quellvorlage geklont (in unserem Fall 3 Mal). Diese Klone sind Instanzvorlagen, die jeweils im DOM erscheinen und an den relevanten Bereich gebunden sind.

Kompilierfunktion

Die compile jeder Direktive wird nur einmal aufgerufen, wenn Angular Bootstraps verwendet werden.

Offiziell ist dies der Ort, an dem (Quell-) Template-Manipulationen durchgeführt werden, die keinen scope oder Datenbindung beinhalten.

Dies geschieht primär zu Optimierungszwecken; Betrachten Sie das folgende Markup:

    

Die -Direktive wird einen bestimmten Satz von DOM-Markup rendern. Also können wir entweder:

  • Lassen Sie ng-repeat die Quellvorlage duplizieren ( ) und ändern Sie dann das Markup jeder Instanzvorlage (außerhalb der compile ).
  • Ändern Sie die Quellvorlage so, dass sie das gewünschte Markup enthält (in der compile ), und lassen Sie anschließend ng-repeat zu, dass sie dupliziert wird.

Wenn in der raws Sammlung 1000 Elemente vorhanden sind, ist die letztere Option möglicherweise schneller als die vorherige.

Machen:

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

Unterlassen Sie

  • Hängen Sie Ereignishandler an.
  • Untergeordnete Elemente prüfen.
  • Richten Sie Beobachtungen zu Attributen ein.
  • Richten Sie am Scope Uhren ein.

Post-Link-function

Wenn die post-link function aufgerufen wird, haben alle vorherigen Schritte stattgefunden – Bindung, Transclusion usw.

Dies ist normalerweise ein Platz, um das gerenderte DOM weiter zu manipulieren.

Machen:

  • Bearbeiten Sie DOM-Elemente (gerendert und damit instanziiert).
  • Hängen Sie Ereignishandler an.
  • Untergeordnete Elemente prüfen.
  • Richten Sie Beobachtungen zu Attributen ein.
  • Richten Sie am Scope Uhren ein.

Controller-function

Die controller jeder statement wird aufgerufen, wenn ein neues verwandtes Element instanziiert wird.

Offiziell ist die controller function, wo man:

  • Definiert die Steuerungslogik (Methoden), die von den Steuerungen gemeinsam genutzt werden können.
  • Initiiert Bereichsvariablen.

Auch hier ist es wichtig, sich daran zu erinnern, dass, wenn die Richtlinie einen isolierten Gültigkeitsbereich umfasst, noch keine Eigenschaften vorhanden sind, die vom übergeordneten Gültigkeitsbereich erben.

Machen:

  • Definieren Sie die Steuerungslogik
  • Initiiere Bereichsvariablen

Unterlassen Sie:

  • Untergeordnete Elemente überprüfen (sie werden möglicherweise noch nicht gerendert, sind an den Bereich gebunden usw.).

Pre-Link-function

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

Wie zuvor im Abschnitt zur Kompilierungsreihenfolge zu sehen ist, werden pre-link functionen als Parent-Then-Child bezeichnet, während post-link functionen child-then-parent .

Die pre-link function wird selten verwendet, kann aber in speziellen Szenarien nützlich sein. Wenn sich ein untergeordneter Controller zum Beispiel beim übergeordneten Controller ngModelController muss die Registrierung in einer parent-then-child ngModelController Form erfolgen ( ngModelController führt dies auf diese Weise aus).

Unterlassen Sie:

  • Untergeordnete Elemente überprüfen (sie werden möglicherweise noch nicht gerendert, sind an den Bereich gebunden usw.).