Enlace vs compilación vs controlador


529

Cuando crea una directiva, puede poner código en el compilador, la función de enlace o el controlador.

En los documentos, explican que:

  • La función de compilación y enlace se utiliza en diferentes fases del ciclo angular.
  • los controladores se comparten entre directivas

Sin embargo, para mí no está claro, qué tipo de código debería ir a dónde.

Por ejemplo: ¿Puedo crear funciones en compilación y adjuntarlas al alcance en el enlace o solo adjuntar funciones al alcance en el controlador?

¿Cómo se comparten los controladores entre las directivas, si cada directiva puede tener su propio controlador? ¿Los controladores realmente se comparten o son solo las propiedades del alcance?



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

1
Escribí una publicación con un diagrama del ciclo de vida de la directiva (fase de creación). Quizás ayude a alguien: filimanjaro.com/2014/…
promedio Joe

Respuestas:


470

Compilar:

Esta es la fase en la que Angular realmente compila su directiva. Esta función de compilación se llama solo una vez para cada referencia a la directiva dada. Por ejemplo, supongamos que está utilizando la directiva ng-repeat. ng-repeat tendrá que buscar el elemento al que está adjunto, extraer el fragmento html al que está adjunto y crear una función de plantilla.

Si ha utilizado HandleBars, subrayar plantillas o equivalentes, es como compilar sus plantillas para extraer una función de plantilla. Para esta función de plantilla, pasa datos y el valor de retorno de esa función es el html con los datos en los lugares correctos.

La fase de compilación es ese paso en Angular que devuelve la función de plantilla. Esta función de plantilla en angular se llama función de enlace.

Fase de enlace:

La fase de vinculación es donde adjunta los datos ($ scope) a la función de vinculación y debe devolverle el html vinculado. Dado que la directiva también especifica dónde va este html o qué cambia, ya es bueno ir. Esta es la función en la que desea realizar cambios en el html vinculado, es decir, el html que ya tiene los datos adjuntos. En angular, si escribe código en la función de enlace, generalmente es la función posterior al enlace (por defecto). Es una especie de devolución de llamada que se llama después de que la función de vinculación ha vinculado los datos con la plantilla.

Controlador :

El controlador es un lugar donde pones alguna lógica específica de directiva. Esta lógica también puede entrar en la función de vinculación, pero luego tendrías que poner esa lógica en el alcance para que sea "compartible". El problema con eso es que estarías corrompiendo el alcance con tus directivas, lo cual no es realmente algo que se espera. Entonces, ¿cuál es la alternativa si dos Directivas quieren hablar entre ellas / cooperar entre ellas? Por supuesto, podría poner toda esa lógica en un servicio y luego hacer que ambas directivas dependan de ese servicio, pero eso solo trae una dependencia más. La alternativa es proporcionar un controlador para este alcance (¿generalmente aislar el alcance?) Y luego este controlador se inyecta en otra directiva cuando esa directiva "requiere" la otra.


67
Para aclarar: compilar compila la plantilla que se utilizará en toda la página. Linker está vinculado a cada instancia. ¿Derecha? El controlador luego funciona entre instancias.
Zlatko

44
@CMCDragonkai para cada controllerfunción directiva se ejecuta después de la compilación, pero antes pre-link en una rama de árbol DOM local. También controllery pre-linkfunciones se ejecutan atraviesa la rama DOM local en una de arriba hacia abajo de forma. Después de eso post-linkse ejecuta de manera ascendente .
Artem Platonov

99
Es solo un desastre si no lo entiendes. Hay una razón para que haga lo que hace.
Demisx

3
Esta es la respuesta técnica correcta, sin embargo, todavía me quedan preguntas sobre cuándo debo usar la función de enlace.
Nicholas Marshall

2
¿Lo usaremos en controllerlugar de en linktodas partes? ¿De modo que no necesito cambiar el código en el futuro si es necesario compartir el método o introducir alguna lógica? ¿Hay algún inconveniente en usar controllertodo el tiempo en lugar del enlace?
JPS

99

