AngularJS - Dostęp do zakresu podrzędnego

110

Jeśli mam następujące kontrolery:

function parent($scope, service) {
    $scope.a = 'foo';

    $scope.save = function() {
        service.save({
            a:  $scope.a,
            b:  $scope.b
        });
    }
}

function child($scope) {
    $scope.b = 'bar';
}

Jaki jest właściwy sposób aby parentzapoznać bsię z child? Jeśli konieczne jest zdefiniowanie bw parent, czy nie oznacza to, że byłoby to semantycznie niepoprawne, zakładając, że bjest to właściwość, która opisuje coś związanego, childa nie parent?

Aktualizacja: Dalsze myślenie o tym, gdyby miało bto więcej niż jedno dziecko , spowodowałoby konflikt, parentna którym można bby odzyskać. Moi pozostaje pytanie, jaki jest właściwy sposób, aby uzyskać dostęp bz parent?

Aziz Alfoudari
źródło
1
Upewnij się również, że przeczytałeś to: stackoverflow.com/questions/14049480/… Bardzo dobry i dogłębny przegląd zakresów i dziedziczenia w angularJS.
Martijn,

Odpowiedzi:

162

Zakresy w AngularJS używają dziedziczenia prototypowego, podczas wyszukiwania właściwości w zakresie podrzędnym interpreter będzie szukał łańcucha prototypów zaczynając od dziecka i kontynuował do rodziców, aż znajdzie właściwość, a nie na odwrót.

Sprawdź komentarze Vojty w tej sprawie https://groups.google.com/d/msg/angular/LDNz_TQQiNE/ygYrSvdI0A0J

W skrócie: nie można uzyskać dostępu do zakresów podrzędnych z zakresu nadrzędnego.

Twoje rozwiązania:

  1. Zdefiniuj właściwości w rodzicach i uzyskaj do nich dostęp od dzieci (przeczytaj link powyżej)
  2. Użyj usługi, aby udostępnić stan
  3. Przekazywanie danych przez zdarzenia. $emitwysyła zdarzenia w górę do rodziców aż do zakresu głównego, a $broadcastzdarzenia w dół. Może to pomóc w utrzymaniu poprawności semantycznej.
jaime
źródło
8
Dodatkowo różnica między $ emit i $ broadcast polega na tym, że $ emit jest anulowalny, a $ broadcast nie.
Azri Jamil
Jednym z miejsc, w których prawdopodobnie chcesz uzyskać zakres podrzędny, są dyrektywy testów jednostkowych. Jeśli masz dyrektywę transkludowaną, zakres jest elementem podrzędnym zakresu użytego do kompilacji elementu. Uzyskanie dostępu do zakresu dyrektywy skompilowanej w celu testowania jest trudne.
pmc
Dzięki. Inną opcją może być localStoragezależność taka jak ngStorage.
Akash
81

Chociaż odpowiedź jm- jest najlepszym sposobem na rozwiązanie tego przypadku, w przyszłości możliwe jest uzyskanie dostępu do zakresów podrzędnych za pomocą elementów członkowskich $$ childHead, $$ childTail, $$ nextSibling i $$ prevSibling zakresu. Nie są one udokumentowane, więc mogą ulec zmianie bez powiadomienia, ale są tam, jeśli naprawdę potrzebujesz przemierzać lunety.

// get $$childHead first and then iterate that scope's $$nextSiblings
for(var cs = scope.$$childHead; cs; cs = cs.$$nextSibling) {
    // cs is child scope
}

Skrzypce

Jussi Kosunen
źródło
49

Możesz spróbować tego:

$scope.child = {} //declare it in parent controller (scope)

następnie w kontrolerze podrzędnym (zakresie) dodaj:

var parentScope = $scope.$parent;
parentScope.child = $scope;

Teraz rodzic ma dostęp do zakresu dziecka.

