¿Cuál es la diferencia entre compilar y enlazar en angularjs?


208

¿Alguien puede explicar en términos simples?

Los documentos parecen un poco obtusos. No entiendo la esencia y el panorama general de cuándo usar uno sobre el otro. Un ejemplo que contrastara a los dos sería asombroso.


2
Quizás una descripción más completa de las funciones de las directivas: directivas angulares: cuándo utilizar compilación, controlador, preenlace y postenlace .
Izhaki

Respuestas:


217
  • función de compilación: se utiliza para la manipulación de plantillas DOM (es decir, la manipulación de tElement = elemento de plantilla), por lo tanto, las manipulaciones que se aplican a todos los clones DOM de la plantilla asociada con la directiva.

  • función de enlace: se utiliza para registrar oyentes DOM (es decir, expresiones $ watch en el ámbito de la instancia), así como la manipulación DOM de la instancia (es decir, la manipulación de iElement = elemento de instancia individual).
    Se ejecuta después de que la plantilla ha sido clonada. Por ejemplo, dentro de <li ng-repeat ...>, la función de enlace se ejecuta después de que la plantilla <li> (tElement) se haya clonado (en un iElement) para ese elemento <li> en particular.
    Un $ watch () permite que una directiva sea notificada de los cambios de propiedad del alcance de la instancia (un alcance de instancia está asociado con cada instancia), lo que permite que la directiva represente un valor de instancia actualizado al DOM, copiando el contenido del alcance de la instancia en el DOM

Tenga en cuenta que las transformaciones DOM se pueden hacer en la función de compilación y / o la función de enlace.

La mayoría de las directivas solo necesitan una función de enlace, ya que la mayoría de las directivas solo tratan con una instancia de elemento DOM específica (y su alcance de instancia).

Una forma de ayudar a determinar cuál usar: considere que la función de compilación no recibe un scopeargumento. (Estoy ignorando deliberadamente el argumento de la función de vinculación de transclude, que recibe un alcance transcluido; esto rara vez se usa). Por lo tanto, la función de compilación no puede hacer nada que desee hacer que requiera un alcance (instancia): puede No mire ninguna propiedad de alcance de modelo / instancia, no puede manipular el DOM utilizando información de alcance de instancia, no puede llamar a funciones definidas en el alcance de instancia, etc.

Sin embargo, la función de compilación (como la función de enlace) tiene acceso a los atributos. Entonces, si sus manipulaciones DOM no requieren el alcance de la instancia, puede usar una función de compilación. Aquí hay un ejemplo de una directiva que solo usa una función de compilación, por esas razones. Examina los atributos, pero no necesita un alcance de instancia para hacer su trabajo.

Aquí hay un ejemplo de una directiva que también solo usa una función de compilación. La directiva solo necesita transformar la plantilla DOM, por lo que se puede utilizar una función de compilación.

Otra forma de ayudar a determinar cuál usar: si no usa el parámetro "elemento" en la función de enlace, entonces probablemente no necesite una función de enlace.

Como la mayoría de las directivas tienen una función de enlace, no voy a proporcionar ningún ejemplo; deberían ser muy fáciles de encontrar.

Tenga en cuenta que si necesita una función de compilación y una función de enlace (o funciones de enlace anteriores y posteriores), la función de compilación debe devolver las funciones de enlace porque el atributo 'enlace' se ignora si se define el atributo 'compilar'.

Ver también


55
La mejor explicación sobre compilación vs enlace.
Nexus23

1
Cuando dices, if you don't use the "element" parameter in the link function, then you probably don't need a link function.¿te refieres a "alcance" en lugar de "elemento"?
Jason Larke

69

Me golpeé la cabeza contra la pared durante un par de días y siento que se necesita un poco más de explicación.

Básicamente, los documentos mencionan que la separación es en gran medida una mejora del rendimiento. Reitero que la fase de compilación se usa principalmente cuando necesita modificar el DOM ANTES de que se compilen los subelementos.

