当标记位于单个文件中时,为什么我的嵌套指令不起作用?
Why don't my nested directives work when the markup is in a single file?
我一直在研究 isolateScope 指令,以更好地了解它们如何与其他嵌套的 isolateScope 指令交互,因此将 plnkr 放在一起来测试一些东西。
http://plnkr.co/edit/7Tl7GbWIovDSmVeKKN26?p=preview
这按预期工作。如您所见,每个指令都有自己独立的模板。
然后我决定将 html 从每个指令中移出并移到主 html 文件中,但是它停止工作了?我可以看到 e1Ctrl
在指令的范围内,但在处理包含的标记时它似乎不可用。
http://plnkr.co/edit/33Zz1oO4q7BVFw0cMvYa?p=preview
谁能告诉我为什么会这样?
------------更新------------
我简化了无法正常工作的 plunker 以清楚地显示问题。该指令使用 controllerAs
语法并且 e1Ctrl
明确设置在其 $scope
上(请参阅控制台输出)。
http://plnkr.co/edit/g2U2XskJDwWKuK3gqips?p=preview
angular
.module('app', [])
.controller('AppCtrl', AppCtrl)
.directive('elementOne', elementOne)
.controller('E1Ctrl', E1Ctrl)
function AppCtrl() {
var vm = this;
vm.data = [
{
label: 'one'
},
{
label: 'two'
},
{
label: 'three'
},
{
label: 'four'
}
];
vm.callback = function() {
console.log('called app callback');
};
}
function elementOne() {
return {
restrict: 'E',
scope: {
data: '=',
handler: '&'
},
controller: 'E1Ctrl',
controllerAs: 'e1Ctrl',
bindToController: true
}
}
function E1Ctrl($scope) {
console.log('E1Ctrl', $scope);
var vm = this;
vm.click = function() {
vm.handler();
};
vm.callback = function() {
console.log('called e1 callback');
};
}
加价:
<body ng-app="app" ng-controller="AppCtrl as appCtrl">
<ul>
<div ng-repeat='item in appCtrl.data'>
<element-one data='item' handler='appCtrl.callback()'>
<button ng-click='e1Ctrl.click()'>e1: {{e1Ctrl.data.label}}</button>
</element-one>
</div>
</ul>
</body>
------ 嵌入解决方案 -----
http://plnkr.co/edit/l3YvnKOYoNANteNXqRrA?p=preview
function elementOne() {
return {
restrict: 'E',
transclude: true,
scope: {
data: '=',
handler: '&'
},
controller: 'E1Ctrl',
link: function($scope, $element, $attr, ctrl, transclude) {
transclude($scope, function(clone){
$element.append(clone);
});
}
}
}
指令的 template
中的 HTML 和指令子树中的 HTML 的范围有所不同。前者在指令范围的上下文中进行评估;后者 - 在视图范围内。
如果指令有一个隔离作用域 - scope: {}
,则子树看不到它。如果它使用 scope: true
,那么它会为子树创建一个新的子范围,该子树的原型继承自视图的范围。
考虑以下几点:
// isolate scope
app.directive("foo", function(){
return {
scope: {},
link: function(scope){
scope.name = "foo";
}
}
});
// child scope
app.directive("bar", function(){
return {
scope: true,
link: function(scope){
scope.name = "bar";
}
}
});
app.controller("Main", function($scope){
$scope.name = "main";
});
视图的呈现方式如下:
<body ng-controller="MainCtrl">
<pre>in main: {{name}} will render "main"</pre>
<foo>
<pre>in subtree of foo: {{name}} will render "main"</pre>
</foo>
<bar>
<pre>in subtree of bar: {{name}} will render "bar"</pre>
</bar>
</body>
在您的情况下,子树是在视图的范围内计算的 - 而不是指令,这就是它无法按预期工作的原因。
编辑:
在某些情况下,在指令的隔离范围的上下文中评估子树可能是有意义的。我已经看到它与允许模板的指令一起使用。但要小心这一点,因为主视图的作者不应该(太多)了解指令的内部工作原理(即内部范围中公开的内容)。这也很难阅读,因为您会看到在外部范围内没有意义的变量。
要在指令的隔离范围内评估子树,指令需要 $compile
子树并 link 它针对其范围。
这是一个允许用户为列表中的每个项目提供模板的指令。 item
变量未在主作用域中定义,仅在指令的隔离作用域的上下文中才有意义:
<list src="items">
<item-template>
{{item.a}} | {{item.b}}
</item-template>
</list>
指令'list'如下:
app.directive("list", function($compile){
return {
scope: {
src: "="
},
link: {
pre: function(scope, element){
var itemTemplate = element.find("item-template");
element.empty();
var template = angular.element('<div ng-repeat="item in src"></div>')
.append(itemTemplate.html());
element.append(template);
$compile(element.contents())(scope);
}
}
}
});
我一直在研究 isolateScope 指令,以更好地了解它们如何与其他嵌套的 isolateScope 指令交互,因此将 plnkr 放在一起来测试一些东西。
http://plnkr.co/edit/7Tl7GbWIovDSmVeKKN26?p=preview
这按预期工作。如您所见,每个指令都有自己独立的模板。
然后我决定将 html 从每个指令中移出并移到主 html 文件中,但是它停止工作了?我可以看到 e1Ctrl
在指令的范围内,但在处理包含的标记时它似乎不可用。
http://plnkr.co/edit/33Zz1oO4q7BVFw0cMvYa?p=preview
谁能告诉我为什么会这样?
------------更新------------
我简化了无法正常工作的 plunker 以清楚地显示问题。该指令使用 controllerAs
语法并且 e1Ctrl
明确设置在其 $scope
上(请参阅控制台输出)。
http://plnkr.co/edit/g2U2XskJDwWKuK3gqips?p=preview
angular
.module('app', [])
.controller('AppCtrl', AppCtrl)
.directive('elementOne', elementOne)
.controller('E1Ctrl', E1Ctrl)
function AppCtrl() {
var vm = this;
vm.data = [
{
label: 'one'
},
{
label: 'two'
},
{
label: 'three'
},
{
label: 'four'
}
];
vm.callback = function() {
console.log('called app callback');
};
}
function elementOne() {
return {
restrict: 'E',
scope: {
data: '=',
handler: '&'
},
controller: 'E1Ctrl',
controllerAs: 'e1Ctrl',
bindToController: true
}
}
function E1Ctrl($scope) {
console.log('E1Ctrl', $scope);
var vm = this;
vm.click = function() {
vm.handler();
};
vm.callback = function() {
console.log('called e1 callback');
};
}
加价:
<body ng-app="app" ng-controller="AppCtrl as appCtrl">
<ul>
<div ng-repeat='item in appCtrl.data'>
<element-one data='item' handler='appCtrl.callback()'>
<button ng-click='e1Ctrl.click()'>e1: {{e1Ctrl.data.label}}</button>
</element-one>
</div>
</ul>
</body>
------ 嵌入解决方案 -----
http://plnkr.co/edit/l3YvnKOYoNANteNXqRrA?p=preview
function elementOne() {
return {
restrict: 'E',
transclude: true,
scope: {
data: '=',
handler: '&'
},
controller: 'E1Ctrl',
link: function($scope, $element, $attr, ctrl, transclude) {
transclude($scope, function(clone){
$element.append(clone);
});
}
}
}
指令的 template
中的 HTML 和指令子树中的 HTML 的范围有所不同。前者在指令范围的上下文中进行评估;后者 - 在视图范围内。
如果指令有一个隔离作用域 - scope: {}
,则子树看不到它。如果它使用 scope: true
,那么它会为子树创建一个新的子范围,该子树的原型继承自视图的范围。
考虑以下几点:
// isolate scope
app.directive("foo", function(){
return {
scope: {},
link: function(scope){
scope.name = "foo";
}
}
});
// child scope
app.directive("bar", function(){
return {
scope: true,
link: function(scope){
scope.name = "bar";
}
}
});
app.controller("Main", function($scope){
$scope.name = "main";
});
视图的呈现方式如下:
<body ng-controller="MainCtrl">
<pre>in main: {{name}} will render "main"</pre>
<foo>
<pre>in subtree of foo: {{name}} will render "main"</pre>
</foo>
<bar>
<pre>in subtree of bar: {{name}} will render "bar"</pre>
</bar>
</body>
在您的情况下,子树是在视图的范围内计算的 - 而不是指令,这就是它无法按预期工作的原因。
编辑:
在某些情况下,在指令的隔离范围的上下文中评估子树可能是有意义的。我已经看到它与允许模板的指令一起使用。但要小心这一点,因为主视图的作者不应该(太多)了解指令的内部工作原理(即内部范围中公开的内容)。这也很难阅读,因为您会看到在外部范围内没有意义的变量。
要在指令的隔离范围内评估子树,指令需要 $compile
子树并 link 它针对其范围。
这是一个允许用户为列表中的每个项目提供模板的指令。 item
变量未在主作用域中定义,仅在指令的隔离作用域的上下文中才有意义:
<list src="items">
<item-template>
{{item.a}} | {{item.b}}
</item-template>
</list>
指令'list'如下:
app.directive("list", function($compile){
return {
scope: {
src: "="
},
link: {
pre: function(scope, element){
var itemTemplate = element.find("item-template");
element.empty();
var template = angular.element('<div ng-repeat="item in src"></div>')
.append(itemTemplate.html());
element.append(template);
$compile(element.contents())(scope);
}
}
}
});