angularJS: Jak wywołać funkcję zakresu potomnego w zakresie nadrzędnym


95

Jak wywołać metodę zdefiniowaną w zakresie podrzędnym z zakresu nadrzędnego?

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

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

http://jsfiddle.net/wUPdW/


2
Najlepiej, zdefiniuj usługę, wstrzyknij tę usługę do obu kontrolerów. Lub użyj rootscope.
— tymeJV

Odpowiedzi:


146

Możesz używać $broadcastod rodzica do dziecka:

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

Skrzypce robocze: http://jsfiddle.net/wUPdW/2/

AKTUALIZACJA : Istnieje inna wersja, mniej sprzężona i bardziej testowalna:

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

Skrzypce: http://jsfiddle.net/uypo360u/


To fajnie! Ale co, jeśli nie (chcesz) wiedzieć, gdzie jest zakres wywołującego (mam na myśli in $scope.$parentlub in $scope.$parent.$parentitd.)? Ach, tak: przekaż wywołanie zwrotne w parametrach! :)
— user2173353

@ user2173353 masz rację; jest jeszcze jeden sposób: $emitod dziecka do rodzica. Myślę, że nadszedł czas, aby zaktualizować moją odpowiedź ...
— Ivan Chernykh

Czy to dobry pomysł, aby wywołać globalny odbiornik, gdzie gdzieś jest, w tym przypadku kontroler podrzędny? Czy to nie jest antywzorowe i trudne do późniejszego przetestowania? Czy nie powinniśmy używać zastrzyków czy czegoś takiego?
— calmbird,

@calmbird, z każdej rzeczy trzeba korzystać ostrożnie, to na pewno… Niektórzy wolą korzystać z usług w podobnych przypadkach. W każdym razie dodałem bardziej elegancką wersję (bez denerwowania $parent)
— Ivan Chernykh

9
To podejście jest czystsze. Przekaż oddzwonienie pod adresem $broadcasti możesz pingBackcałkowicie wyeliminować .
— najpiękniejszy

35

Zasugeruję inne rozwiązanie:

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


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

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

Mniej kodu i korzystanie z dziedziczenia prototypowego.

Dolar


Czy ktoś mógłby wyjaśnić, czy to podejście jest lepsze niż wspomniane powyżej podejście oparte na zdarzeniach, w jakich przypadkach, a jeśli tak, to dlaczego? co jeśli masz hierarchię wielopoziomową z kontrolerami podrzędnymi? czy to zadziałałoby, gdyby obiekt obj.get został zdefiniowany w kontrolerze podrzędnym w kontrolerze podrzędnym, o ile żaden z kontrolerów nie ma zdefiniowanej tej samej nazwy funkcji? Z góry dziękuję.
— ZvKa

1
Pozwólcie, że poprawię to, co powiedziałem, co nie jest do końca prawdą. Masz rację w tym, co powiedziałeś: dziedziczenie zakresu jest stosowane tylko w jednym kierunku ... dziecko -> rodzic. W rzeczywistości dzieje się tutaj to, że jeśli zdefiniujesz $ scope.get w zakresie potomnym, 'get' zostanie zdefiniowane tylko w zakresie potomnym (czasami określane jako definicja pierwotna). Ale kiedy zdefiniujesz $ scope.obj.get, wtedy zakres potomny będzie szukał obiektu, który jest zdefiniowany w zakresie nadrzędnym, który znajduje się wyżej w hierarchii.
— Canttouchit

3
Jak to zadziała, jeśli dziecko ma swój własny izolowany zakres?
— Dinesh

2
Pytanie nie odnosiło się do zakresu izolowanego, jednak jeśli masz zakres izolowany, możesz użyć powiązania zakresu izolowanego. Imo, nadawanie jest trochę bałaganiarskie.
— Canttouchit

3
Nie sądzę, że ten przykład jest możliwe, jeśli chcesz mieć dostęp do funkcji regulatora podrzędnego od rodzica kontrolera , a nie szablonu. Ten przykład działa tylko z powodu dwukierunkowego wiązania w szablonie, które, jak zakładam, odpytuje, dopóki nie $scope.obj.get()będzie prawidłową funkcją.
— danyim

12

Zarejestruj funkcję dziecka w rodzicu, gdy dziecko jest inicjowane. Użyłem notacji „jako” dla przejrzystości szablonu.

SZABLON

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

KONTROLERY

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

„Ale”, mówisz, „ ng-init nie powinno być używane w ten sposób !”. Cóż, tak, ale

  1. ta dokumentacja nie wyjaśnia, dlaczego nie, i
  2. Nie wierzę, że autorzy dokumentacji rozważali WSZYSTKIE możliwe przypadki użycia tego.

Mówię, że to dobry użytek. Jeśli chcesz mnie zlekceważyć, skomentuj z uzasadnieniem! :)

Podoba mi się to podejście, ponieważ sprawia, że ​​komponenty są bardziej modułowe. Jedyne powiązania znajdują się w szablonie i to oznacza

  • kontroler podrzędny nie musi nic wiedzieć o tym, do którego obiektu dodać swoją funkcję (jak w odpowiedzi @ canttouchit)
  • formant nadrzędny może być używany z dowolnym innym formantem podrzędnym, który ma funkcję get
  • nie wymaga nadawania, co w dużej aplikacji będzie bardzo brzydkie, chyba że ściśle kontrolujesz przestrzeń nazw zdarzeń

To podejście jest bliższe idei Tero, polegającej na modularyzacji za pomocą dyrektyw (zauważ, że w jego zmodularyzowanym przykładzie contestantsjest przekazywana z dyrektywy rodzica do dyrektywy „dziecka” W SZABLONIE).

W istocie innym rozwiązaniem mogłoby być rozważenie implementacji ChildCntldyrektywy w formie dyrektywy i wykorzystanie &powiązania do zarejestrowania initmetody.


1
Wiem, że to stary post, ale wydaje się to złym pomysłem, ponieważ rodzic może zachować odniesienie do dziecka po zniszczeniu dziecka.
— Amy Blankenship,

Jest to znacznie czystsze rozwiązanie niż transmitowanie wydarzeń. Nie idealnie, ale lepiej. Chociaż porządkowanie jest rzeczywiście problemem.
— mcv

-1

Możesz zrobić obiekt potomny.

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

Tutaj dziecko udowadnia przeznaczenie metody get.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.