95 votes

Appeler une fonction de contrôleur à partir d'une directive sans portée isolée en AngularJS

Je n'arrive pas à trouver un moyen d'appeler une fonction sur la portée parent à partir d'une directive sans utiliser la portée isolée. Je sais que si j'utilise la portée isolée, je peux simplement utiliser "&" dans la portée isolée pour accéder à la fonction de la portée parente, mais l'utilisation de la portée isolée lorsqu'elle n'est pas nécessaire a des conséquences. Considérons le HTML suivant :

<button ng-hide="hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>

Dans cet exemple simple, je veux afficher une boîte de dialogue de confirmation JavaScript et n'appeler doIt() que si l'utilisateur clique sur "OK" dans la boîte de dialogue de confirmation. Ceci est simple en utilisant une portée isolée. La directive ressemblerait à ceci :

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

Mais le problème est que, parce que j'utilise une portée isolée, ng-hide dans l'exemple ci-dessus ne s'exécute plus par rapport à la portée parent. mais plutôt dans la portée isolée (puisque l'utilisation d'une portée isolée sur une directive entraîne l'utilisation de la portée isolée par toutes les directives de cet élément). Voici un jsFiddle de l'exemple ci-dessus où le ng-hide ne fonctionne pas. (Notez que dans ce bidule, le bouton doit se cacher lorsque vous tapez "oui" dans la zone de saisie).

L'alternative serait de NE PAS utiliser une lunette isolée Ce qui est en fait ce que je veux ici, car il n'y a pas besoin d'isoler la portée de cette directive. Le seul problème que j'ai est le suivant, comment appeler une méthode dans la portée parent si je ne la passe pas dans une portée isolée ? ?

Voici un jsfiddle où je n'utilise PAS de portée isolée et le ng-hide fonctionne bien, mais, bien sûr, l'appel à confirmAction() ne fonctionne pas, et je ne sais pas comment le faire fonctionner.

Veuillez noter que la réponse que je cherche vraiment est de savoir comment appeler des fonctions sur la portée externe SANS utiliser une portée isolée. Et je ne suis pas intéressé par le fait de faire fonctionner cette boîte de dialogue de confirmation d'une autre manière, parce que le but de cette question est de comprendre comment faire des appels à l'étendue externe et être toujours capable de faire fonctionner d'autres directives sur l'étendue parent.

Sinon, je serais intéressé de connaître les solutions qui utilisent une portée isolée si les autres directives fonctionnent toujours par rapport à la portée parente. mais je ne pense pas que ce soit possible.

116voto

Mark Rajcok Points 85912

Puisque la directive ne fait qu'appeler une fonction (et n'essaie pas de définir une valeur sur une propriété), vous pouvez utiliser la commande $eval au lieu de $parse (avec une portée non isolée) :

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

Ou mieux, utilisez simplement Appliquer qui va $eval()uer son argument par rapport à la portée :

scope.$apply(attrs.confirmAction);

Violon

17voto

Clark Pan Points 2849

Vous voulez utiliser le service $parse dans AngularJS.

Donc, pour votre exemple :

.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});
                    });
                }
            });
        }
    };
});

Il y a un problème avec la définition d'une propriété à l'aide de $parse, mais je vous laisserai traverser ce pont quand vous y arriverez.

12voto

Jason Points 8799

Appeler explicitement hideButton sur l'étendue parent.

Voici le violon : http://jsfiddle.net/pXej2/5/

Et voici le HTML mis à jour :

<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>

1voto

7stud Points 7467

Le seul problème que j'ai est de savoir comment appeler une méthode sur l'étendue parente si je ne la passe pas sur la portée isolée ?

Voici un jsfiddle où je n'utilise PAS la portée isolée et le ng-hide. fonctionne bien, mais, bien entendu, l'appel à confirmAction() ne fonctionne pas ne fonctionne pas et je ne sais pas comment le faire fonctionner.

Tout d'abord, un petit point : dans ce cas, la portée du contrôleur externe n'est pas la portée parent de la portée de la directive ; la portée du contrôleur externe est la portée de la directive. En d'autres termes, les noms de variables utilisés dans la directive seront recherchés directement dans la portée du contrôleur.

Ensuite, l'écriture attrs.confirmAction() ne fonctionne pas parce que attrs.confirmAction pour ce bouton,

<button ... confirm-action="doIt()">Do It</button>

est la chaîne de caractères "doIt()" et vous ne pouvez pas appeler une chaîne de caractères, par ex. "doIt()"() .

Votre question est vraiment :

Comment appeler une fonction dont le nom est une chaîne de caractères ?

En JavaScript, on appelle généralement les fonctions en utilisant la notation par points, comme ceci :

some_obj.greet()

Mais vous pouvez également utiliser la notation des tableaux :

some_obj['greet']

Notez comment la valeur de l'index est une chaîne . Donc, dans votre directive, vous pouvez simplement faire ceci :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X