Qual è la differenza tra la funzione di compilazione e di collegamento in angularjs


208

Qualcuno può spiegare in termini semplici?

La documentazione sembra un po 'ottusa. Non sto ottenendo l'essenza e il quadro generale di quando usare l'uno sull'altro. Un esempio in contrasto con i due sarebbe fantastico.


2
Forse una panoramica più completa delle funzioni della direttiva: direttive angolari - quando usare compilazione, controller, pre-link e post-link .
Izhaki,

Risposte:


217
  • funzione di compilazione: utilizzare per la manipolazione del modello DOM (ovvero la manipolazione di tElement = elemento modello), quindi le manipolazioni che si applicano a tutti i cloni DOM del modello associato alla direttiva.

  • funzione di collegamento: utilizzare per la registrazione di listener DOM (ad esempio $ watch espressioni nell'ambito dell'istanza) nonché per la manipolazione del DOM dell'istanza (ovvero la manipolazione di iElement = elemento dell'istanza individuale).
    Viene eseguito dopo la clonazione del modello. Ad esempio, all'interno di una <li ng-repeat ...>, la funzione di collegamento viene eseguita dopo che il modello <li> (tElement) è stato clonato (in un iElement) per quel particolare elemento <li>.
    Un $ watch () consente a una direttiva di essere informata delle modifiche alle proprietà dell'ambito dell'istanza (un ambito dell'istanza è associato a ciascuna istanza), che consente alla direttiva di rendere un valore di istanza aggiornato sul DOM - copiando il contenuto dall'ambito dell'istanza in il DOM.

Si noti che le trasformazioni DOM possono essere eseguite nella funzione di compilazione e / o nella funzione di collegamento.

La maggior parte delle direttive necessita solo di una funzione di collegamento, poiché la maggior parte delle direttive riguarda solo un'istanza di elemento DOM specifica (e il relativo ambito di istanza).

Un modo per determinare quale utilizzare: considerare che la funzione di compilazione non riceve un scopeargomento. (Sto volutamente ignorando l'argomento della funzione di collegamento transclude, che riceve un ambito escluso - questo viene usato raramente .) Quindi la funzione di compilazione non può fare tutto ciò che vorresti fare che richiede un ambito (istanza) - puoi $ guardate tutte le proprietà dell'ambito del modello / istanza, non potete manipolare il DOM usando le informazioni sull'ambito dell'istanza, non potete chiamare funzioni definite sull'ambito dell'istanza, ecc.

Tuttavia, la funzione di compilazione (come la funzione di collegamento) ha accesso agli attributi. Pertanto, se le manipolazioni del DOM non richiedono l'ambito dell'istanza, è possibile utilizzare una funzione di compilazione. Ecco un esempio di direttiva che utilizza solo una funzione di compilazione, per questi motivi. Esamina gli attributi, ma non ha bisogno di un ambito di istanza per fare il suo lavoro.

Ecco un esempio di direttiva che utilizza anche solo una funzione di compilazione. La direttiva deve solo trasformare il modello DOM, quindi è possibile utilizzare una funzione di compilazione.

Un altro modo per determinare quale utilizzare: se non si utilizza il parametro "element" nella funzione link, probabilmente non è necessaria una funzione link.

Poiché la maggior parte delle direttive ha una funzione di collegamento, non fornirò alcun esempio: dovrebbero essere molto facili da trovare.

Si noti che se è necessaria una funzione di compilazione e una funzione di collegamento (o funzioni di collegamento pre e post), la funzione di compilazione deve restituire le funzioni di collegamento perché l'attributo 'collegamento' viene ignorato se viene definito l'attributo 'compilazione'.

Guarda anche


5
Migliore spiegazione sulla compilazione vs collegamento.
Nexus23

1
Quando dici if you don't use the "element" parameter in the link function, then you probably don't need a link function."intendi" invece di "elemento"?
Jason Larke,

69

Ho battuto la testa contro il muro su questo per un paio di giorni e sento che un po 'più di spiegazione è in ordine.

Fondamentalmente, i documenti menzionano che la separazione è in gran parte un miglioramento delle prestazioni. Ribadisco che la fase di compilazione viene utilizzata principalmente quando è necessario modificare il DOM PRIMA che vengano compilati gli elementi secondari stessi.

Per i nostri scopi, sottolineerò la terminologia, che altrimenti confonde:

Il servizio di compilazione SERVICE ($ compile) è il meccanismo angolare che elabora il DOM ed esegue i vari bit di codice nelle direttive.

La FUNZIONE di compilazione è un bit di codice all'interno di una direttiva, che viene eseguita in un determinato momento dal compilatore SERVICE ($ compile).

Alcune note sulla compilazione FUNZIONE:

  1. Non puoi modificare l'elemento ROOT (quello interessato dalla tua direttiva), poiché è già in fase di compilazione dal livello esterno di DOM (il servizio di compilazione ha già scansionato le direttive su quell'elemento).

  2. Se vuoi aggiungere altre direttive agli elementi (nidificati), puoi:

    1. Devi aggiungerli durante la fase di compilazione.

    2. Devono iniettare il servizio di compilazione nella fase di collegamento e compilare manualmente gli elementi. MA, attenzione a compilare qualcosa due volte!

È anche utile vedere come funzionano l'annidamento e le chiamate esplicite a $ compilate, quindi ho creato un parco giochi per visualizzarlo su http://jsbin.com/imUPAMoV/1/edit . Fondamentalmente, registra solo i passaggi su console.log.

Dichiarerò qui i risultati di ciò che vedresti nel cestino. Per un DOM di direttive personalizzate tp e sp nidificate come segue:

<tp>
   <sp>
   </sp>
</tp>

La compilazione angolare SERVICE chiamerà:

tp compile
sp compile
tp pre-link
sp pre-link
sp post-link
tp post-link

Il codice jsbin ha anche la FUNZIONE post-link di tp che chiama esplicitamente il servizio di compilazione su una terza direttiva (su), che alla fine esegue tutti e tre i passaggi.

Ora, voglio passare attraverso un paio di scenari per mostrare come si potrebbe fare usando la compilazione e il collegamento per fare varie cose:

SCENARIO 1: Direttiva come MACRO

Volete aggiungere una direttiva (diciamo ng-show) in modo dinamico a qualcosa nel vostro modello che potete derivare da un attributo.

Supponi di avere un templateUrl che punta a:

<div><span><input type="text"></span><div>

e vuoi una direttiva personalizzata:

<my-field model="state" name="address"></my-field>

che trasforma il DOM in questo:

<div><span ng-show="state.visible.address"><input ng-model="state.fields.address" ...>

fondamentalmente, si desidera ridurre il boilerplate avendo una struttura di modello coerente che la direttiva può interpretare. In altre parole: vuoi una macro.

Questo è di grande utilità per la fase di compilazione, poiché puoi basare tutte le manipolazioni del DOM su cose che conosci solo dagli attributi. Usa semplicemente jQuery per aggiungere gli attributi:

compile: function(tele, tattr) {
   var span = jQuery(tele).find('span').first();
   span.attr('ng-show', tattr.model + ".visible." + tattr.name);
   ...
   return { 
     pre: function() { },
     post: function() {}
   };
}

La sequenza delle operazioni sarà (puoi vederla tramite la jsbin menzionata in precedenza):

  1. Il servizio di compilazione trova my-field
  2. Chiama la compilazione FUNCTION sulla direttiva, che aggiorna il DOM.
  3. Il servizio di compilazione quindi entra nel DOM risultante e COMPILES (ricorsivamente)
  4. Il servizio di compilazione quindi chiama pre-link top-down
  5. Il servizio di compilazione quindi chiama post-link BOTTOM UP, quindi la funzione di collegamento di my-field è chiamata DOPO che i nodi interni sono stati collegati.

Nell'esempio sopra, non è necessario alcun collegamento, poiché tutto il lavoro della direttiva è stato svolto nella compilazione FUNCTION.

In qualsiasi momento, il codice in una direttiva può richiedere l'esecuzione del compilatore SERVICE su elementi aggiuntivi.

Ciò significa che possiamo fare esattamente la stessa cosa in una funzione di collegamento se si inietta il servizio di compilazione:

directive('d', function($compile) {
  return {
    // REMEMBER, link is called AFTER nested elements have been compiled and linked!
    link: function(scope, iele, iattr) {
      var span = jQuery(iele).find('span').first();
      span.attr('ng-show', iattr.model + ".visible." + iattr.name);
      // CAREFUL! If span had directives on it before
      // you will cause them to be processed again:
      $compile(span)(scope);
    }
});

Se sei sicuro che gli elementi che stai passando a $ compilano SERVICE inizialmente erano privi di direttiva (ad es. Provenivano da un modello che hai definito, o li hai appena creati con angular.element ()), il risultato finale è praticamente lo stesso di prima (anche se potresti ripetere qualche lavoro). Tuttavia, se l'elemento aveva altre direttive su di esso, le hai semplicemente fatte elaborare di nuovo, il che può causare ogni tipo di comportamento irregolare (ad es. Doppia registrazione di eventi e orologi).

Pertanto, la fase di compilazione è una scelta molto migliore per il lavoro in stile macro.

SCENARIO 2: configurazione DOM tramite dati dell'ambito

Questo segue l'esempio sopra. Supponiamo che tu abbia bisogno di accedere all'ambito mentre manipoli il DOM. Bene, in tal caso, la sezione di compilazione è inutile per te, poiché accade prima che un ambito sia disponibile.

Quindi, supponiamo che tu voglia eliminare un input con convalide, ma desideri esportare le convalide da una classe ORM (DRY) sul lato server e farle applicare automaticamente e generare l'interfaccia utente sul lato client appropriata per tali convalide.

Il tuo modello potrebbe spingere:

scope.metadata = {
  validations: {
     address: [ {
       pattern: '^[0-9]',
       message: "Address must begin with a number"
     },
     { maxlength: 100,
       message: "Address too long"
     } ]
  }
};
scope.state = {
  address: '123 Fern Dr'
};

e potresti volere una direttiva:

<form name="theForm">
  <my-field model="state" metadata="metadata" name="address">
</form>

per includere automaticamente le direttive e i div corretti per mostrare i vari errori di convalida:

<form name="theForm">
  <div>
    <input ng-model="state.address" type="text">
    <div ng-show="theForm.address.$error.pattern">Address must begin with a number</input>
...

In questo caso hai sicuramente bisogno dell'accesso all'ambito (poiché è lì che sono archiviate le tue convalide) e dovrai compilare le aggiunte manualmente, facendo di nuovo attenzione a non compilare due volte le cose. (come nota a margine, dovresti impostare un nome sul tag del modulo contenente (presumo qui il Form), e potresti accedervi in ​​collegamento con iElement.parent (). controller ('form'). $ name) .

In questo caso non ha senso scrivere una funzione di compilazione. Link è davvero quello che vuoi. I passaggi sarebbero:

  1. Definire un modello completamente privo di direttive angolari.
  2. Definire una funzione di collegamento che aggiunge i vari attributi
  3. RIMUOVI qualsiasi direttiva angolare che potresti consentire sul tuo elemento di primo livello (la direttiva my-field). Sono già stati elaborati e questo è un modo per impedire che vengano elaborati due volte.
  4. Termina chiamando il servizio di compilazione sul tuo elemento di livello superiore

Così:

angular.module('app', []).
directive('my-field', function($compile) {
  return {
    link: function(scope, iele, iattr) {
      // jquery additions via attr()
      // remove ng attr from top-level iele (to avoid duplicate processing)
      $compile(iele)(scope); // will pick up additions
    }
  };
});

È possibile, ovviamente, compilare gli elementi nidificati uno per uno per evitare di doversi preoccupare dell'elaborazione duplicata delle direttive ng quando si compila nuovamente l'elemento di livello superiore.

Un'ultima nota su questo scenario: ho insinuato che avresti spinto la definizione delle convalide da un server e nel mio esempio le ho mostrate come dati già nell'ambito. Lascio che sia un esercizio per il lettore capire come si potrebbe affrontare la necessità di estrarre quei dati da un'API REST (suggerimento: compilazione differita).

SCENARIO 3: associazione dati bidirezionale via link

Naturalmente l'uso più comune del collegamento è semplicemente quello di collegare l'associazione dati bidirezionale tramite watch / apply. La maggior parte delle direttive rientrano in questa categoria, quindi è adeguatamente coperta altrove.


2
Risposta stupenda e interessante!
Nexus23

Come aggiungere elementi nidificati senza compilarli due volte?
Art 713

50

Dai documenti:

Compiler

Il compilatore è un servizio angolare che attraversa il DOM alla ricerca di attributi. Il processo di compilazione avviene in due fasi.

  1. Compila: attraversa il DOM e raccogli tutte le direttive. Il risultato è una funzione di collegamento.

  2. Link: combina le direttive con un ambito e produce una vista dal vivo. Eventuali modifiche al modello di ambito si riflettono nella vista e tutte le interazioni dell'utente con la vista si riflettono nel modello di ambito. Trasformare il modello di ambito in un'unica fonte di verità.

Alcune direttive ng-repeatclonano elementi DOM una volta per ogni elemento della raccolta. Avere una fase di compilazione e collegamento migliora le prestazioni poiché il modello clonato deve essere compilato una sola volta e quindi collegato una volta per ogni istanza del clone.

Quindi, almeno in alcuni casi, le due fasi esistono separatamente come ottimizzazione.


Da @ UmurKontacı :

Se hai intenzione di fare trasformazioni DOM, dovrebbe essere compile. Se si desidera aggiungere alcune funzionalità che sono cambiamenti di comportamento, dovrebbe essere presente link.


46
Se hai intenzione di effettuare la DOMtrasformazione, dovrebbe essere compilese vuoi aggiungere alcune funzionalità in caso di modifiche del comportamento, dovrebbe esserci link.
Umur Kontacı,

4
+1 al commento sopra; questa è la descrizione più concisa che ho trovato finora. Si abbina al tutorial che ho trovato qui .
Benny Bottema,

18

Questo è dal discorso di Misko sulle direttive. http://youtu.be/WqmeI5fZcho?t=16m23s

Pensa alla funzione del compilatore come alla cosa che funziona su un modello e alla cosa che può cambiare il modello stesso, ad esempio aggiungendo una classe o qualcosa del genere. Ma è la funzione di collegamento che svolge effettivamente il lavoro di associazione dei due perché la funzione di collegamento ha accesso all'ambito ed è la funzione di collegamento che viene eseguita una volta per ogni istanza del modello particolare. Quindi l'unico tipo di cose che puoi mettere all'interno delle funzioni di compilazione sono cose che sono comuni in tutte le istanze.


10

Poco in ritardo per la discussione. Ma, a beneficio dei futuri lettori:

Mi sono imbattuto nel seguente video che spiega Compile e Link in Angular JS in un modo fantastico:

https://www.youtube.com/watch?v=bjFqSyddCeA

Non sarebbe piacevole copiare / digitare tutto il contenuto qui. Ho preso un paio di schermate dal video, che spiegano ogni fase delle fasi di compilazione e collegamento:

Compilare e collegare in JS angolare

Compilare e collegare in JS angolare - Direttive nidificate

Il secondo screenshot è un po 'confuso. Ma, se seguiamo la numerazione dei passi, è abbastanza semplice.

Primo ciclo: "Compile" viene eseguito per primo su tutte le direttive.
Secondo ciclo: "Controller" e "Pre-Link" vengono eseguiti (uno dopo l'altro) Terzo ciclo: "Post-Link" viene eseguito in ordine inverso (a partire dal più interno)

Di seguito è riportato il codice, che dimostra quanto sopra:

var app = angular.module ('app', []);

app.controller ('msg', ['$ scope', funzione ($ scope) {

}]);

app.directive ('message', function ($ interpolate) {
    ritorno{

        compile: function (tElement, tAttributes) { 
            console.log (tAttributes.text + "-In compile ..");
            ritorno {

                pre: funzione (ambito, iElement, iAttributes, controller) {
                    console.log (iAttributes.text + "-In pre ..");
                },

                inserisci: funzione (ambito, iElement, iAttributes, controller) {
                    console.log (iAttributes.text + "-In Post ..");
                }

            }
        },

        controller: function ($ scope, $ element, $ attrs) {
            console.log ($ attrs.text + "-In controller ..");
        },

    }
});
<body ng-app="app">
<div ng-controller="msg">
    <div message text="first">
        <div message text="..second">
            <div message text="....third">

            </div>              
        </div>  
    </div>
</div>

AGGIORNARE:

La parte 2 dello stesso video è disponibile qui: https://www.youtube.com/watch?v=1M3LZ1cu7rw Il video spiega di più su come modificare DOM e gestire gli eventi durante il processo di compilazione e collegamento di Angular JS, in un semplice esempio .


Usato compilee postper modificare un DOM prima che venga modificato in templateparte da una direttiva del fornitore.
jedi,

6

Due fasi: compilazione e collegamento

Compilare:

Attraversa l'albero del DOM alla ricerca di direttive (elementi / attributi / classi / commenti). Ogni compilazione di una direttiva può modificare il suo modello o modificare il suo contenuto che non è stato ancora compilato. Una volta che una direttiva viene abbinata, restituisce una funzione di collegamento, che viene utilizzata in una fase successiva per collegare gli elementi insieme. Alla fine della fase di compilazione, abbiamo un elenco di direttive compilate e le relative funzioni di collegamento.

link:

Quando un elemento è collegato, l'albero DOM viene interrotto nel punto di diramazione dell'albero DOM e il contenuto viene sostituito dall'istanza compilata (e collegata) del modello. Il contenuto spostato originale viene scartato o, in caso di inclusione, ricollegato al modello. Con la inclusione, i due pezzi sono collegati di nuovo insieme (un po 'come una catena, con il pezzo modello nel mezzo). Quando viene chiamata la funzione di collegamento, il modello è già stato associato a un ambito e aggiunto come figlio dell'elemento. La funzione di collegamento è la tua opportunità per manipolare ulteriormente il DOM e configurare i listener di modifica.


3

Questa domanda è vecchia e vorrei fare un breve riassunto che può aiutare:

  • Compilazione chiamata una volta per tutte le istanze della direttiva
  • Compilare lo scopo principale è restituire / creare il collegamento (e possibilmente pre / post) funzione / oggetto. È inoltre possibile avviare elementi condivisi tra istanze della direttiva.
  • Secondo me, "link" è un nome confuso per questa funzione. Preferirei "pre-rendering".
  • viene chiamato il collegamento per ogni istanza della direttiva e il suo scopo è preparare il rendering della direttiva nel DOM.

1
uno in più per il nome del suggerimento: "pre-rendering"
Hailong Cao
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.