web-dev-qa-db-fra.com

AngularJS - Accès à la portée de l'enfant

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 parent lire b sur child? S'il est nécessaire de définir b dans parent, cela ne le rend-il pas sémantiquement incorrect si l'on suppose que b est une propriété qui décrit un élément lié à child et non parent?

Mise à jour: Si l’on réfléchissait plus avant, si plus d’un enfant avait b, cela créerait un conflit pour parent sur lequel b à récupérer. Ma question demeure, quelle est la bonne façon d'accéder à b à partir de parent?

107
Aziz Alfoudari

Les champs d’application dans AngularJS utilisent l’héritage prototype, lorsqu’il recherche une propriété dans un périmètre enfant, l’interprète recherche la chaîne de prototypes à partir de l’enfant et continue 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 portée parent. 

Vos solutions:

  1. Définir les propriétés des parents et y accéder depuis les enfants (lire le lien ci-dessus)
  2. Utiliser un service pour partager l'état
  3. Passer des données à travers des événements. $emit envoie les événements vers le haut aux parents jusqu'à ce que la portée racine et $broadcast distribue les événements vers le bas. Cela pourrait vous aider à garder les choses sémantiquement correctes.
158
jaime

Bien que la réponse de jm- soit le meilleur moyen de gérer ce cas, il est possible d'accéder ultérieurement aux étendues enfants à l'aide des membres $$ childHead, $$ childTail, $$ nextSibling et $$ prevSibling d'une étendue. Celles-ci ne sont pas documentées, elles peuvent donc changer sans préavis, mais elles sont présentes si vraiment devez parcourir des étendues.

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

Fiddle

80
Jussi Kosunen

Vous pouvez essayer ceci:

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

puis dans le contrôleur enfant (scope), ajoutez:

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

Maintenant, le parent a accès à la portée de l'enfant.

48
Farzin Mo

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

Mise en oeuvre possible:

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

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

Où dans ChildController vous avez:

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

}])

Alors maintenant, dans la ParentController, vous 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-controller et renommer chaque contrôleur en utilisant as comme je l’ai fait dans le code HTML, par exemple.

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

4
borracciaBlu

Oui, nous pouvons affecter des variables du contrôleur enfant aux variables du contrôleur parent. C'est 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 y a deux contrôleurs. Dans le code HTML, notez que le contrôleur parent englobe le contrôleur enfant. Cela signifie que le contrôleur parent sera exécuté avant le contrôleur enfant. Donc, setValue () sera d'abord défini, puis le contrôle ira au contrôleur enfant. $ scope.variable sera assigné comme "enfant". Ensuite, cette étendue enfant sera transmise en tant qu’argument à la fonction de contrôleur parent, où $ scope.assign obtiendra la valeur sous la forme "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>
1
rupali317

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

Pour déclencher un événement vers le haut (d'un enfant à l'autre)

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

Pour déclencher un événement vers le bas (de parent à enfant)

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

et enfin d'é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 :)

0
Anjana Silva

Je ne suis pas sûr que vous "deviez" ou non accéder à la portée de l'enfant à partir du parent, mais c'est possible. Je le sais parce que je viens de le faire moi-même. Voici mon scénario:

J'ai une fonction qui navigue dans mes pages en modifiant la chaîne dans une variable. Que cette variable soit référencée dans le fichier index.html principal en tant que ng-include. Ainsi, lorsque la chaîne est modifiée, un nouveau fichier HTML est chargé dans ce ng-include. À l'intérieur de l'un de ces fichiers HTML, j'ai un autre ng-include. Pensez à celui-ci comme une fenêtre plus petite dans la première fenêtre. Chaque fois que je modifie le contenu de ces fenêtres, je modifie également le contenu d'un panneau latéral. Ce panneau latéral est au niveau parent. Mais, lorsque je vais dans la fenêtre la plus petite du parent, ce panneau latéral de niveau parent contient des liens de navigation qui changent les choses dans la petite fenêtre. Comme le panneau latéral est au niveau parent et que la navigation de la fenêtre la plus petite est prise en charge dans son propre contrôleur au niveau enfant, lorsque je clique sur les liens du panneau latéral, il doit éditer le contrôleur enfant. Cela peut être une mauvaise programmation. Honnêtement, je ne sais pas. Je viens de commencer avec angularjs il y a quelques mois et j'apprends encore. Mais, pour que cela fonctionne avec mon scénario, j'ai créé une fonction dans la portée parente, appelée lors du chargement de la portée enfant:

function parent($scope, service) {
    setCurrentChildController = function (childScope) {
    this.$scope.childScope = childScope;
}
}

function child($scope) {
    scope.$parent.setCurrentChildController($scope);
}

ainsi, la variable $ scope.childScope de l'étendue parent est maintenant essentiellement l'étendue du contrôleur enfant. Il a accès à toutes les variables et fonctions de la portée des enfants. Si c'est une mauvaise programmation et que n'importe qui peut trouver une solution alternative au problème avec mon scénario, ce serait génial. Mais cela fonctionne si nécessaire.

0
tkd_aj