AngularJS - Accès à la portée enfant

110

Si j'ai les contrôleurs suivants:

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

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

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

Quelle est la bonne façon de laisser sortir la parentlecture ? S'il est nécessaire de définir dans , cela ne le rendrait-il pas sémantiquement incorrect en supposant qu'il s'agit d'une propriété qui décrit quelque chose lié à et non ?bchildbparentbchildparent

Mise à jour: en y réfléchissant plus loin, si plus d'un enfant en avait, bcela créerait un conflit pour parentlequel brécupérer. Ma question demeure, quelle est la bonne façon d'accéder à bpartir parent?

Aziz Alfoudari
la source
1
Assurez-vous également de lire ceci: stackoverflow.com/questions/14049480/… Un très bon aperçu détaillé des portées et de l'héritage dans angularJS.
Martijn

Réponses:

162

Les étendues dans AngularJS utilisent l'héritage prototypique, lors de la recherche d'une propriété dans une étendue enfant, l'interpréteur recherchera la chaîne prototype à partir de l'enfant et continuera jusqu'aux parents jusqu'à ce qu'il trouve la propriété, et non l'inverse.

Consultez les commentaires de Vojta sur le problème https://groups.google.com/d/msg/angular/LDNz_TQQiNE/ygYrSvdI0A0J

En un mot: vous ne pouvez pas accéder aux étendues enfants à partir d'une étendue parent.

Vos solutions:

  1. Définissez les propriétés des parents et accédez-y depuis les enfants (lisez le lien ci-dessus)
  2. Utiliser un service pour partager l'état
  3. Transmettez les données à travers les événements. $emitenvoie les événements vers le haut aux parents jusqu'à la portée racine et $broadcastdistribue les événements vers le bas. Cela peut vous aider à garder les choses sémantiquement correctes.
jaime
la source
8
De plus, la différence entre $ emit et $ broadcast est où $ emit est annulable et $ broadcast dont.
Azri Jamil
Un endroit où vous souhaitez probablement obtenir la portée enfant est lorsque les directives de test unitaire. Si vous avez une directive transclue, la portée est un enfant de la portée utilisée pour compiler l'élément. Accéder à la portée de la directive compilée pour les tests est un défi.
pmc
Merci. Une autre option pourrait être localStorageavec une dépendance comme ngStorage.
Akash
81

Bien que la réponse de jm- soit la meilleure façon de gérer ce cas, pour référence future, il est possible d'accéder aux portées enfants en utilisant les membres $$ childHead, $$ childTail, $$ nextSibling et $$ prevSibling d'une portée. Ceux-ci ne sont pas documentés, ils peuvent donc changer sans préavis, mais ils sont là si vous avez vraiment besoin de parcourir les étendues.

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

Violon

Jussi Kosunen
la source
49

Vous pouvez essayer ceci:

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

puis dans le contrôleur enfant (portée), ajoutez:

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

Le parent a maintenant accès à la portée de l'enfant.

Farzin Mo
la source
1
Très propre et simple. Pour ajouter à cela, sachez qu'il n'y a pas de liaison, donc lorsque vous le déclarez comme ci-dessus, parentScope.child tient la portée de l'enfant à ce moment. Pour résoudre ce problème, vous pouvez ajouter $ scope. $ Watch (function () {parentScope.child = $ scope}); de sorte qu'après chaque résumé, il pousse la nouvelle portée jusqu'au parent. Du côté parent, si vous utilisez l'une des portées enfants pour les visuels dans le html, vous devrez ajouter une montre sur sa variable enfant et lui dire de mettre à jour la portée comme suit: $ scope. $ Watch ('child', function ( ) {$ portée. $ evalAsync ();}); . J'espère que cela fera gagner du temps à quelqu'un!
IfTrue
2
@IfTrue Je crois qu'un $ watch () n'est pas nécessaire. Ce sont des objets et ils passent par référence.
Swanidhi
2
@Swanidhi = Je sais ce que vous voulez dire et je le pensais aussi mais cela n'a pas fonctionné lorsque j'expérimentais au moment d'écrire le commentaire. Pour ce que cela vaut, je suis passé de cela à émettre / diffuser.
IfTrue
1
Comment faire cela dans Type Script?
ATHER
Meilleure réponse pour moi !! Meilleur exemple d'objets traités comme par référence en javascript
Vikas Gautam
4

Une solution de contournement possible consiste à injecter le contrôleur enfant dans le contrôleur parent à l'aide d'une fonction init.

Implémentation possible:

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

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

Où en ChildControllervous avez:

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

}])

Alors maintenant, ParentControllervous pouvez utiliser:

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

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

}])

Important:
pour fonctionner correctement, vous devez utiliser la directive ng-controlleret renommer chaque contrôleur en utilisant ascomme je l'ai fait dans le html, par exemple.

Conseils:
utilisez le plugin chrome ng-inspector pendant le processus. Cela vous aidera à comprendre l'arbre.

borracciaBlu
la source
3

Utilisation de $ emit et $ broadcast , (comme mentionné par walv dans les commentaires ci-dessus)

Pour déclencher un événement vers le haut (de l'enfant au parent)

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

Pour déclencher un événement vers le bas (du parent à l'enfant)

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

et enfin écouter

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

Pour plus de détails: - https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/

Prendre plaisir :)

Anjana Silva
la source
1

Oui, nous pouvons affecter des variables du contrôleur enfant aux variables du contrôleur parent. Voici un moyen possible:

Vue d'ensemble: Le but principal du code, ci-dessous, est d'affecter $ scope.variable du contrôleur enfant au $ scope.assign du contrôleur parent

Explication: Il existe deux contrôleurs. Dans le html, notez que le contrôleur parent renferme le contrôleur enfant. Cela signifie que le contrôleur parent sera exécuté avant le contrôleur enfant. Ainsi, d'abord setValue () sera défini, puis le contrôle ira au contrôleur enfant. $ scope.variable sera assigné comme "enfant". Ensuite, cette portée enfant sera passée en argument à la fonction du contrôleur parent, où $ scope.assign obtiendra la valeur comme "enfant"

<!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
la source