Как получить доступ к родительской области из пользовательской директивы * с помощью собственной области * в AngularJS?
Я ищу любой способ доступа к «родительской» области действия в директиве. Любая комбинация сферы действия, переключение, необходимость, передача в переменных (или сама область) сверху и т. Д. Я полностью готов наклониться назад, но я хочу избежать чего-то совершенно взломанного или недостижимого. Например, я знаю, что могу сделать это прямо сейчас, взяв параметр $scope
из параметров preLink и итерации над его $sibling
scopes, чтобы найти концептуальный «родительский».
Я действительно хочу, чтобы $watch
выражение в родительской области. Если я смогу это сделать, тогда я смогу выполнить то, что я пытаюсь сделать здесь: AngularJS – Как отобразить частичное с переменными?
Важно отметить , что директива должна быть повторно использована в пределах одной и той же родительской области. Поэтому поведение по умолчанию (scope: false) не работает для меня. Мне нужна индивидуальная область действия для экземпляра директивы, а затем мне нужно, чтобы $watch
переменную, которая живет в родительской области.
- Угловая динамическая проверка ng-шаблонов
- Несколько директив , запрашивающих новую / выделенную область
- Связь с компилятором и controllerом
- Как реализовать ng-изменение для пользовательской директивы
- Как автокапитализировать первый символ в поле ввода в AngularJS?
Пример кода составляет 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? } } } }; });
- Когда в пользу ng-if vs. ng-show / ng-hide?
- Каков наилучший способ условного применения атрибутов в AngularJS?
- Предварительный просмотр изображения перед загрузкой Angularjs
- Как понять «терминал» директивы?
- AngularJS. Преобразование значения тега (время Unix для чтения человеком)
- Отложить угловое выполнение часов после $ digest (повышение DOM-события)
- AngularJS: ngInclude vs директива
- $ apply vs $ digest при тестировании директивы
См. Каковы нюансы объема прототипа / прототипического наследования в AngularJS?
Подводя итог: способ обращения директивы к родительской ( $parent
) области зависит от типа области действия, создаваемой директивой:
-
default (
scope: false
) – директива не создает новую область действия, поэтому здесь нет наследования. Область действия директивы совпадает с областью действия родителя / контейнера. В функции связи используйте первый параметр (обычноscope
). -
scope: true
– директива создает новую дочернюю область, которая прототипически наследуется от родительской области. Свойства, определенные в родительской области, доступны для областиscope
директивы (из-за прототипального наследования). Просто остерегайтесь писать свойство примитивной области видимости – это создаст новое свойство в области директивы (которое скрывает / затеняет свойство родительской области с тем же именем). -
scope: { ... }
– директива создает новую изоляцию / изолированную область. Он не прототипически наследует родительскую область. Вы все равно можете получить доступ к родительской области с помощью$parent
, но это обычно не рекомендуется. Вместо этого вы должны указать свойства родительской области (и / или функции), необходимые директиве, с помощью дополнительных атрибутов в том же элементе, где используется директива, с использованием символов=
,@
и&
. -
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; ''}}
Он просто пишет атрибут / переменную родительской области, к которой вы хотите получить доступ к текущей области.
Также обратите внимание ; ''
; ''
в конце инструкции, убедитесь, что в вашем шаблоне нет вывода. (Угловое оценивает каждое утверждение, но выводит только последний).
Это немного взломанный, но после нескольких часов проб и ошибок он выполняет эту работу.