Para nuestros propósitos, voy a enfatizar la terminología, que de lo contrario es confusa:

El compilador SERVICE ($ compile) es el mecanismo angular que procesa el DOM y ejecuta los diversos bits de código en las directivas.

La función de compilación es un bit de código dentro de una directiva, que se ejecuta en un momento particular POR el servicio de compilación ($ compile).

Algunas notas sobre la función de compilación:

  1. No puede modificar el elemento ROOT (el que afecta su directiva), ya que ya se está compilando desde el nivel externo de DOM (el servicio de compilación ya ha escaneado las directivas sobre ese elemento).

  2. Si desea agregar otras directivas a elementos (anidados), puede:

    1. Hay que agregarlos durante la fase de compilación.

    2. Tiene que inyectar el servicio de compilación en la fase de enlace y compilar los elementos manualmente. PERO, ¡cuidado con compilar algo dos veces!

También es útil ver cómo funcionan las llamadas anidadas y explícitas a $ compile, por lo que he creado un patio de recreo para verlo en http://jsbin.com/imUPAMoV/1/edit . Básicamente, solo registra los pasos para console.log.

Expondré los resultados de lo que verías en ese contenedor aquí. Para un DOM de directivas personalizadas tp y sp anidadas de la siguiente manera:

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

El servicio de compilación angular llamará a:

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

El código jsbin también tiene la función tp post-link. Llama explícitamente al servicio de compilación en una tercera directiva (arriba), que realiza los tres pasos al final.

Ahora, quiero recorrer un par de escenarios para mostrar cómo se puede usar la compilación y el enlace para hacer varias cosas:

ESCENARIO 1: Directiva como MACRO

Desea agregar una directiva (digamos ng-show) dinámicamente a algo en su plantilla que puede derivar de un atributo.

Digamos que tiene una templateUrl que apunta a:

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

y quieres una directiva personalizada:

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

eso convierte el DOM en esto:

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

Básicamente, usted desea reducir la repetitividad al tener una estructura de modelo consistente que su directiva pueda interpretar. En otras palabras: quieres una macro.

Este es un gran uso para la fase de compilación, ya que puede basar todas las manipulaciones DOM en cosas que conoce solo de los atributos. Simplemente use jQuery para agregar los atributos:

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 secuencia de operaciones será (puede ver esto a través del jsbin mencionado anteriormente):

  1. El servicio de compilación encuentra mi campo
  2. Llama a la función de compilación en la directiva, que actualiza el DOM.
  3. El SERVICIO de compilación luego entra al DOM resultante y COMPILA (recursivamente)
  4. Luego, el SERVICIO de compilación llama al enlace previo descendente
  5. Luego, el servicio de compilación llama a ABAJO ARRIBA después del enlace, por lo que la función de enlace de mi campo se llama DESPUÉS de que se hayan vinculado los nodos interiores.

En el ejemplo anterior, no se necesita vinculación, ya que todo el trabajo de la directiva se realizó en la función de compilación.

En cualquier momento, el código de una directiva puede solicitar que el compilador SERVICE se ejecute en elementos adicionales.

Esto significa que podemos hacer exactamente lo mismo en una función de enlace si inyecta el servicio de compilación:

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

Si está seguro de que los elementos que está pasando a $ compile SERVICE originalmente no tenían directivas (por ejemplo, provenían de una plantilla que definió o simplemente los creó con angular.element ()), entonces el resultado final es más o menos lo mismo que antes (aunque puede estar repitiendo algo de trabajo). Sin embargo, si el elemento tenía otras directivas sobre él, solo hizo que se procesaran nuevamente, lo que puede causar todo tipo de comportamiento errático (por ejemplo, doble registro de eventos y relojes).

Por lo tanto, la fase de compilación es una opción mucho mejor para el trabajo de estilo macro.

ESCENARIO 2: configuración DOM a través de datos de alcance

Este se desprende del ejemplo anterior. Suponga que necesita acceso al alcance mientras manipula el DOM. Bueno, en ese caso, la sección de compilación es inútil para usted, ya que ocurre antes de que haya un alcance disponible.

