AngularJS - Accesso all'ambito figlio


110

Se ho i seguenti controller:

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

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

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

Qual è il modo corretto per far parentleggere bfuori child? Se è necessario definire bin parent, questo non lo renderebbe semanticamente errato assumendo che bsia una proprietà che descrive qualcosa di correlato childe non parent?

Aggiornamento: Pensandoci oltre, se più di un bambino l'avesse avuta b, creerebbe un conflitto parentsu cui brecuperare. La mia domanda rimane: qual è il modo corretto per accedere bda parent?


1
Inoltre, assicurati di leggere questo: stackoverflow.com/questions/14049480/… Una panoramica molto buona e approfondita degli ambiti e dell'ereditarietà in angularJS.
Martijn

Risposte:


162

Gli ambiti in AngularJS utilizzano l'ereditarietà prototipale, quando si cerca una proprietà in uno scope figlio, l'interprete cercherà la catena del prototipo a partire dal bambino e continuerà con i genitori fino a quando non trova la proprietà, non viceversa.

Controlla i commenti di Vojta sul problema https://groups.google.com/d/msg/angular/LDNz_TQQiNE/ygYrSvdI0A0J

In poche parole: non è possibile accedere agli ambiti figlio da un ambito padre.

Le tue soluzioni:

  1. Definire le proprietà nei genitori e accedervi dai bambini (leggi il link sopra)
  2. Usa un servizio per condividere lo stato
  3. Passa i dati attraverso gli eventi. $emitinvia eventi verso l'alto ai genitori fino all'ambito radice e $broadcastinvia gli eventi verso il basso. Questo potrebbe aiutarti a mantenere le cose semanticamente corrette.

8
Inoltre, la differenza tra $ emit e $ broadcast è dove $ emit è cancellabile e $ broadcast no.
Azri Jamil

Un posto in cui probabilmente vuoi ottenere l'ambito figlio è quando le direttive di unit test. Se si dispone di una direttiva transclusa, l'ambito è un figlio dell'ambito utilizzato per compilare l'elemento. Ottenere l'accesso all'ambito della direttiva compilata per i test è impegnativo.
pmc

Grazie. Un'altra opzione potrebbe esserelocalStorage con una dipendenza come ngStorage.
Akash

81

Sebbene la risposta di jm sia il modo migliore per gestire questo caso, per riferimento futuro è possibile accedere agli ambiti figlio utilizzando i membri $$ childHead, $$ childTail, $$ nextSibling e $$ prevSibling di un ambito. Questi non sono documentati, quindi potrebbero cambiare senza preavviso, ma sono lì se hai davvero bisogno di attraversare gli ambiti.

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

Violino


49

Puoi provare questo:

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

quindi nel controller figlio (ambito) aggiungi:

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

Ora il genitore ha accesso all'ambito del bambino.


1
Molto pulito e semplice. Per aggiungere a questo, renditi conto che non esiste alcun legame, quindi quando lo dichiari come sopra, parentScope.child sta tenendo l'ambito figlio in quel momento. Per risolvere questo problema puoi aggiungere $ scope. $ Watch (function () {parentScope.child = $ scope}); in modo che dopo ogni digest spinga il nuovo ambito al genitore. All'estremità genitore, se stai usando uno qualsiasi degli ambiti figlio per gli oggetti visivi nell'html, dovrai aggiungere un orologio alla sua variabile figlia e dirgli di aggiornare l'ambito in questo modo: $ scope. $ Watch ('child', function ( ) {$ scope. $ evalAsync ();}); . Spero che questo faccia risparmiare tempo a qualcuno!
IfTrue

2
@IfTrue credo che un $ watch () non sia richiesto. Questi sono oggetti e passano per riferimento.
Swanidhi

2
@Swanidhi = So cosa intendi e lo pensavo anch'io, ma non ha funzionato quando stavo sperimentando al momento della stesura del commento. Per quello che vale, sono passato dal fare questo all'emissione / trasmissione.
IfTrue

1
Come farlo in Type Script?
ATHER

La migliore risposta per me !! Miglior esempio di oggetti trattati come riferimento in javascript
Vikas Gautam

4

Una possibile soluzione è inserire il controller figlio nel controller padre utilizzando una funzione di inizializzazione.

Possibile implementazione:

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

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

Dove ChildControllerhai:

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

}])

Quindi ora in ParentControllerpuoi usare:

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

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

}])

Importante:
per funzionare correttamente è necessario utilizzare la direttiva ng-controllere rinominare ogni controller utilizzandoas come ho fatto io in html es.

Suggerimenti:
utilizza il plug -in di chrome ng-inspector durante il processo. Ti aiuterà a capire l'albero.


3

Utilizzo di $ emit e $ broadcast , (come menzionato da walv nei commenti sopra)

Per attivare un evento verso l'alto (da bambino a genitore)

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

Per attivare un evento verso il basso (da genitore a figlio)

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

e infine ascoltare

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

Per maggiori dettagli: - https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/

Godere :)


1

Sì, possiamo assegnare variabili dal controller figlio alle variabili nel controller padre. Questo è un modo possibile:

Panoramica: l'obiettivo principale del codice, di seguito, è assegnare la $ scope.variable del controller figlio alla $ scope.assign del controller padre

Spiegazione: ci sono due controller. Nell'html, notare che il controller padre racchiude il controller figlio. Ciò significa che il controller padre verrà eseguito prima del controller figlio. Quindi, prima setValue () verrà definito e quindi il controllo andrà al controller figlio. $ scope.variable verrà assegnato come "figlio". Quindi questo ambito figlio verrà passato come argomento alla funzione del controller padre, dove $ scope.assign otterrà il valore come "figlio"

<!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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.