Как получить доступ к родительской области из пользовательской директивы * с помощью собственной области * в AngularJS?

Я ищу любой способ доступа к «родительской» области действия в директиве. Любая комбинация сферы действия, переключение, необходимость, передача в переменных (или сама область) сверху и т. Д. Я полностью готов наклониться назад, но я хочу избежать чего-то совершенно взломанного или недостижимого. Например, я знаю, что могу сделать это прямо сейчас, взяв параметр $scope из параметров preLink и итерации над его $sibling scopes, чтобы найти концептуальный «родительский».

Я действительно хочу, чтобы $watch выражение в родительской области. Если я смогу это сделать, тогда я смогу выполнить то, что я пытаюсь сделать здесь: AngularJS – Как отобразить частичное с переменными?

Важно отметить , что директива должна быть повторно использована в пределах одной и той же родительской области. Поэтому поведение по умолчанию (scope: false) не работает для меня. Мне нужна индивидуальная область действия для экземпляра директивы, а затем мне нужно, чтобы $watch переменную, которая живет в родительской области.

Пример кода составляет 1000 слов, поэтому:

 app.directive('watchingMyParentScope', function() { return { require: /* ? */, scope: /* ? */, transclude: /* ? */, controller: /* ? */, compile: function(el,attr,trans) { // Can I get the $parent from the transclusion function somehow? return { pre: function($s, $e, $a, parentControl) { // Can I get the $parent from the parent controller? // By setting this.$scope = $scope from within that controller? // Can I get the $parent from the current $scope? // Can I pass the $parent scope in as an attribute and define // it as part of this directive's scope definition? // What don't I understand about how directives work and // how their scope is related to their parent? }, post: function($s, $e, $a, parentControl) { // Has my situation improved by the time the postLink is called? } } } }; }); 

См. Каковы нюансы объема прототипа / прототипического наследования в AngularJS?

Подводя итог: способ обращения директивы к родительской ( $parent ) области зависит от типа области действия, создаваемой директивой:

  1. default ( scope: false ) – директива не создает новую область действия, поэтому здесь нет наследования. Область действия директивы совпадает с областью действия родителя / контейнера. В функции связи используйте первый параметр (обычно scope ).

  2. scope: true – директива создает новую дочернюю область, которая прототипически наследуется от родительской области. Свойства, определенные в родительской области, доступны для области scope директивы (из-за прототипального наследования). Просто остерегайтесь писать свойство примитивной области видимости – это создаст новое свойство в области директивы (которое скрывает / затеняет свойство родительской области с тем же именем).

  3. scope: { ... } – директива создает новую изоляцию / изолированную область. Он не прототипически наследует родительскую область. Вы все равно можете получить доступ к родительской области с помощью $parent , но это обычно не рекомендуется. Вместо этого вы должны указать свойства родительской области (и / или функции), необходимые директиве, с помощью дополнительных атрибутов в том же элементе, где используется директива, с использованием символов = , @ и & .

  4. transclude: true – директива создает новую «трансключенную» дочернюю область, которая прототипически наследуется от родительской области. Если в директиве также создается область изоляции, то трансифицированные и изоляционные области являются братьями и сестрами. $parent свойство каждой области ссылается на одну и ту же родительскую область.
    Угловое обновление версии v1.3 : если директива также создает область выделения, то трансграниченная область теперь является дочерним элементом области выделения. Заключенные и изолирующие области больше не являются братьями и сестрами. $parent свойство transcluded scope теперь ссылается на область выделения.

В приведенной выше ссылке приведены примеры и изображения всех 4 типов.

Вы не можете получить доступ к области в функции компиляции директивы (как указано здесь: https://github.com/angular/angular.js/wiki/Understanding-Directives ). Вы можете получить доступ к области директивы в функции связи.

Наблюдение:

Для 1. и 2. выше: обычно вы указываете, какое родительское свойство требуется директиве через атрибут, затем $ watch it:

 

 scope.$watch(attrs.attr1, function() { ... }); 

Если вы просматриваете свойство объекта, вам нужно использовать $ parse:

 

 var model = $parse(attrs.attr2); scope.$watch(model, function() { ... }); 

Для 3. выше (выделить область), посмотрите имя, которое вы даете свойству директивы, используя обозначение @ или = :

 

 scope: { localName3: '@attr3', attr4: '=' // here, using the same name as the attribute }, link: function(scope, element, attrs) { scope.$watch('localName3', function() { ... }); scope.$watch('attr4', function() { ... }); 

Доступ к методу controllerа означает доступ к методу в родительской области из директивного controllerа / ссылки / области.

Если директива разделяет / наследует родительскую область, то довольно просто просто вызвать метод родительской области.

Требуется немного больше работы, если вы хотите получить доступ к методу родительской области видимости из изолированной области действия.

Существует несколько опций (может быть больше, чем указано ниже), чтобы вызвать метод родительской области из изолированной области действия или просмотреть переменные родительской области ( опция № 6 специально).

Обратите внимание, что я использовал link function в этих примерах, но вы также можете использовать directive controller основанный на требовании.

Опция 1. Через Object literal и из шаблона html директивы

index.html

     AngularJS Plunker       

Hello {{name}}!

Directive Content

Selected Items (in parent controller) set to: {{selectedItemsReturnedFromDirective}}

itemfilterTemplate.html

  

app.js

 var app = angular.module('plunker', []); app.directive('sdItemsFilter', function() { return { restrict: 'E', scope: { items: '=', selectedItems: '=', selectedItemsChanged: '&' }, templateUrl: "itemfilterTemplate.html" } }) app.controller('MainCtrl', function($scope) { $scope.name = 'TARS'; $scope.selectedItems = ["allItems"]; $scope.selectedItemsChanged = function(selectedItems1) { $scope.selectedItemsReturnedFromDirective = selectedItems1; } $scope.items = [{ "id": "allItems", "name": "All Items", "order": 0 }, { "id": "CaseItem", "name": "Case Item", "model": "PredefinedModel" }, { "id": "Application", "name": "Application", "model": "Bank" }] }); 

рабочий plnkr: http://plnkr.co/edit/rgKUsYGDo9O3tewL6xgr?p=preview

Вариант № 2. Через Object literal и из директивной ссылки / области

index.html

     AngularJS Plunker       

Hello {{name}}!

Directive Content

Selected Items (in parent controller) set to: {{selectedItemsReturnedFromDirective}}

itemfilterTemplate.html

  

app.js

 var app = angular.module('plunker', []); app.directive('sdItemsFilter', function() { return { restrict: 'E', scope: { items: '=', selectedItems: '=', selectedItemsChanged: '&' }, templateUrl: "itemfilterTemplate.html", link: function (scope, element, attrs){ scope.selectedItemsChangedDir = function(){ scope.selectedItemsChanged({selectedItems:scope.selectedItems}); } } } }) app.controller('MainCtrl', function($scope) { $scope.name = 'TARS'; $scope.selectedItems = ["allItems"]; $scope.selectedItemsChanged = function(selectedItems1) { $scope.selectedItemsReturnedFromDirective = selectedItems1; } $scope.items = [{ "id": "allItems", "name": "All Items", "order": 0 }, { "id": "CaseItem", "name": "Case Item", "model": "PredefinedModel" }, { "id": "Application", "name": "Application", "model": "Bank" }] }); 

work plnkr: http://plnkr.co/edit/BRvYm2SpSpBK9uxNIcTa?p=preview

Вариант № 3. Через ссылку на функцию и из шаблона html директивы

index.html

     AngularJS Plunker       

Hello {{name}}!

Directive Content

Selected Items (in parent controller) set to: {{selectedItemsReturnFromDirective}}

itemfilterTemplate.html

  

app.js

 var app = angular.module('plunker', []); app.directive('sdItemsFilter', function() { return { restrict: 'E', scope: { items: '=', selectedItems:'=', selectedItemsChanged: '&' }, templateUrl: "itemfilterTemplate.html" } }) app.controller('MainCtrl', function($scope) { $scope.name = 'TARS'; $scope.selectedItems = ["allItems"]; $scope.selectedItemsChanged = function(selectedItems1) { $scope.selectedItemsReturnFromDirective = selectedItems1; } $scope.items = [{ "id": "allItems", "name": "All Items", "order": 0 }, { "id": "CaseItem", "name": "Case Item", "model": "PredefinedModel" }, { "id": "Application", "name": "Application", "model": "Bank" }] }); 

Работая plnkr: http://plnkr.co/edit/Jo6FcYfVXCCg3vH42BIz?p=preview

Вариант № 4. Через ссылку на функцию и из директивной ссылки / области

index.html

     AngularJS Plunker       

Hello {{name}}!

Directive Content

Selected Items (in parent controller) set to: {{selectedItemsReturnedFromDirective}}

itemfilterTemplate.html

  

app.js

 var app = angular.module('plunker', []); app.directive('sdItemsFilter', function() { return { restrict: 'E', scope: { items: '=', selectedItems: '=', selectedItemsChanged: '&' }, templateUrl: "itemfilterTemplate.html", link: function (scope, element, attrs){ scope.selectedItemsChangedDir = function(){ scope.selectedItemsChanged()(scope.selectedItems); } } } }) app.controller('MainCtrl', function($scope) { $scope.name = 'TARS'; $scope.selectedItems = ["allItems"]; $scope.selectedItemsChanged = function(selectedItems1) { $scope.selectedItemsReturnedFromDirective = selectedItems1; } $scope.items = [{ "id": "allItems", "name": "All Items", "order": 0 }, { "id": "CaseItem", "name": "Case Item", "model": "PredefinedModel" }, { "id": "Application", "name": "Application", "model": "Bank" }] }); 

рабочий plnkr: http://plnkr.co/edit/BSqx2J1yCY86IJwAnQF1?p=preview

Вариант № 5: через ng-модель и двустороннюю привязку вы можете обновить переменные родительской области. , Таким образом, в некоторых случаях вам может не потребоваться вызывать функции родительской области.

index.html

     AngularJS Plunker       

Hello {{name}}!

Directive Content

Selected Items (in parent controller) set to: {{selectedItems}}

itemfilterTemplate.html

  

app.js

 var app = angular.module('plunker', []); app.directive('sdItemsFilter', function() { return { restrict: 'E', scope: { items: '=', selectedItems: '=ngModel' }, templateUrl: "itemfilterTemplate.html" } }) app.controller('MainCtrl', function($scope) { $scope.name = 'TARS'; $scope.selectedItems = ["allItems"]; $scope.items = [{ "id": "allItems", "name": "All Items", "order": 0 }, { "id": "CaseItem", "name": "Case Item", "model": "PredefinedModel" }, { "id": "Application", "name": "Application", "model": "Bank" }] }); 

рабочий plnkr: http://plnkr.co/edit/hNui3xgzdTnfcdzljihY?p=preview

Вариант № 6: через $watch и $watchCollection Это двухсторонняя привязка для items во всех вышеприведенных примерах, если элементы изменены в родительской области, элементы в директиве также будут отражать изменения.

Если вы хотите смотреть другие атрибуты или объекты из родительской области, вы можете сделать это, используя $watch и $watchCollection как указано ниже

HTML

     AngularJS Plunker       

Hello {{user}}!

directive is watching name and current item

Id:
Name:
Model:

Directive Contents

Selected Items (in parent controller) set to: {{selectedItems}}

script app.js

var app = angular.module (‘plunker’, []);

 app.directive('sdItemsFilter', function() { return { restrict: 'E', scope: { name: '@', currentItem: '=', items: '=', selectedItems: '=ngModel' }, template: '', link: function(scope, element, attrs) { scope.$watchCollection('currentItem', function() { console.log(JSON.stringify(scope.currentItem)); }); scope.$watch('name', function() { console.log(JSON.stringify(scope.name)); }); } } }) app.controller('MainCtrl', function($scope) { $scope.user = 'World'; $scope.addItem = function() { $scope.items.push({ id: $scope.id, name: $scope.name, model: $scope.model }); $scope.currentItem = {}; $scope.currentItem.id = $scope.id; $scope.currentItem.name = $scope.name; $scope.currentItem.model = $scope.model; } $scope.selectedItems = ["allItems"]; $scope.items = [{ "id": "allItems", "name": "All Items", "order": 0 }, { "id": "CaseItem", "name": "Case Item", "model": "PredefinedModel" }, { "id": "Application", "name": "Application", "model": "Bank" }] }); 

Вы можете всегда ссылаться на документацию AngularJs для подробного объяснения директив.

  scope: false transclude: false 

и у вас будет такая же область действия (с родительским элементом)

 $scope.$watch(... 

Существует множество способов доступа к родительской области в зависимости от этой двух областей возможностей и перехода.

Вот трюк, который я использовал один раз: создайте «фиктивную» директиву для хранения родительской области и поместите ее где-нибудь за пределы желаемой директивы. Что-то вроде:

 module.directive('myDirectiveContainer', function () { return { controller: function ($scope) { this.scope = $scope; } }; }); module.directive('myDirective', function () { return { require: '^myDirectiveContainer', link: function (scope, element, attrs, containerController) { // use containerController.scope here... } }; }); 

а потом

 

Возможно, это не самое изящное решение, но оно выполнило свою работу.

Если вы используете синтаксис ES6 Classes и ControllerAs , вам нужно сделать что-то немного другое.

См. Ниже fragment и обратите внимание, что vm – это значение ControllerAs родительского controllerа, используемое в родительском HTML

 myApp.directive('name', function() { return { // no scope definition link : function(scope, element, attrs, ngModel) { scope.vm.func(...) 

Пробовав все, я наконец придумал решение.

Просто разместите в своем шаблоне следующее:

{{currentDirective.attr = parentDirective.attr; ''}}

Он просто пишет атрибут / переменную родительской области, к которой вы хотите получить доступ к текущей области.

Также обратите внимание ; '' ; '' в конце инструкции, убедитесь, что в вашем шаблоне нет вывода. (Угловое оценивает каждое утверждение, но выводит только последний).

Это немного взломанный, но после нескольких часов проб и ошибок он выполняет эту работу.

  • Установите фокус на первый недопустимый ввод в форме AngularJs
  • Доступ к атрибутам из директивы AngularJS
  • Переменные привязки от Service / Factory to Controllers
  • Директива AngularJS, связывающая функцию с несколькими аргументами
  • Как выбрать элемент по имени classа с помощью jqLite?
  • угловой ng-bind-html и директива внутри него
  • AngularJS оставляет комментарии в HTML: можно ли их удалить?
  • Экран загрузки Angularjs по запросу ajax
  • Пользовательская форма проверки валидации для сравнения двух полей
  • Почему свойство «replace» устарело в директивах AngularJS?
  • Угловые директивы - когда и как использовать компиляцию, controller, предварительную ссылку и пост-ссылку
  • Давайте будем гением компьютера.