创建新范围时,具有子指令的父级将不起作用
parent with child directives won't work when creating new scope
我创建了一个父指令,它可以从给定的 URL 检索数据,效果很好。
作为第二步,我想在几个子指令之间分发数据,例如:
<parent data="some/url/with/json/response">
<child data="data[0]"></child>
<child data="data[1]"></child>
...
</parent>
这也很好用。但是,当创建另一组指令时,旧数据会被覆盖,因为父指令不会创建新范围,而是会覆盖主范围中的所有内容。在指令中指定以下行之一时,根本不显示任何数据(现在出现错误消息):
scope: true,
//or
scope: {},
这里有一个错误情况的例子:http://plnkr.co/edit/GWcLrhaUHNLPWNkOHIS8?p=preview
顶部应该有 "This is working"。
所以问题是:有谁知道我如何强制父指令创建一个所有子元素都可以访问的新范围?
使用:
scope: false
在您的子范围中保留子元素中父元素的范围。
使用 true 或 {} 称为 isolateScope,这是有原因的。保护子作用域。
问题在于父指令是继承作用域(控制器的作用域),因此所有父指令本质上共享相同的作用域。当两个父级共享相同的作用域时,最后执行的指令(没有 "working" 属性的父级指令)将在共享作用域上定义 'working' 属性,在 link 实现,分配 $scope.working = "this is not working".
<!-- bind object to child scope -->
<parent-bind-scope working=true>
Should be working:
<child-bind-scope data="working"></child-bind-scope>
</parent-bind-scope>
<br>
<parent-bind-scope>
Should not be working:
<child-bind-scope data="working"></child-bind-scope>
</parent-bind-scope>
如果在分配 $scope.working 时发出警报,您可以看到它正在执行 if 条件的两个分支(else 控制流,在最后):
controller: function($scope, $attrs) {
if($attrs.working) {
alert('working attribute exists');
$scope.working = {key: 'this is working'};
} else {
alert('working attribute does not exist');
$scope.working = {key: 'this is not working'};
}
}
在您的子指令中,您应该在父范围内评估字符串,如下所示:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app.directive('parentBindScope', function($timeout) {
return {
restrict: 'E',
transclude: true,
scope: true,
template: '<div ng-transclude></div>',
controller: function($scope, $attrs) {
if($attrs.working) {
$scope.working = {key: 'this is working'};
} else {
$scope.working = { key: 'something else' }
$timeout(function(){
$scope.working = {key: 'this is not working'};
},1000)
}
}
}
});
app.directive('childBindScope', function($parse) {
return {
restrict: 'E',
template: '<div>child-template: {{data}}</div>',
controller: function($scope, $attrs) {
$scope.$watch('$parent.' + $attrs.data, function() {
$scope.data = $scope.$parent.$eval($attrs.data);
});
},
link: function(scope, element, attrs) {
console.log('data: ' + JSON.stringify(scope.data));
}
}
})
我终于找到了一个看起来也不错的解决方案。因为plunker默认是用angular1.2创建脚本的,所以之前没找到这个方案(貌似只在1.3.x下有效)。现在每个父指令确实创建了自己的作用域,它的子指令也这样做。然后,通过使用此表示法(使用 $parent),显示正确的数据:
<parent-bind-scope working=true>
Should be working:
<child-bind-scope data="$parent.working"></child-bind-scope>
</parent-bind-scope>
另见:http://plnkr.co/edit/NkVxpjryD8YQm9xovw4M?p=preview
当使用 Remco 的答案并为子级创建一个独立的作用域时,我不得不使用 $parent.$parent 来获得相同的结果,所以这是朝着这个解决方案迈出的重要一步。
补充:虽然使用 ng-repeat 时很奇怪,但在我的真实代码中,我现在必须像这样 $parent.$parent.$parent 才能让它工作:
//not the exact code
<parent parentdata="/url/with/data">
<div ng-repeat="i in $parent.range($parentdata.data.cols.length) track by $index">
<child data1="$parent.$parent.$parent.parentdata.cols[$index]"
data2="$parent.$parent.$parent.parentdata.rows[$index]"/>
</div>
</parent>
我创建了一个父指令,它可以从给定的 URL 检索数据,效果很好。 作为第二步,我想在几个子指令之间分发数据,例如:
<parent data="some/url/with/json/response">
<child data="data[0]"></child>
<child data="data[1]"></child>
...
</parent>
这也很好用。但是,当创建另一组指令时,旧数据会被覆盖,因为父指令不会创建新范围,而是会覆盖主范围中的所有内容。在指令中指定以下行之一时,根本不显示任何数据(现在出现错误消息):
scope: true,
//or
scope: {},
这里有一个错误情况的例子:http://plnkr.co/edit/GWcLrhaUHNLPWNkOHIS8?p=preview 顶部应该有 "This is working"。
所以问题是:有谁知道我如何强制父指令创建一个所有子元素都可以访问的新范围?
使用:
scope: false
在您的子范围中保留子元素中父元素的范围。
使用 true 或 {} 称为 isolateScope,这是有原因的。保护子作用域。
问题在于父指令是继承作用域(控制器的作用域),因此所有父指令本质上共享相同的作用域。当两个父级共享相同的作用域时,最后执行的指令(没有 "working" 属性的父级指令)将在共享作用域上定义 'working' 属性,在 link 实现,分配 $scope.working = "this is not working".
<!-- bind object to child scope -->
<parent-bind-scope working=true>
Should be working:
<child-bind-scope data="working"></child-bind-scope>
</parent-bind-scope>
<br>
<parent-bind-scope>
Should not be working:
<child-bind-scope data="working"></child-bind-scope>
</parent-bind-scope>
如果在分配 $scope.working 时发出警报,您可以看到它正在执行 if 条件的两个分支(else 控制流,在最后):
controller: function($scope, $attrs) {
if($attrs.working) {
alert('working attribute exists');
$scope.working = {key: 'this is working'};
} else {
alert('working attribute does not exist');
$scope.working = {key: 'this is not working'};
}
}
在您的子指令中,您应该在父范围内评估字符串,如下所示:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app.directive('parentBindScope', function($timeout) {
return {
restrict: 'E',
transclude: true,
scope: true,
template: '<div ng-transclude></div>',
controller: function($scope, $attrs) {
if($attrs.working) {
$scope.working = {key: 'this is working'};
} else {
$scope.working = { key: 'something else' }
$timeout(function(){
$scope.working = {key: 'this is not working'};
},1000)
}
}
}
});
app.directive('childBindScope', function($parse) {
return {
restrict: 'E',
template: '<div>child-template: {{data}}</div>',
controller: function($scope, $attrs) {
$scope.$watch('$parent.' + $attrs.data, function() {
$scope.data = $scope.$parent.$eval($attrs.data);
});
},
link: function(scope, element, attrs) {
console.log('data: ' + JSON.stringify(scope.data));
}
}
})
我终于找到了一个看起来也不错的解决方案。因为plunker默认是用angular1.2创建脚本的,所以之前没找到这个方案(貌似只在1.3.x下有效)。现在每个父指令确实创建了自己的作用域,它的子指令也这样做。然后,通过使用此表示法(使用 $parent),显示正确的数据:
<parent-bind-scope working=true>
Should be working:
<child-bind-scope data="$parent.working"></child-bind-scope>
</parent-bind-scope>
另见:http://plnkr.co/edit/NkVxpjryD8YQm9xovw4M?p=preview
当使用 Remco 的答案并为子级创建一个独立的作用域时,我不得不使用 $parent.$parent 来获得相同的结果,所以这是朝着这个解决方案迈出的重要一步。
补充:虽然使用 ng-repeat 时很奇怪,但在我的真实代码中,我现在必须像这样 $parent.$parent.$parent 才能让它工作:
//not the exact code
<parent parentdata="/url/with/data">
<div ng-repeat="i in $parent.range($parentdata.data.cols.length) track by $index">
<child data1="$parent.$parent.$parent.parentdata.cols[$index]"
data2="$parent.$parent.$parent.parentdata.rows[$index]"/>
</div>
</parent>