Farzin Mo
źródło
1
Bardzo czysty i prosty. Aby dodać do tego, po prostu uświadom sobie, że nie ma żadnego wiązania, więc kiedy deklarujesz to jak powyżej parentScope.child trzyma w tym momencie zasięg childs. Aby rozwiązać ten problem, możesz dodać $ scope. $ Watch (function () {parentScope.child = $ scope}); tak, że po każdym skrócie wypycha nowy zakres do rodzica. Po stronie nadrzędnej, jeśli używasz któregokolwiek z zakresów podrzędnych do wizualizacji w html, będziesz musiał dodać zegarek do jego zmiennej podrzędnej i powiedzieć mu, aby zaktualizował zakres w następujący sposób: $ scope. $ Watch ('child', function ( ) {$ scope. $ evalAsync ();}); . Mam nadzieję, że to komuś zaoszczędzi trochę czasu!
IfTrue
2
@IfTrue Uważam, że $ watch () nie jest wymagany. To są obiekty i są przekazywane przez odniesienie.
Swanidhi
2
@Swanidhi = Wiem, co masz na myśli i ja też tak myślałem, ale to nie zadziałało, kiedy eksperymentowałem w momencie pisania komentarza. Na co warto przestawiłem się z robienia tego na emisję / nadawanie.
IfTrue
1
Jak to zrobić w skrypcie typu?
ATHER
Najlepsza odpowiedź dla mnie !! Najlepszy przykład obiektów traktowanych jako odniesienie w javascript
Vikas Gautam
4

Jednym z możliwych obejść jest wstrzyknięcie kontrolera podrzędnego do kontrolera nadrzędnego za pomocą funkcji inicjującej.

Możliwa realizacja:

<div ng-controller="ParentController as parentCtrl">
   ...

    <div ng-controller="ChildController as childCtrl" 
         ng-init="ChildCtrl.init()">
       ...
    </div>
</div>

Gdzie ChildControllermasz:

app.controller('ChildController',
    ['$scope', '$rootScope', function ($scope, $rootScope) {
    this.init = function() {
         $scope.parentCtrl.childCtrl = $scope.childCtrl;
         $scope.childCtrl.test = 'aaaa';
    };

}])

Więc teraz ParentControllermożesz użyć:

app.controller('ParentController',
    ['$scope', '$rootScope', 'service', function ($scope, $rootScope, service) {

    this.save = function() {
        service.save({
            a:  $scope.parentCtrl.ChildCtrl.test
        });
     };

}])

Ważne:
Aby działać poprawnie, musisz użyć dyrektywy ng-controlleri zmienić nazwę każdego kontrolera, używając astak, jak zrobiłem w html, np.

Wskazówki: Podczas procesu
użyj wtyczki ng-inspector chrome . Pomoże ci to zrozumieć drzewo.

borracciaBlu
źródło
3

Używanie $ emit i $ broadcast (jak wspomniał walv w komentarzach powyżej)

Aby uruchomić wydarzenie w górę (od dziecka do rodzica)

$scope.$emit('myTestEvent', 'Data to send');

Aby uruchomić zdarzenie w dół (od rodzica do dziecka)

$scope.$broadcast('myTestEvent', {
  someProp: 'Sending you some data'
});

i wreszcie posłuchać

$scope.$on('myTestEvent', function (event, data) {
  console.log(data);
});

Więcej informacji: - https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/

Cieszyć się :)

Anjana Silva
źródło
1

Tak, możemy przypisać zmienne z kontrolera podrzędnego do zmiennych w kontrolerze nadrzędnym. Oto jeden możliwy sposób:

Omówienie: głównym celem poniższego kodu jest przypisanie zmiennej $ scope.variable kontrolera podrzędnego do $ scope.epage kontrolera nadrzędnego

Objaśnienie: Istnieją dwa kontrolery. Zwróć uwagę, że w html kontroler nadrzędny obejmuje kontroler podrzędny. Oznacza to, że kontroler nadrzędny zostanie wykonany przed kontrolerem podrzędnym. Zatem najpierw zostanie zdefiniowana metoda setValue (), a następnie sterowanie przejdzie do kontrolera podrzędnego. Zmienna $ scope.variable zostanie przypisana jako „dziecko”. Następnie ten zakres potomny zostanie przekazany jako argument do funkcji kontrolera nadrzędnego, gdzie $ scope. assign otrzyma wartość „child”

<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script type="text/javascript">
    var app = angular.module('myApp',[]);

    app.controller('child',function($scope){
        $scope.variable = "child";
        $scope.$parent.setValue($scope);
    });

    app.controller('parent',function($scope){
        $scope.setValue = function(childscope) {
            $scope.assign = childscope.variable;
        }
    });

</script>
<body ng-app="myApp">
 <div ng-controller="parent">
    <p>this is parent: {{assign}}</p>
    <div ng-controller="child">
        <p>this is {{variable}}</p>
    </div>
 </div>
</body>
</html>
rupali317
źródło