angularjs - personalizadas - directivas angular 6
Llamar a una función de controlador desde una directiva sin alcance aislado en AngularJS (4)
El único problema que tengo es, ¿cómo puedo llamar a un método en el alcance principal si no lo paso en el alcance aislado?
Aquí hay un jsfiddle donde NO estoy usando un alcance aislado y ng-hide funciona bien, pero, por supuesto, la llamada a confirmAction () no funciona y no sé cómo hacer que funcione.
Primero, un pequeño punto: en este caso, el alcance del controlador externo no es el alcance principal del alcance de la directiva; el alcance del controlador externo es el alcance de la directiva. En otras palabras, los nombres de variables utilizados en la directiva se buscarán directamente en el alcance del controlador.
A continuación, escribir attrs.confirmAction()
no funciona porque attrs.confirmAction
para este botón,
<button ... confirm-action="doIt()">Do It</button>
es la cadena "doIt()"
, y no se puede llamar a una cadena, por ejemplo, "doIt()"()
.
Tu pregunta realmente es:
¿Cómo llamo a una función cuando tengo su nombre como una cadena?
En JavaScript, la mayoría de las funciones se llaman mediante notación de puntos, como esta:
some_obj.greet()
Pero también puedes usar la notación de matriz:
some_obj[''greet'']
Observe cómo el valor del índice es una cadena . Entonces, en su directiva simplemente puede hacer esto:
link: function (scope, element, attrs) {
element.bind(''click'', function (e) {
if (confirm(attrs.confirm)) {
var func_str = attrs.confirmAction;
var func_name = func_str.substring(0, func_str.indexOf(''('')); // Or func_str.match(/[^(]+/)[0];
func_name = func_name.trim();
console.log(func_name); // => doIt
scope[func_name]();
}
});
}
Parece que no puedo encontrar una forma de llamar a una función en el ámbito primario desde una directiva sin usar el ámbito aislado. Sé que si utilizo el alcance aislado puedo simplemente usar "&" en el aislado para acceder a la función en el alcance principal, pero usar el alcance aislado cuando no es necesario tiene consecuencias. Considere el siguiente HTML:
<button ng-hide="hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>
En este ejemplo simple, quiero mostrar un diálogo de confirmación de JavaScript y solo llamar a doIt () si hacen clic en "Aceptar" en el cuadro de diálogo de confirmación. Esto es simple usando un alcance aislado. La directiva se vería así:
.directive(''confirm'', function () {
return {
restrict: ''A'',
scope: {
confirm: ''@'',
confirmAction: ''&''
},
link: function (scope, element, attrs) {
element.bind(''click'', function (e) {
if (confirm(scope.confirm)) {
scope.confirmAction();
}
});
}
};
})
Pero el problema es que, debido a que estoy usando un alcance aislado, ng-hide en el ejemplo anterior ya no se ejecuta en el ámbito principal , sino en el ámbito aislado (ya que usar un ámbito aislado en cualquier directiva hace que todas las directivas de ese elemento use el alcance aislado). Aquí hay un jsFiddle del ejemplo anterior donde ng-hide no está funcionando. (Tenga en cuenta que en este violín, el botón debería ocultarse cuando escribe "sí" en el cuadro de entrada).
La alternativa sería NO usar un alcance aislado , que en realidad es lo que realmente quiero aquí, ya que no es necesario que se limite el alcance de esta directiva. El único problema que tengo es, ¿cómo puedo llamar a un método en el alcance principal si no lo paso en el alcance aislado ?
Aquí hay un jsfiddle donde NO estoy usando un alcance aislado y el ng-hide funciona bien, pero, por supuesto, la llamada a confirmAction () no funciona, y no sé cómo hacer que funcione.
Tenga en cuenta que la respuesta que realmente estoy buscando es cómo llamar funciones en el alcance externo SIN usar un alcance aislado. Y no estoy interesado en hacer que este diálogo de confirmación funcione de otra manera, porque el objetivo de esta pregunta es descubrir cómo hacer llamadas al alcance externo y aún poder tener otras directivas que funcionen contra el alcance principal.
Alternativamente, me interesaría saber si hay soluciones que usan un alcance aislado si otras directivas seguirán funcionando en contra del alcance principal , pero no creo que esto sea posible.
Como la directiva solo llama a una función (y no intenta establecer un valor en una propiedad), puede usar $eval lugar de $ parse (con un alcance no aislado):
scope.$apply(function() {
scope.$eval(attrs.confirmAction);
});
O mejor, simplemente use $apply , que evaluará $ eval () su argumento contra el alcance:
scope.$apply(attrs.confirmAction);
Desea hacer uso del servicio $ parse en AngularJS.
Entonces para tu ejemplo:
.directive(''confirm'', function ($parse) {
return {
restrict: ''A'',
// Child scope, not isolated
scope : true,
link: function (scope, element, attrs) {
element.bind(''click'', function (e) {
if (confirm(attrs.confirm)) {
scope.$apply(function(){
// $parse returns a getter function to be
// executed against an object
var fn = $parse(attrs.confirmAction);
// In our case, we want to execute the statement in
// confirmAction i.e. ''doIt()'' against the scope
// which this directive is bound to, because the
// scope is a child scope, not an isolated scope.
// The prototypical inheritance of scopes will mean
// that it will eventually find a ''doIt'' function
// bound against a parent''s scope.
fn(scope, {$event : e});
});
}
});
}
};
});
Hay un problema que tiene que ver con establecer una propiedad usando $ parse, pero te dejaré cruzar ese puente cuando vengas a él.
Llamar explícitamente a hideButton
en el alcance principal.
Aquí está el violín: http://jsfiddle.net/pXej2/5/
Y aquí está el HTML actualizado:
<div ng-app="myModule" ng-controller="myController">
<input ng-model="showIt"></input>
<button ng-hide="$parent.hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>
</div>