angularJS: Comment appeler la fonction de portée enfant dans la portée parent

95

Comment appeler une méthode définie dans la portée enfant à partir de sa portée parent?

function ParentCntl() {
    // I want to call the $scope.get here
}

function ChildCntl($scope) {
    $scope.get = function() {
        return "LOL";    
    }
}

http://jsfiddle.net/wUPdW/

9 bleu
la source
2
Le meilleur pari, définissez un service, injectez ce service dans les deux contrôleurs. Ou utilisez rootscope.
tymeJV du

Réponses:

145

Vous pouvez utiliser $broadcastdu parent à un enfant:

function ParentCntl($scope) {

    $scope.msg = "";
    $scope.get = function(){
        $scope.$broadcast ('someEvent');
        return  $scope.msg;        
    }
}

function ChildCntl($scope) {               
    $scope.$on('someEvent', function(e) {  
        $scope.$parent.msg = $scope.get();            
    });

    $scope.get = function(){
        return "LOL";    
    }
}

Violon de travail: http://jsfiddle.net/wUPdW/2/

MISE À JOUR : Il existe une autre version, moins couplée et plus testable:

function ParentCntl($scope) {
    $scope.msg = "";
    $scope.get = function(){
        $scope.$broadcast ('someEvent');
        return  $scope.msg;        
    }

    $scope.$on('pingBack', function(e,data) {  
        $scope.msg = data;        
    });
}

function ChildCntl($scope) {               
    $scope.$on('someEvent', function(e) {  
        $scope.$emit("pingBack", $scope.get());        
    });

    $scope.get = function(){
        return "LOL";    
    }
}

Violon: http://jsfiddle.net/uypo360u/

Tcherniv
la source
C'est plutôt cool! Mais que se passe-t-il si vous ne (voulez) pas savoir où se trouve la portée de l'appelant (je veux dire dans $scope.$parentou dans $scope.$parent.$parent, etc.)? Ah, oui: passez un callback en params! :)
user2173353
@ user2173353 vous avez tout à fait raison; il y a un autre moyen: $emitd'un enfant à un parent. Je pense qu'il est temps de mettre à jour ma réponse ..
Cherniv
Est-ce une bonne idée d'appeler un écouteur global où se trouve quelque part, contrôleur enfant dans ce cas? N'est-il pas anti-modèle et difficile à tester plus tard? Ne devrions-nous pas utiliser des injections ou du smth?
calmbird
@calmbird, chaque chose doit être utilisée avec précaution, c'est sûr .. Certains préfèrent utiliser les services dans des cas similaires. Quoi qu'il en soit, j'ai ajouté une version plus élégante (sans $parent
gêner
9
Cette approche est plus propre. Passez un rappel au $broadcastet vous pouvez éliminer pingBackcomplètement le.
chic
34

Laissez-moi vous suggérer une autre solution:

var app = angular.module("myNoteApp", []);


app.controller("ParentCntl", function($scope) {
    $scope.obj = {};
});

app.controller("ChildCntl", function($scope) {
    $scope.obj.get = function() {
            return "LOL";    
    };
});

Moins de code et utilisant l'héritage prototypique.

Plunk

