Может ли несколько директив для одного элемента разделять изолированную область?
Две директивы одного и того же элемента не могут иметь изолированную область видимости, но могут ли они использовать одну и ту же область, изолированную от родителя? И могут ли они использовать свойства, связанные с изолированной областью?
Например, если у меня есть две директивы для элемента
<eDirective aDirective prop="parentProp"/>
И одна директива определяет изолированную область со связанным свойством
App.directive('eDirective', function() {
return {
restrict: 'E',
scope: {
localProp: '=prop'
},
...
};
});
Получает ли другая директива эту область и может ли она использовать свойство bound?
App.directive('aDirective', function() {
return {
restrict: 'A',
link: function postLink(scope, element, attrs) {
scope.$watch('localProp', function(newProp, oldProp) {
...
}
},
...
};
});
Моя первоначальная попытка (в значительной степени закодированная, как указано выше) не удалась.
Ответы
Ответ 1
Я предлагаю вам использовать связь между контроллерами директив через свойство require вторичной директивы. Первая директива (e-директива) содержит изолированную область действия, тогда как вторая вспомогательная директива (a-директива) имеет ссылку на первую директиву и задает свойства через функции, определенные в первой директиве. Небольшой образец был бы (см. Plunker):
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="[email protected]" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js" data-semver="1.2.16"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div e-directive config="parentConfig" a-directive></div>
</body>
</html>
и javascript:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.parentConfig = {};
});
app.controller('ECtrl', function ( $scope ) {
this.setProp = function(newProp){$scope.config.prop = newProp;};
$scope.$watch('config', function(newProp, oldProp) {
console.log(oldProp, newProp);
});
});
app.directive('eDirective', function() {
return {
restrict: 'A',
scope: {
config: '='
},
controller: 'ECtrl',
link: function(scope, element, attrs) {
scope.config.prop ="abc";
}
};
});
app.directive('aDirective', function() {
return {
restrict: 'A',
require: 'eDirective',
link: function(scope, element, attrs,ctrl) {
ctrl.setProp("def");
}
};
});
Ответ 2
Вместо изолированной области действия директивы могут создавать новую дочернюю область, которая будет разделяться обеими директивами. Если вам нужно изменить parentProp
в директиве, введите и используйте $parse
:
<div ng-controller="MyCtrl">
<e-directive a-directive prop="parentProp"></e-directive>
</div>
JavaScript:
var app = angular.module('myApp', []);
app.controller('MyCtrl', function($scope) {
$scope.parentProp = { prop1: 'value1' };
});
app.directive('eDirective', function($parse) {
return {
restrict: 'E',
scope: true,
template: '<div>dir template: {{eDirLocalProp}}<br>'
+ '<a href ng-click="eDirChange()">change</a></div>',
link: function(scope, element, attrs) {
scope.eDirProp1 = 'dirPropValue';
var model = $parse(attrs.prop);
scope.eDirLocalProp = model(scope);
scope.eDirChange = function() {
scope.eDirLocalProp.prop1 = "new value";
};
}
};
});
app.directive('aDirective', function() {
return {
scope: true,
link: function postLink(scope, element, attrs) {
scope.$watchCollection(attrs.prop, function(newValue) {
console.log('aDirective', newValue);
});
},
};
});
fiddle
Если обе директивы должны создавать свойства в новой дочерней области, используйте какое-то соглашение об именах для предотвращения конфликтов имен. Например, scope.eDirProp1 = ...
и scope.aDirProp1 = ...
.
Ответ 3
Да, например, используя element.isolateScope()
(или см. fiddle):
HTML
<div ng-app="app" ng-controller="BaseController as baseCtrl">
<input type="text" ng-model="inputA.value" directive-config="{data: 'bar'}" >
<input type="text" ng-model="inputB.value" directive-config="{parsers: externalParser, data: 'buzz'}" custom-input >
<br><br>
<span style="font-style: italic; font-size: 12px; color: red;">*Open Console to view output</span>
</div>
JS
(function(angular){
"use strict";
angular.module("app", [])
.controller("BaseController", ['$scope', function($scope){
$scope.inputA = {value: "This is inputA"};
$scope.inputB = {value: "This is inputB"};
$scope.externalParser = function(value) {
console.log("...parsing value: ", value);
}
}])
.directive("input", [function() {
return {
restrict: "E",
require: '?ngModel',
scope: {
directiveConfig: "="
},
link: function(scope, element, attrs, ngModelCtrl) {
console.log("input directive - scope: ", scope);
console.log("input directive - scope.directiveConfig.data: ", scope.directiveConfig.data);
}
}
}])
.directive("customInput", [function() {
return {
restrict: "A",
require: '?ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
console.log("");
console.log("--------------------------------------------");
console.log("customInput directive - scope: ", scope);
// Use `element.isolateScope()`
var parentScope = element.isolateScope();
console.log("customInput directive - parentScope.directiveConfig.parsers: ", parentScope.directiveConfig.parsers);
console.log("customInput directive - parentScope.directiveConfig.data: ", parentScope.directiveConfig.data);
console.log("");
console.log("--------------------------------------------");
console.warn("DO NOT USE `$$childHead` as it may not target the element you are expecting; use `element.isolateScope()` instead.");
// DO NOT USE `$$childHead` as it may not be the element you expect
console.log("customInput directive - scope.$$childHead.directiveConfig.parsers: ", scope.$$childHead.directiveConfig.parsers);
console.log("customInput directive - scope.$$childHead.directiveConfig.data: ", scope.$$childHead.directiveConfig.data);
}
}
}])
;
})(angular)
вывод консоли
//input directive - scope: n {$id: 3, $$childTail: null, $$childHead: null, $$prevSibling: null, $$nextSibling: null…}
//input directive - scope.directiveConfig.data: bar
//input directive - scope: n {$id: 4, $$childTail: null, $$childHead: null, $$prevSibling: n, $$nextSibling: null…}
//input directive - scope.directiveConfig.data: buzz
//--------------------------------------------
//customInput directive - scope: b {$$childTail: n, $$childHead: n, $$nextSibling: null, $$watchers: Array[4], $$listeners: Object…}
//customInput directive - parentScope.directiveConfig.parsers: function (value) {
// console.log("...parsing value: ", value);
// }
//customInput directive - parentScope.directiveConfig.data: buzz
//--------------------------------------------
//DO NOT USE `$$childHead` as it may not target the element you are expecting; use `element.isolateScope()` instead.
//customInput directive - scope.$$childHead.directiveConfig.parsers: undefined
//customInput directive - scope.$$childHead.directiveConfig.data: bar