Entonces, supongamos que desea aumentar una entrada con validaciones, pero desea exportar sus validaciones desde una clase ORM del lado del servidor (DRY), y hacer que se apliquen automáticamente y generen la interfaz de usuario adecuada del lado del cliente para esas validaciones.

Su modelo podría impulsar:

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

y es posible que desee una directiva:

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

para incluir automáticamente las directivas y divs adecuadas para mostrar los diversos errores de validación:

<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>
...

En este caso, definitivamente necesita acceso al alcance (ya que allí es donde se almacenan sus validaciones), y tendrá que compilar las adiciones manualmente, nuevamente teniendo cuidado de no compilar las cosas dos veces. (como nota al margen, necesitaría establecer un nombre en la etiqueta del formulario que contiene (supongo que el Formulario aquí), y podría acceder a él en enlace con iElement.parent (). controller ('form'). $ name) .

En este caso no tiene sentido escribir una función de compilación. Link es realmente lo que quieres. Los pasos serían:

  1. Defina una plantilla que esté completamente desprovista de directivas angulares.
  2. Defina una función de enlace que agregue los diversos atributos
  3. RETIRE cualquier directiva angular que pueda permitir en su elemento de nivel superior (la directiva my-field). Ya se han procesado y esta es una forma de evitar que se procesen dos veces.
  4. Termine llamando al servicio de compilación en su elemento de nivel superior

Al igual que:

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

Podría, por supuesto, compilar los elementos anidados uno por uno para evitar tener que preocuparse por el procesamiento duplicado de las directivas ng cuando vuelva a compilar el elemento de nivel superior.

Una nota final sobre este escenario: implicaba que empujaría la definición de las validaciones desde un servidor, y en mi ejemplo las he mostrado como datos que ya están en el alcance. Lo dejo como un ejercicio para que el lector descubra cómo uno podría lidiar con la necesidad de extraer esos datos de una API REST (sugerencia: compilación diferida).

ESCENARIO 3: enlace de datos bidireccional por enlace

Por supuesto, el uso más común del enlace es simplemente conectar el enlace de datos bidireccional a través de watch / apply. La mayoría de las directivas entran en esta categoría, por lo que se cubre adecuadamente en otros lugares.


2
¡Impresionante y genial respuesta!
Nexus23

¿Cómo agregar elementos anidados sin compilarlos dos veces?
Art713

50

De los documentos:

Compilador

El compilador es un servicio angular que atraviesa el DOM en busca de atributos. El proceso de compilación ocurre en dos fases.

  1. Compilar: atraviesa el DOM y recoge todas las directivas. El resultado es una función de enlace.

  2. Enlace: combine las directivas con un alcance y produzca una vista en vivo. Cualquier cambio en el modelo de alcance se refleja en la vista, y cualquier interacción del usuario con la vista se refleja en el modelo de alcance. Hacer del modelo de alcance una fuente única de verdad.

Algunas directivas ng-repeatclonan elementos DOM una vez para cada elemento de la colección. Tener una fase de compilación y enlace mejora el rendimiento ya que la plantilla clonada solo necesita compilarse una vez y luego vincularse una vez para cada instancia de clonación.

Entonces, al menos en algunos casos, las dos fases existen por separado como una optimización.


De @ UmurKontacı :

Si va a hacer transformaciones DOM, debería serlo compile. Si desea agregar algunas características que son cambios de comportamiento, debería estar en link.


46
Si va a realizar una DOMtransformación, debería ser compilesi desea agregar algunas características son cambios de comportamiento, debería estar en link.
Umur Kontacı

44
+1 al comentario anterior; Esta es la descripción más concisa que encontré hasta ahora. Coincide con el tutorial que encontré aquí .
Benny Bottema

18

Esto es de la charla de Misko sobre directivas. http://youtu.be/WqmeI5fZcho?t=16m23s