Canttouchit
la source
Quelqu'un pourrait-il expliquer si cette approche est meilleure que l'approche événementielle mentionnée ci-dessus dans quels cas et si c'est le cas, pourquoi? Et si vous avez une hiérarchie multi niveaux avec des contrôleurs enfants? cela fonctionnerait-il si le obj.get est défini à l'intérieur d'un contrôleur enfant dans un contrôleur enfant tant qu'aucun des contrôleurs n'a le même nom de fonction défini? Merci d'avance.
ZvKa
1
Permettez-moi de corriger ce que j'ai dit qui n'est pas totalement vrai. Vous avez raison dans ce que vous avez dit: l'héritage de portée n'est appliqué qu'à une seule direction ... enfant -> parent. Ce qui se passe réellement ici, c'est que si vous définissez $ scope.get dans la portée enfant, «get» ne sera défini que sur la portée enfant (parfois appelée définition primitive). Mais lorsque vous définissez $ scope.obj.get, la portée enfant recherchera obj qui est défini sur la portée parent, qui est en haut de la hiérarchie.
Canttouchit
3
Comment cela fonctionnera-t-il si l'enfant a sa propre portée d'isolat?
Dinesh
2
La question ne faisait pas référence à une portée isolée. Cependant, si vous avez une portée isolée, vous pouvez utiliser la liaison de portée isolée. Imo, la diffusion est un peu compliquée.
Canttouchit
2
Je ne pense pas que cet exemple soit possible si vous devez accéder à la fonction du contrôleur enfant à partir du contrôleur du parent , pas du modèle. Cet exemple ne fonctionne qu'en raison de la liaison bidirectionnelle sur le modèle, qui, je suppose, continue d'interroger jusqu'à ce que $scope.obj.get()soit une fonction valide.
danyim
11

Enregistrez la fonction de l'enfant sur le parent lors de l'initialisation de l'enfant. J'ai utilisé la notation «comme» pour plus de clarté dans le modèle.

MODÈLE

<div ng-controller="ParentCntl as p">
  <div ng-controller="ChildCntl as c" ng-init="p.init(c.get)"></div>
</div>

CONTRÔLEURS

...
function ParentCntl() {
  var p = this;
  p.init = function(fnToRegister) {
    p.childGet = fnToRegister;
  };
 // call p.childGet when you want
}

function ChildCntl() {
  var c = this;
  c.get = function() {
    return "LOL";    
  };
}

"Mais", dites-vous, " ng-init n'est pas censé être utilisé de cette façon !". Eh bien, oui, mais

  1. cette documentation n'explique pas pourquoi, et
  2. Je ne pense pas que les auteurs de la documentation aient considéré TOUS les cas d'utilisation possibles.

Je dis que c'est une bonne utilisation pour cela. Si vous souhaitez me rejeter, veuillez commenter avec des raisons! :)

J'aime cette approche car elle garde les composants plus modulaires. Les seules liaisons se trouvent dans le modèle et cela signifie que

  • le contrôleur enfant n'a rien à savoir sur l'objet auquel ajouter sa fonction (comme dans la réponse de @ canttouchit)
  • le contrôle parent peut être utilisé avec tout autre contrôle enfant qui a une fonction get
  • ne nécessite pas de diffusion, ce qui deviendra très moche dans une grosse application à moins que vous ne contrôliez étroitement l'espace de noms des événements

Cette approche approche plus étroitement l'idée de Tero de modulariser avec des directives (notez que dans son exemple modularisé, contestantsest passée de la directive parent à la directive "enfant" DANS LE TEMPLATE).

En effet, une autre solution pourrait être d'envisager de mettre en œuvre le ChildCntlcomme une directive et d'utiliser la &liaison pour enregistrer la initméthode.

chic
la source
1
Je sais que c'est un ancien message, mais cela semble être une mauvaise idée car le parent peut garder une référence à l'enfant après la destruction de l'enfant.
Amy Blankenship le
C'est une solution bien plus propre que la diffusion d'événements. Pas parfait, mais mieux. Bien que le nettoyage soit effectivement un problème.
mcv
-1

Vous pouvez créer un objet enfant.

var app = angular.module("myApp", []);


app.controller("ParentCntl", function($scope) {
    $scope.child= {};
    $scope.get = function(){
      return $scope.child.get(); // you can call it. it will return 'LOL'
    }
   // or  you can call it directly like $scope.child.get() once it loaded.
});

app.controller("ChildCntl", function($scope) {
    $scope.obj.get = function() {
            return "LOL";    
    };
});

Ici, l'enfant prouve la destination de la méthode get.

Ramu Agrawal
la source