Enlace vs compilación vs controlador
Cuando creas una directiva, puedes poner código en el compilador, la función de enlace o el controlador.
En los documentos, explican que:
- Las funciones de compilación y enlace se utilizan 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 y 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 directivas, si cada directiva puede tener su propio controlador? ¿Los controladores realmente se comparten o son solo las propiedades del alcance?
Compilar:
Esta es la fase en la que Angular realmente compila su directiva. Esta función de compilación se llama solo una vez por cada referencia a la directiva dada. Por ejemplo, digamos 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, plantillas de subrayado o equivalentes, es como compilar sus plantillas para extraer una función de plantilla. A esta función de plantilla le 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 vinculación:
La fase de vinculación es donde adjunta los datos ( $scope ) a la función de vinculación y debería devolverle el html vinculado. Dado que la directiva también especifica dónde va este html o qué cambia, ya está listo. Esta es la función donde 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 (de forma predeterminada). Es una especie de devolución de llamada que se llama después de que la función de vinculación haya vinculado los datos con la plantilla.
Controlador :
El controlador es un lugar donde se coloca una lógica específica de directiva. Esta lógica también puede incluirse en la función de vinculación, pero luego tendría que poner esa lógica en el alcance para que sea "compartible". El problema con esto es que entonces estaría corrompiendo el alcance con sus directivas, lo cual en realidad no es algo que se espera. Entonces, ¿cuál es la alternativa si dos Directivas quieren dialogar o cooperar entre sí? Por supuesto, se puede poner toda esa lógica en un servicio y luego hacer que ambas directivas dependan de ese servicio, pero eso sólo genera 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. Consulte las pestañas y paneles en la primera página de angularjs.org para ver un ejemplo.
Quería agregar también lo que dice el libro O'Reily AngularJS del equipo de Google:
Controlador: cree un controlador que publique una API para comunicarse entre directivas. Un buen ejemplo es la comunicación de directiva a directiva.
Enlace: modifique mediante programación las instancias de elementos DOM resultantes, agregue detectores de eventos y configure el enlace de datos.
Compilar: modifica mediante programación la plantilla DOM para funciones en 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.
A directive
le permite ampliar el vocabulario HTML de forma declarativa para crear componentes web. El ng-app
atributo es una directiva, al igual que ng-controller
todos los ng- prefixed attributes
. Las directivas pueden ser attributes
, tags
o incluso class
names
,.comments
Cómo nacen las directivas ( compilation
y instantiation
)
Compilar: usaremos la compile
función tanto para manipulate
el DOM antes de procesarla como para devolver una link
función (que se encargará del enlace por nosotros). Este también es el lugar para colocar cualquier método que deba compartirse con todos los participantes instances
de esta directiva.
enlace: usaremos la link
función para registrar todos los oyentes en un elemento DOM específico (que se clona de la plantilla) y configuraremos nuestros enlaces a la página.
Si se configuraran en la compile()
función, solo se habrían configurado una vez (que suele ser lo que desea). Si se configuran en la link()
función, se configurarán cada vez que el elemento HTML esté vinculado a datos en el
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){
}
};
});
Compile
La función devuelve la función de enlace pre
y post
. En la función de enlace previo tenemos la plantilla de instancia y también el alcance de controller
, pero aún así la plantilla no está vinculada al alcance y aún no tiene contenido transcluido.
Post
La función de enlace es donde el enlace de publicación es la última función que se ejecuta. Ahora el transclusion
está completo, the template is linked to a scope
y el view will update with data bound values after the next digest cycle
. La link
opción es sólo un atajo para configurar una post-link
función.
controlador: el controlador de directivas se puede pasar a otra fase de vinculación/compilación de directivas. Se puede inyectar en otras directivas como medio para utilizar en la comunicación entre directivas.
Debe especificar el nombre de la directiva que se requerirá; debe estar vinculada al mismo elemento o a su padre. El nombre puede tener el 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.
Utilice corchetes [‘directive1′, ‘directive2′, ‘directive3′]
para requerir un controlador de directivas múltiples.
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
}
}
});
Además, una buena razón para usar una función de controlador versus función de enlace (ya que ambos tienen acceso al alcance, elemento y atributos) es porque puede pasar 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
este es un buen ejemplo 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>