Piense en la función del compilador como lo que funciona en una plantilla y lo que se le permite cambiar la plantilla en sí, por ejemplo, agregando una clase o algo así. Pero es la función de enlace que realmente hace el trabajo de unir los dos porque la función de enlace tiene acceso al alcance y es la función de enlace que se ejecuta una vez por cada instancia de la plantilla en particular. Entonces, el único tipo de cosas que puede colocar dentro de las funciones de compilación son cosas que son comunes en todas las instancias.


10

Poco tarde para el hilo. Pero, en beneficio de los futuros lectores:

Encontré el siguiente video que explica Compile and Link en Angular JS de una manera muy genial:

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

No sería agradable copiar / escribir todo el contenido aquí. Tomé un par de capturas de pantalla del video, que explican cada etapa de las fases de compilación y enlace:

Compilar y vincular en Angular JS

Compilar y vincular en Angular JS - Directivas anidadas

La segunda captura de pantalla es un poco confusa. Pero, si seguimos la numeración de pasos, es bastante sencillo.

Primer ciclo: "Compilar" se realiza primero en todas las directivas.
Segundo ciclo: se realiza "Controlador" y "Preenlace" (uno tras otro) Tercer ciclo: "Post-Enlace" se realiza en orden inverso (comenzando desde lo más interno)

El siguiente es el código, que demuestra lo anterior:

var aplicación = angular.module ('aplicación', []);

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

}]);

app.directive ('mensaje', función ($ interpolar) {
    regreso{

        compilar: función (tElement, tAttributes) { 
            console.log (tAttributes.text + "-In compile ..");
            regreso {

                pre: function (alcance, iElement, iAttributes, controlador) {
                    console.log (iAttributes.text + "-In pre ..");
                },

                post: function (alcance, iElement, iAttributes, controlador) {
                    console.log (iAttributes.text + "-In Post ..");
                }

            }
        },

        controlador: función ($ 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>

ACTUALIZAR:

La Parte 2 del mismo video está disponible aquí: https://www.youtube.com/watch?v=1M3LZ1cu7rw El video explica más sobre cómo modificar DOM y manejar eventos durante el proceso de compilación y enlace de Angular JS, en un ejemplo simple .


Se usa compiley postpara modificar un DOM antes de que se modifique en templateparte de una directiva de proveedor.
Jedi

6

Dos fases: compilar y vincular

Compilar:

Atraviesa el árbol DOM buscando directivas (elementos / atributos / clases / comentarios). Cada compilación de una directiva puede modificar su plantilla o modificar su contenido que aún no se ha compilado. Una vez que una directiva coincide, devuelve una función de vinculación, que se utiliza en una fase posterior para vincular elementos. Al final de la fase de compilación, tenemos una lista de directivas compiladas y sus correspondientes funciones de enlace.

Enlace:

Cuando un elemento está vinculado, el árbol DOM se rompe en su punto de ramificación en el árbol DOM, y el contenido se reemplaza por la instancia compilada (y vinculada) de la plantilla. El contenido original desplazado se descarta o, en el caso de la transclusión, se vuelve a vincular a la plantilla. Con la transclusión, las dos piezas se unen nuevamente (como una cadena, con la pieza de la plantilla en el medio). Cuando se llama a la función de enlace, la plantilla ya se ha vinculado a un ámbito y se ha agregado como elemento secundario del elemento. La función de enlace es su oportunidad para manipular aún más el DOM y configurar los oyentes de cambio.


3

Esta pregunta es antigua, me gustaría hacer un breve resumen que puede ayudar:

  • Compilar llamado una vez para toda instancia directiva
  • El propósito principal de la compilación es devolver / crear la función / objeto de enlace (y posiblemente pre / post). También puede iniciar cosas que se comparten entre instancias de la directiva.
  • En mi opinión, "enlace" es un nombre confuso para esta función. Preferiría "pre-render".
  • Se llama a link para cada instancia de directiva y su propósito es preparar la representación de la directiva en el DOM.

1
uno más para el nombre de la sugerencia: "pre-render"
Hailong Cao
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.