Quería agregar también lo que el libro O'Reily AngularJS del Equipo de Google tiene que decir:

Controlador: cree un controlador que publique una API para comunicarse entre directivas. Un buen ejemplo es Directiva a la Comunicación de la Directiva

Enlace: modifique mediante programación las instancias de elementos DOM resultantes, agregue escuchas de eventos y configure el enlace de datos.

Compilar: modifique mediante programación la plantilla DOM para las características en las copias de una directiva, como cuando se usa en ng-repeat. Su función de compilación también puede devolver funciones de enlace para modificar las instancias de elementos resultantes.


Su enlace thinkster.io no se puede ver sin pagar. No es mi enlace, pero quizás esto sea más adecuado: toddmotto.com/directive-to-directive-communication-with-require
R. van Twisk

51

A le directivepermite ampliar el vocabulario HTML de manera declarativa para crear componentes web. El ng-appatributo es una directiva, también lo es ng-controllery todos los ng- prefixed attributes. Las directivas pueden ser attributes, tagso incluso class names,comments .

Cómo nacen las directivas ( compilationy instantiation)

Compilar: Utilizaremos la compilefunción tanto para manipulateel DOM antes de que se procese como para devolver una linkfunción (que se encargará de la vinculación por nosotros). Este también es el lugar para colocar cualquier método que deba compartirse con todos losinstances esta directiva.

enlace: Utilizaremos la linkfunción para registrar a todos los oyentes en un elemento DOM específico (que se clona desde la plantilla) y configurar nuestros enlaces a la página.

Si se configuran en la compile()función, solo se habrían configurado una vez (que a menudo es lo que desea). Si se establece en la link()función, se establecerían cada vez que el elemento HTML se vincule a los datos del objeto.

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

app.directive("simple", function(){
   return {
     restrict: "EA",
     transclude:true,
     template:"<div>{{label}}<div ng-transclude></div></div>",        
     compile: function(element, attributes){  
     return {
             pre: function(scope, element, attributes, controller, transcludeFn){

             },
             post: function(scope, element, attributes, controller, transcludeFn){

             }
         }
     },
     controller: function($scope){

     }
   };
});

CompileLa función devuelve la función prey postenlace. En la función de preenlace tenemos la plantilla de instancia y también el alcance de controller, pero la plantilla no está vinculada al alcance y aún no tiene contenido translúcido.

PostLa función de enlace es donde el enlace de publicación es la última función que se ejecuta. Ahora el transclusionestá completo the template is linked to a scope, y el view will update with data bound values after the next digest cycle. La linkopción es solo un atajo para configurar una post-linkfunción.

controlador: el controlador de la directiva se puede pasar a otra fase de vinculación / compilación de la directiva. Se puede inyectar en otros directorios como un medio para usar en la comunicación inter-directiva.

Debe especificar el nombre de la directiva que se requiere: debe estar vinculada al mismo elemento o su elemento primario. El nombre puede tener como prefijo:

?  Will not raise any error if a mentioned directive does not exist.
^  Will look for the directive on parent elements, if not available on the same element.

Use corchetes [‘directive1′, ‘directive2′, ‘directive3′]para requerir múltiples directivas de controlador.

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

app.controller('MainCtrl', function($scope, $element) {
});

app.directive('parentDirective', function() {
  return {
    restrict: 'E',
    template: '<child-directive></child-directive>',
    controller: function($scope, $element){
      this.variable = "Hi Vinothbabu"
    }
  }
});

app.directive('childDirective', function() {
  return {
    restrict:  'E',
    template: '<h1>I am child</h1>',
    replace: true,
    require: '^parentDirective',
    link: function($scope, $element, attr, parentDirectCtrl){
      //you now have access to parentDirectCtrl.variable
    }
  }
});

1
usted mencionó que mostró cómo colocar parentDirectiveCtrl en el controlador del niño ... en este ejemplo, el niño no tiene un controlador, sino más bien la función de enlace ... Actualmente no estoy atascado en este problema, por lo que podría no ser tan importante, pero una pregunta curiosa.
alockwood05

