AngularJS - доступ до сфери дії для дітей


110

Якщо у мене є такі контролери:

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

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

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

Що таке правильний спосіб , щоб parentпрочитати bз child? Якщо це потрібно визначити bв parent, чи не буде це семантично неправильним, вважаючи, що bце властивість, яка описує щось, що стосується, childа не parent?

Оновлення: Думаючи про це далі, якщо б більше ніж одна дитина мала, bце створило б конфлікт, parentщодо якого bможна відшукати. Моє питання залишається, що правильний спосіб доступу bвід parent?


1
Також не забудьте прочитати це: stackoverflow.com/questions/14049480/… Дуже хороший та глибокий огляд областей та успадкування в кутовій JS.
Martijn

Відповіді:


162

Область застосування AngularJS використовує прототипне успадкування, коли шукати майно в області дитини, інтерпретатор буде шукати прототип ланцюжка, починаючи від дитини і продовжуючи до батьків, поки він не знайде властивість, а не навпаки.

Перевірте коментарі Vojta до проблеми https://groups.google.com/d/msg/angular/LDNz_TQQiNE/ygYrSvdI0A0J

Коротше кажучи: Ви не можете отримати доступ до дочірніх областей з батьківської області.

Ваші рішення:

  1. Визначте властивості у батьків та отримайте доступ до них від дітей (читайте посилання вище)
  2. Скористайтеся послугою, щоб поділитися станом
  3. Передайте дані через події. $emitпосилає події вгору батькам, поки не з'явиться коренева область та посилає $broadcastподії вниз. Це може допомогти вам тримати речі семантично правильними.

8
Плюс різниця між $ emit та $ Broadcast - це те, коли $ emit може скасовуватися, а $ Broadcast - не.
Азрі Джаміль

Одне місце, напевно, ви хочете отримати дочірнє поле - це директиви щодо тестування одиниць. Якщо у вас є включена директива, область застосування є дочірньою сферою, що використовується для компіляції елемента. Отримати доступ до сфери дії складеної директиви для тестування є складним завданням.
pmc

Дякую. Інший варіант може бути localStorageз такою залежністю ngStorage.
Акаш

81

У той час як відповідь jm-is є найкращим способом вирішення цього випадку, для подальшої довідки можна отримати доступ до областей дитини, використовуючи $$ childHead, $$ childTail, $$ nextSibling та $$ prevSibling. Вони не задокументовані, тому можуть змінитися без попередження, але вони є там, якщо вам дійсно потрібно пройти обсяг.

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

Скрипка


49

Ви можете спробувати це:

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

потім у дочірній контролер (область) додати:

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

Тепер батько має доступ до сфери дитини.


1
Дуже чистий і прямолінійний. Щоб додати до цього, просто зрозумійте, що немає прив'язки, тому коли ви заявляєте про це, як вище, то parentScope.child утримує область дитини в цей момент. Для вирішення цього питання можна додати $ range. $ Watch (function () {parentScope.child = $ obseg}); так що після кожного дайджесту він підштовхує нову сферу до батьківського. На батьківському кінці, якщо ви використовуєте будь-який із дочірніх областей для наочності в html, вам потрібно буде додати годинник до його дочірньої змінної та сказати їй оновити область на зразок: $ range. $ Watch ('child', function ( ) {$ obseg. $ evalAsync ();}); . Сподіваюся, це економить когось деякий час!
IfTrue

2
@IfTrue Я вважаю, що $ watch () не потрібно. Це об'єкти, і вони проходять шляхом посилання.
Swanidhi

2
@Swanidhi = Я знаю, що ти маєш на увазі, і я так думав, але це не спрацювало, коли я експериментував під час написання коментаря. Що варто, я перейшов від цього на емісію / мовлення.
IfTrue

1
Як це зробити в Type Script?
АТЕР

Найкраща відповідь для мене !! Найкращий приклад об’єктів, які посилаються на javascript
Vikas Gautam,

4

Одним із можливих варіантів вирішення проблеми є введення дочірнього контролера у батьківський контролер за допомогою функції init.

Можлива реалізація:

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

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

Де у ChildControllerвас є:

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

}])

Тож тепер у ParentControllerвашому користуванні можна використовувати:

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

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

}])

Важливо:
Щоб правильно працювати, ви повинні використовувати директиву ng-controllerта перейменовувати кожен контролер, використовуючи так, asяк я це робив у html, наприклад.

Поради:
Використовуйте хромовий плагін ng-інспектор під час процесу. Це допоможе вам зрозуміти дерево.


3

Використання $ emit та $ Broadcast (як згадує Walv у коментарях вище)

Запустити подію вгору (від дитини до батьків)

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

Запустити подію вниз (від батька до дитини)

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

і нарешті слухати

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

Докладніше: - https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/

Насолоджуйтесь :)


1

Так, ми можемо призначити змінні від дочірнього контролера до змінних у батьківському контролері. Це один із можливих способів:

Огляд: Основна мета коду нижче - присвоїти дочірньому контролеру $ range.variable батьківському контролеру $ range.assign

Пояснення: Є два контролери. У html зверніть увагу, що батьківський контролер додає дочірній контролер. Це означає, що батьківський контролер буде виконаний перед дочірнім контролером. Отже, спочатку буде визначено setValue (), а потім управління перейде до дочірнього контролера. $ range.variable буде призначено як "дочірня". Тоді ця дочірня область буде передана як аргумент функції батьківського контролера, де $ Domain.assign отримає значення "дочірній"

<!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>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.