angularjs - switch - Acceda al alcance del controlador desde la directiva.
ng-class (5)
He creado una directiva simple que muestra los encabezados de las columnas de clasificación para un <table>
que estoy creando.
ngGrid.directive("sortColumn", function() {
return {
restrict: "E",
replace: true,
transclude: true,
scope: {
sortby: "@",
onsort: "="
},
template: "<span><a href=''#'' ng-click=''sort()'' ng-transclude></a></span>",
link: function(scope, element, attrs) {
scope.sort = function () {
// I want to call CONTROLLER.onSort here, but how do I access the controller scope?...
scope.controllerOnSort(scope.sortby);
};
}
};
});
Aquí hay un ejemplo de algunos encabezados de tablas que se están creando:
<table id="mainGrid" ng-controller="GridCtrl>
<thead>
<tr>
<th><sort-column sortby="Name">Name</sort-column></th>
<th><sort-column sortby="DateCreated">Date Created</sort-column></th>
<th>Hi</th>
</tr>
</thead>
Entonces, cuando se hace clic en la columna de clasificación, quiero activar la función onControllerSort en mi controlador de red ... ¡pero estoy atascado! Hasta ahora, la única forma en que he podido hacer esto es para cada <sort-column>
, agregar atributos para el "onSort" y hacer referencia a aquellos en la directiva:
<sort-column onSort="controllerOnSort" sortby="Name">Name</sort-column>
Pero eso no es muy bueno, ya que SIEMPRE quiero llamar a controllerOnSort, por lo que insinuarlo para cada directiva es un poco feo. ¿Cómo puedo hacer esto dentro de la directiva sin requerir un marcado innecesario en mi HTML? Tanto la directiva como el controlador se definen dentro del mismo módulo si eso ayuda.
La propiedad & local scope permite al consumidor de una directiva pasar una función que la directiva puede invocar.
Vea los detalles here .
Aquí hay una respuesta a una pregunta similar , que muestra cómo pasar un argumento en la función de devolución de llamada desde el código de la directiva.
Crear una segunda directiva como un contenedor:
ngGrid.directive("columnwrapper", function() {
return {
restrict: "E",
scope: {
onsort: ''=''
}
};
});
Luego, solo puede hacer referencia a la función para llamar una vez en la directiva externa:
<columnwrapper onsort="controllerOnSort">
<sort-column sortby="Name">Name</sort-column>
<sort-column sortby="DateCreated">Date Created</sort-column>
</columnwrapper>
En la directiva "sortColumn" puede llamar a esa función referenciada llamando
scope.$parent.onsort();
Vea este violín para un ejemplo de trabajo: http://jsfiddle.net/wZrjQ/1/
Por supuesto, si no te importa tener dependencias codificadas, también puedes seguir con una directiva y simplemente llamar a la función en el ámbito principal (que sería el controlador en cuestión) a través de
scope.$parent.controllerOnSort():
Tengo otro violín que muestra esto: http://jsfiddle.net/wZrjQ/2
Esta solución tendría el mismo efecto (con las mismas críticas con respecto al acoplamiento duro) que la solución en la otra respuesta ( https://.com/a/19385937/2572897 ) pero es al menos algo más fácil que esa solución . De todos modos, si se unen con fuerza, no creo que haya un punto en hacer referencia al controlador, ya que probablemente esté disponible en $ scope. $ Parent todo el tiempo (pero tenga cuidado con otros elementos que configuran un alcance).
Sin embargo, me gustaría ir por la primera solución. Agrega un poco de margen de beneficio pero soluciona el problema y mantiene una separación limpia. También puede estar seguro de que $ scope. $ Parent coincide con la directiva externa si utiliza la segunda directiva como un contenedor directo.
En su directiva, requiera el ngController
y modifique la función de enlace como:
ngGrid.directive("sortColumn", function() {
return {
...
require: "ngController",
...
link: function(scope, element, attrs, ngCtrl) {
...
}
};
});
Lo que obtienes como ngCtrl
es tu controlador, GridCtrl
. Aunque no tienes su alcance; Tendrías que hacer algo en la línea de:
xxxx.controller("GridCtrl", function($scope, ...) {
// add stuff to scope as usual
$scope.xxxx = yyyy;
// Define controller public API
// NOTE: USING this NOT $scope
this.controllerOnSort = function(...) { ... };
});
Llámelo desde la función de enlace simplemente como:
ngCtrl.controllerOnSort(...);
Tenga en cuenta que este requisito obtendrá el primer controlador ngController
principal. Si hay otro controlador especificado entre GridCtrl
y la directiva, obtendrás ese.
Un violín que demuestra el principio (una directiva que accede a un controlador ng-controller
principal con métodos): http://jsfiddle.net/NAfm5/1/
La gente teme que esta solución pueda introducir un acoplamiento apretado no deseado. Si esto es realmente una preocupación, puede abordarse como:
Cree una directiva que esté al lado del controlador, llamémoslo master
:
<table id="mainGrid" ng-controller="GridCtrl" master="controllerOnSort()">
Esta directiva hace referencia al método deseado del controlador (por lo tanto: desacoplamiento).
La directiva secundaria ( sort-column
en su caso) requiere la directiva master
:
require: "^master"
Usando el servicio $parse
, se puede llamar al método especificado desde un método miembro del controlador maestro. Vea el violín actualizado que implementa este principio: http://jsfiddle.net/NAfm5/3/
Hay otra forma de hacerlo, aunque, dada mi relativa falta de experiencia, no puedo hablar de la idoneidad de tal solución. De todos modos, lo transmitiré solo con fines informativos.
En tu columna, creas un atributo de variable de alcance:
<sort-column data-sortby="sortby">Date Created</sort-column>
Luego en su controlador define la variable de alcance:
$scope.sortby = ''DateCreated'' // just a default sort here
Luego agregue su función de clasificación en el controlador:
$scope.onSort = function(val) {
$scope.sortby = val;
}
Luego, en su cable de marcado, haga clic en ng-click:
<sort-column data-sortby="sortby" ng-click="onSort(''DateCreated'')">Date Created</sort-column>
Luego, en su directiva, agregue el atributo sortby al ámbito de la directiva:
scope: {
sortby: ''='' // not sure if you need
}
Y en su función "link:" agregue $ watch:
scope.$watch(''sortby'', function () {
... your sort logic here ...
}
La belleza de este enfoque de IMO es que su directiva se ha desacoplado completamente, no necesita volver a llamar a onSort desde la directiva porque realmente no deja onSort en el controlador durante esa parte de la ruta de ejecución.
Si necesita decirle a su controlador que espere a que finalice la clasificación, podría definir un evento en el controlador:
$scope.$on("_sortFinished", function(event, message){
..do something...
});
Luego, en su directiva simplemente emita el evento y luego el proceso se realiza:
$scope.$emit(''_sortFinished'');
Hay otras formas de hacer eso, y este tipo de agregue un poco de acoplamiento apretado porque su controlador tiene que escuchar. y su directiva tiene que emitir un valor específico incluso ... pero eso puede no ser un problema para usted, ya que están estrechamente relacionados de todos modos.
Llámeme loco, pero parece más fácil simplemente obtener el controlador del elemento a través del método incorporado para eso, en lugar de manipularlo con un require
:
var mod = angular.module(''something'', []).directive(''myDir'',
function () {
return {
link: function (scope, element) {
console.log(element.controller(''myDir''));
},
controller: function () {
this.works = function () {};
},
scope: {}
}
}
);