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 . Это поможет вам понять дерево.


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>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.