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? Если необходимо определить bin parent, не сделает ли это семантически некорректным, если предположить, что bэто свойство описывает что-то, связанное с чем-то, childа не parent?

Обновление: Размышляя дальше об этом, если более чем один ребенок имел bэто создало бы конфликт для parentна который bнеобходимо получить. Мой вопрос остается, что правильный способ доступа bот parent?

Азиз Альфудари
источник
1
Также обязательно прочтите это: stackoverflow.com/questions/14049480/… Очень хороший и подробный обзор областей видимости и наследования в angularJS.
Martijn

Ответы:

162

Области видимости в AngularJS используют прототипное наследование, при поиске свойства в дочерней области интерпретатор будет искать цепочку прототипов, начиная с дочернего элемента, и переходить к родителям, пока не найдет свойство, а не наоборот.

Проверьте комментарии Войты по проблеме https://groups.google.com/d/msg/angular/LDNz_TQQiNE/ygYrSvdI0A0J

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

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

  1. Определите свойства в родителях и получите к ним доступ от детей (прочтите ссылку выше)
  2. Используйте сервис, чтобы поделиться состоянием
  3. Передавать данные через события. $emitотправляет события вверх родительским элементам до корневой области и $broadcastотправляет события вниз. Это может помочь вам сохранить семантическую правильность.
Хайме
источник
8
Кроме того, разница между $ emit и $ broadcast заключается в том, что $ emit можно отменить, а $ broadcast - нет.
Азри Джамиль
Одно из мест, где вы, вероятно, захотите получить дочернюю область, - это директивы модульного тестирования. Если у вас есть включенная директива, область видимости является дочерней по отношению к области, используемой для компиляции элемента. Получить доступ к объему скомпилированной директивы для тестирования непросто.
pmc
Спасибо. Другой вариант может быть localStorageс зависимостью вроде ngStorage.
Akash
81

Хотя ответ jm- - лучший способ справиться с этим случаем, для справки в будущем можно получить доступ к дочерним областям, используя элементы $$ 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 удерживает дочернюю область в этот момент. Чтобы решить эту проблему, вы можете добавить $ scope. $ Watch (function () {parentScope.child = $ scope}); так что после каждого дайджеста он подталкивает новую область видимости к родительской. На родительском конце, если вы используете любую из дочерних областей видимости для визуальных элементов в html, вам нужно будет добавить часы для ее дочерней переменной и указать ему обновить область следующим образом: $ scope. $ Watch ('child', function ( ) {$ scope. $ evalAsync ();}); . Надеюсь, это кому-то сэкономит время!
IfTrue 03
2
@IfTrue Я считаю, что $ watch () не требуется. Это объекты, и они передаются по ссылке.
Swanidhi
2
@Swanidhi = Я знаю, что вы имеете в виду, и я тоже так думал, но это не сработало, когда я экспериментировал во время написания комментария. Как бы то ни было, я переключился с этого на излучение / трансляцию.
IfTrue
1
Как это сделать в Type Script?
ATHER
Лучший ответ для меня !! Лучший пример объектов, рассматриваемых как ссылка в javascript
Викас Гаутам
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, например.

Советы:
используйте в процессе Chrome плагин ng-Inspector . Это поможет вам понять дерево.

BorracciaBlu
источник
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

Да, мы можем назначать переменные дочернего контроллера переменным родительского контроллера. Это один из возможных способов:

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

Пояснение: Есть два контроллера. Обратите внимание, что в HTML-коде родительский контроллер включает дочерний контроллер. Это означает, что родительский контроллер будет выполнен раньше дочернего контроллера. Итак, сначала будет определена setValue (), а затем управление перейдет к дочернему контроллеру. $ scope.variable будет назначен "дочерним". Затем эта дочерняя область будет передана в качестве аргумента функции родительского контроллера, где $ scope.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>
rupali317
источник