angleJS: Aufrufen der untergeordneten Bereichsfunktion im übergeordneten Bereich

95

Wie kann eine im untergeordneten Bereich definierte Methode aus dem übergeordneten Bereich aufgerufen werden?

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

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

http://jsfiddle.net/wUPdW/

9blue
quelle
2
Am besten definieren Sie einen Dienst und fügen diesen Dienst in beide Controller ein. Oder verwenden Sie Rootscope.
TymeJV

Antworten:

145

Sie können $broadcastvom Elternteil zum Kind verwenden:

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";    
    }
}

Arbeitsgeige: http://jsfiddle.net/wUPdW/2/

UPDATE : Es gibt eine andere Version, die weniger gekoppelt und besser testbar ist:

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";    
    }
}

Geige: http://jsfiddle.net/uypo360u/

Cherniv
quelle
Das ist ziemlich toll! Aber was ist, wenn Sie nicht wissen (wollen), wo sich der Anruferbereich befindet (ich meine in $scope.$parentoder in $scope.$parent.$parentusw.)? Ah ja: Übergeben Sie einen Rückruf in params! :)
user2173353
@ user2173353 du hast sehr recht; Es gibt noch einen Weg: $emitvom Kind zum Elternteil. Ich denke, es ist eine Zeit, meine Antwort zu aktualisieren ..
Cherniv
Ist es eine gute Idee, den globalen Listener anzurufen, wo sich irgendwo ein untergeordneter Controller befindet? Ist es nicht antipattern und später schwer zu testen? Sollten wir nicht Injektionen oder etw verwenden?
Callybird
@calmbird, jedes Ding muss sorgfältig verwendet werden, das ist sicher. Einige bevorzugen die Verwendung von Diensten in ähnlichen Fällen. Wie auch immer, ich habe eine elegantere Version hinzugefügt (ohne zu nerven $parent)
Cherniv
9
Dieser Ansatz ist sauberer. Übergeben Sie einen Rückruf an der $broadcastund Sie können die pingBackinsgesamt beseitigen .
vornehmsten
34

Lassen Sie mich eine andere Lösung vorschlagen:

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


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

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

Weniger Code und prototypische Vererbung.

Zupfen

Canttouchit
quelle
Könnte jemand erklären, ob dieser Ansatz in welchen Fällen besser ist als der oben erwähnte ereignisgesteuerte Ansatz, und wenn ja, warum? Was ist, wenn Sie eine Multi-Level-Hierarchie mit untergeordneten Controllern haben? Würde dies funktionieren, wenn die obj.get in einem untergeordneten Controller innerhalb eines untergeordneten Controllers definiert ist, solange auf keinem der Controller derselbe Funktionsname definiert ist? Vielen Dank im Voraus.
ZvKa
1
Lassen Sie mich korrigieren, was ich gesagt habe, was nicht ganz richtig ist. Sie haben Recht mit dem, was Sie gesagt haben: Die Bereichsvererbung wird nur auf eine Richtung angewendet .... child -> parent. Was hier tatsächlich passiert, ist, dass, wenn Sie $ scope.get im untergeordneten Bereich definieren, 'get' nur im untergeordneten Bereich definiert wird (manchmal als primitive Definition bezeichnet). Wenn Sie jedoch $ scope.obj.get definieren, sucht der untergeordnete Bereich nach obj, das im übergeordneten Bereich definiert ist, der sich in der Hierarchie befindet.
Canttouchit
3
Wie wird dies funktionieren, wenn das Kind einen eigenen isolierten Bereich hat?
Dinesh
2
Die Frage bezog sich nicht auf einen isolierten Bereich. Wenn Sie jedoch einen isolierten Bereich haben, können Sie die Bindung des isolierten Bereichs verwenden. Imo, Rundfunk ist ein bisschen chaotisch.
Canttouchit
2
Ich denke nicht, dass dieses Beispiel möglich ist, wenn Sie über die Steuerung des übergeordneten Controllers auf die Funktion des untergeordneten Controllers zugreifen müssen , nicht über die Vorlage. Dieses Beispiel funktioniert nur aufgrund der bidirektionalen Bindung an die Vorlage, von der ich annehme, dass sie so lange abfragt, bis $scope.obj.get()eine gültige Funktion vorliegt.
Danyim
11

Registrieren Sie die Funktion des Kindes beim Elternteil, wenn das Kind initialisiert wird. Ich habe aus Gründen der Übersichtlichkeit in der Vorlage die Notation "als" verwendet.

VORLAGE

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

CONTROLLER

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

"Aber", sagen Sie, " ng-init soll nicht so verwendet werden !". Na ja, aber

  1. Diese Dokumentation erklärt nicht, warum nicht, und
  2. Ich glaube nicht, dass die Dokumentationsautoren ALLE möglichen Anwendungsfälle dafür in Betracht gezogen haben.

Ich sage, das ist eine gute Verwendung dafür. Wenn Sie mich ablehnen möchten, kommentieren Sie bitte mit Gründen! :) :)

Ich mag diesen Ansatz, weil er die Komponenten modularer hält. Die einzigen Bindungen befinden sich in der Vorlage und bedeuten dies

  • Der untergeordnete Controller muss nichts darüber wissen, welchem ​​Objekt er seine Funktion hinzufügen soll (wie in der Antwort von @ canttouchit).
  • Das übergeordnete Steuerelement kann mit jedem anderen untergeordneten Steuerelement verwendet werden, das eine get-Funktion hat
  • erfordert keine Übertragung, was in einer großen App sehr hässlich wird, wenn Sie den Ereignis-Namespace nicht genau kontrollieren

Dieser Ansatz nähert sich Teros Idee der Modularisierung mit Direktiven näher an (beachten Sie, dass in seinem modularisierten Beispiel die contestantsAnweisung IN DER VORLAGE von der übergeordneten zur "untergeordneten" Direktive übergeben wird).

In der Tat könnte eine andere Lösung darin bestehen, die Implementierung der ChildCntlRichtlinie in Betracht zu ziehen und die &Bindung zur Registrierung der initMethode zu verwenden.

vornehmsten
quelle
1
Ich weiß, dass dies ein alter Beitrag ist, aber dies scheint eine schlechte Idee zu sein, da die Eltern nach der Zerstörung des Kindes einen Verweis auf das Kind behalten können.
Amy Blankenship
Dies ist eine weitaus sauberere Lösung als das Senden von Ereignissen. Nicht perfekt, aber besser. Obwohl die Bereinigung in der Tat ein Problem ist.
mcv
-1

Sie können ein untergeordnetes Objekt erstellen.

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

Hier beweist das Kind das Ziel der Get-Methode.

Ramu Agrawal
quelle