13

Además, una buena razón para usar una función de controlador vs.enlace (ya que ambos tienen acceso al alcance, elemento y atributos) es porque puede transferir cualquier servicio o dependencia disponible a un controlador (y en cualquier orden), mientras que no puedes hacer eso con la función de enlace. Observe las diferentes firmas:

controller: function($scope, $exceptionHandler, $attr, $element, $parse, $myOtherService, someCrazyDependency) {...

vs.

link: function(scope, element, attrs) {... //no services allowed

2
Por favor, deje un comentario para explicar su punto cuando rechace una respuesta. Gracias
svassr

53
Yo no era el downvoter, pero esto no es estrictamente correcto porque todavía puede inyectar cualquier dependencia requerida en la propia Directiva, por ejemplo: module.directive('myDirective', function($window) { etc.... Esto se puede acceder desde dentro de la función de enlace.
Mike Chamberlain

1
esto parece ser directamente incorrecto, ya que puede inyectar servicios en la función de enlace
Code Whisperer

1
@JoshRibakoff El resultado final es el mismo, tiene acceso al servicio en la función de enlace. No importa si se declara en los argumentos de la función o no. En este sentido, Mike Chamberlain tiene razón
Connor Wyatt, el

1
@ cwyatt1 Estaba corrigiendo el lenguaje, el plnkr no muestra la inyección en una función link () porque esa no es una característica que Angular tiene. Puede pensar que estoy siendo pedante, pero el comentario de los metamatos ya describe numerosas diferencias importantes entre lo que hace ese plunkr y lo que hace la inyección a un controlador. El OP pregunta cuáles son las diferencias, y hay diferencias.
Josh Ribakoff

10

Esta es una buena muestra para comprender las fases de la directiva http://codepen.io/anon/pen/oXMdBQ?editors=101

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

app.directive('slngStylePrelink', function() {
    return {
        scope: {
            drctvName: '@'
        },
        controller: function($scope) {
            console.log('controller for ', $scope.drctvName);
        },
        compile: function(element, attr) {
            console.log("compile for ", attr.name)
            return {
                post: function($scope, element, attr) {
                    console.log('post link for ', attr.name)
                },
                pre: function($scope, element, attr) {
                    $scope.element = element;
                    console.log('pre link for ', attr.name)
                        // from angular.js 1.4.1
                    function ngStyleWatchAction(newStyles, oldStyles) {
                        if (oldStyles && (newStyles !== oldStyles)) {
                            forEach(oldStyles, function(val, style) {
                                element.css(style, '');
                            });
                        }
                        if (newStyles) element.css(newStyles);
                    }

                    $scope.$watch(attr.slngStylePrelink, ngStyleWatchAction, true);

                    // Run immediately, because the watcher's first run is async
                    ngStyleWatchAction($scope.$eval(attr.slngStylePrelink));
                }
            };
        }
    };
});

html

<body ng-app="myapp">
    <div slng-style-prelink="{height:'500px'}" drctv-name='parent' style="border:1px solid" name="parent">
        <div slng-style-prelink="{height:'50%'}" drctv-name='child' style="border:1px solid red" name='child'>
        </div>
    </div>
</body>

44
Podría explicar por qué este código de ejemplo ayudaría a entender la diferencia entre link, compiley controller?
cel sharp

¿sabe cómo requirese puede inyectar una directiva d en el controlador de una directiva dependiente?
alockwood05

Ejemplo de código: Error no detectado: [$ inyector: modulador] No se pudo crear una instancia de myapp del módulo debido a: Error: [$ inyector: unpr] Proveedor desconocido: slngStylePrelinkProvider
rofrol

7
  • compilar : se usa cuando necesitamos modificar la plantilla de la directiva, como agregar una nueva expresión, agregar otra directiva dentro de esta directiva
  • controlador : se usa cuando necesitamos compartir / reutilizar datos de $ scope
  • link : es una función que se usa cuando necesitamos adjuntar un controlador de eventos o manipular DOM.
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.