Llamar a una función de controlador desde una directiva sin scope aislado en AngularJS

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 scope aislado puedo simplemente usar “&” en el aislado para acceder a la función en el scope principal, pero usar el scope aislado cuando no es necesario tiene consecuencias. Considere el siguiente HTML:

 

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 scope 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 scope 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 scope 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 scope aislado , que en realidad es lo que realmente quiero aquí, ya que no es necesario que se limite el scope de esta directiva. El único problema que tengo es, ¿cómo puedo llamar a un método en el scope principal si no lo paso en el scope aislado ?

Aquí hay un jsfiddle donde NO estoy usando un scope 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 scope externo SIN usar un scope aislado. Y no estoy interesado en hacer que este diálogo de confirmación funcione de otra manera, porque el objective de esta pregunta es descubrir cómo hacer llamadas al scope externo y aún poder tener otras directivas que funcionen contra el scope principal.

Alternativamente, me interesaría saber si hay soluciones que usan un scope aislado si otras directivas seguirán funcionando en contra del scope 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 en lugar de $ parse (con un scope no aislado):

 scope.$apply(function() { scope.$eval(attrs.confirmAction); }); 

O mejor, simplemente use $ apply , que evaluará $ eval () su argumento contra el scope:

 scope.$apply(attrs.confirmAction); 

Violín

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 ie '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 scope principal.

Aquí está el violín: http://jsfiddle.net/pXej2/5/

Y aquí está el HTML actualizado:

 

El único problema que tengo es, ¿cómo puedo llamar a un método en el scope principal si no lo paso en el scope aislado?

Aquí hay un jsfiddle donde NO estoy usando un scope 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 scope del controlador externo no es el scope principal del scope de la directiva; el scope del controlador externo es el scope de la directiva. En otras palabras, los nombres de variables utilizados en la directiva se buscarán directamente en el scope del controlador.

A continuación, escribir attrs.confirmAction() no funciona porque attrs.confirmAction para este botón,

  

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