Доступ к родительской области в директиве Transcluded

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

Я действительно хочу не помещать что-то вроде SOME_CONST (что помогло бы мне обновлять DOM через поток управления) в MyCtrl.

<div ng-controller="MyCtrl">
    <parent>
        <child></child>
    </parent>
</div>

var myApp = angular.module('myApp',[]);

function MyCtrl($scope) {
    $scope.obj = {prop:'foo'};
}

myApp.directive('parent', function() {
    return {
        scope: true,
        transclude: true,
        restrict: 'EA',
        template: '<div ng-transclude><h1>I\'m parent {{obj.prop}}<h1></div>',
        link: function(scope, elem, attrs) {
            scope.SOME_CONST = 'someConst';
        }
    }
});

myApp.directive('child', function() {
    return {
        restrict: 'EA',
        template: '<h1>I\'m child.... I want to access my parent\'s stuff, but I can\'t.  I can access MyCtrlScope though, see <b>{{obj.prop}}</b></h1> how can I access the <b>SOME_CONST</b> value in my parent\'s link function?  is this even a good idea? {{SOME_CONST}}.  I really don\'t want to put everything inside the MyCtrl',
    }
});

См. эту скрипту.

Спасибо


person binarygiant    schedule 31.05.2013    source источник


Ответы (4)


С transclude: true и scope: true директива parent создает две новые области видимости: введите здесь описание изображения

Область 004 является результатом scope: true, а область 005 — результатом transclude: true. Поскольку директива child не создает новую область, она использует включенную область 005. Как видно из диаграммы, нет пути от области 005 к области 004 (кроме как через частное свойство $$prevSibling, которое идет в противоположном направлении от $$nextSibling -- но не используйте их.)

Решение @joakimbl, вероятно, здесь лучше всего, хотя я думаю, что более распространено определение API в контроллере родительской директивы, а не определение свойств в this:

controller: function($scope) {
    $scope.SOME_CONST = 'someConst';
    this.getConst = function() {
       return $scope.SOME_CONST;
    }
}

Затем в директиве child:

link:function(scope,element,attrs,parentCtrl){
    scope.SOME_CONST = parentCtrl.getConst();
},

Вот как директивы tabs и pane работают на домашней странице Angular (пример «Создать компоненты»).

person Mark Rajcok    schedule 01.06.2013
comment
Это здорово, но кажется, что это значение копируется, а не привязывается (т.е. двусторонняя привязка данных) - person Doug Amos; 02.11.2015
comment
Согласитесь, это не двусторонняя привязка данных. Чтобы ответить на первоначальный вопрос, дочернему контроллеру просто нужно получить доступ к scope.$parent, и он имеет преимущества наследования и двусторонней привязки данных. - person CarbonDry; 11.04.2016
comment
Это потрясающий хороший и чистый ответ. - person akokani; 22.10.2017

Обычно доступ к родительской переменной области действия в директиве осуществляется через двунаправленную привязку (scope:{model:'=model'} — см. руководство по директивам angular) в конфигурации директив), но, поскольку вы используете включение, это не так просто. Если дочерняя директива всегда будет дочерней по отношению к родительской директиве, вы можете настроить ее так, чтобы она требовала родителя, а затем получить доступ к родительскому контроллеру в функции дочерней ссылки:

myApp.directive('parent', function() {
  return {
    scope: true,
    transclude: true,
    restrict: 'EA',
    template: '<div ng-transclude><h1>I\'m parent {{obj.prop}}<h1></div>',
    controller: function($scope) {
        $scope.SOME_CONST = 'someConst';
        this.SOME_CONST = $scope.SOME_CONST;
    }
  }
});

myApp.directive('child', function() {
  return {
    restrict: 'EA',
    require:'^parent',
    scope:true,
    link:function(scope,element,attrs,parentCtrl){
        scope.SOME_CONST = parentCtrl.SOME_CONST;
    },
    template: '<h1>I\'m child.... I want to access my parent\'s stuff, but I can\'t.  I can access MyCtrlScope though, see <b>{{obj.prop}}</b></h1> how can I access the <b>SOME_CONST</b> value in my parent\'s link function?  is this even a good idea? {{SOME_CONST}}.  I really don\'t want to put everything inside the MyCtrl',
  }
});

См. это обновление: http://jsfiddle.net/uN2uv/.

person joakimbl    schedule 31.05.2013

У меня была такая же проблема, и я, наконец, решил ее с помощью углового руководства;)

Вкратце: вам нужно использовать контроллер в вашей директиве parent и требовать этот контроллер в вашем дочерняя директива. Таким образом, вы можете получить свои родительские свойства.

См. https://docs.angularjs.org/guide/directive главу: Создание директив, которые взаимодействуют

Я изменил вашу скрипку, чтобы использовать контроллер, теперь вы можете получить доступ к своей константе: https://jsfiddle.net/bbrqdmt3/1/

var myApp = angular.module('myApp',[]);

function MyCtrl($scope) {
    $scope.obj = {prop:'foo'};
}

myApp.directive('parent', function() {
    return {
        scope: true,
        transclude: true,
        restrict: 'EA',
        template: '<div ng-transclude><h1>I\'m parent {{obj.prop}}<h1></div>',
        controller: function($scope) {
            this.getConst= function() {
                return 'someConst';
            }                        
        },
    }
});

myApp.directive('child', function() {
    return {
        restrict: 'EA',
        require : '^parent',
        link: function(scope, element, attrs, ctrl) {
            scope.value= ctrl.getConst();
        },
        template: '<h1>I\'m child.... I want to access my parent\'s stuff, but I can\'t.  I can access MyCtrlScope though, see <b>{{obj.prop}}</b></h1> how can I access the <b>SOME_CONST</b> value in my parent\'s link function?  is this even a good idea? {{value}}.  I really don\'t want to put everything inside the MyCtrl',
    }
});
person firebean    schedule 23.04.2015

В аргументах ссылки fn после контроллера есть преобразование fn.

myApp.directive('parent', function() {
  return {
    scope: true,
    transclude: true,
    restrict: 'EA',
    template: '<div><h1>I'm a parent header.</h1></div>',
    link: function (scope, el, attrs, ctrl, transclude) {

        transclude(scope, function (clone, scope) {
            element.append(clone); // <-- will transclude it's own scope
        });

    },
    controller: function($scope) {
        $scope.parent = {
            binding: 'I\'m a parent binding'
        };
    }
  }
});

myApp.directive('child', function() {
  return {
    restrict: 'EA',
    require:'^parent',
    scope:true,
    link:function(scope,element,attrs,parentCtrl){

    },
    template: '<div>{{parent.binding}}</div>' // <-- has access to parent's scope
  }
});
person cameronroe    schedule 08.11.2014