提问人:user2165994 提问时间:3/13/2013 最后编辑:user2165994 更新时间:3/14/2013 访问量:40072
如何使用 angularJS 正确绑定指令和控制器之间的范围
How to properly bind scope between directive and controller with angularJS
问:
我正在尝试使用 anugularJS 生成一个 n 级分层无序列表,并且已经能够成功做到这一点。但是现在,我在指令和控制器之间遇到了范围问题。我需要从指令模板中通过 ng-click 调用的函数中更改父级的作用域属性。
查看 http://jsfiddle.net/ahonaker/ADukg/2046/ - 这是 JS
var app = angular.module('myApp', []);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
$scope.itemselected = "None";
$scope.organizations = {
"_id": "SEC Power Generation",
"Entity": "OPUNITS",
"EntityIDAttribute": "OPUNIT_SEQ_ID",
"EntityID": 2,
"descendants": ["Eastern Conf Business Unit", "Western Conf Business Unit", "Atlanta", "Sewanee"],
children: [{
"_id": "Eastern Conf Business Unit",
"Entity": "",
"EntityIDAttribute": "",
"EntityID": null,
"parent": "SEC Power Generation",
"descendants": ["Lexington", "Columbia", "Knoxville", "Nashville"],
children: [{
"_id": "Lexington",
"Entity": "OPUNITS",
"EntityIDAttribute": "OPUNIT_SEQ_ID",
"EntityID": 10,
"parent": "Eastern Conf Business Unit"
}, {
"_id": "Columbia",
"Entity": "OPUNITS",
"EntityIDAttribute": "OPUNIT_SEQ_ID",
"EntityID": 12,
"parent": "Eastern Conf Business Unit"
}, {
"_id": "Knoxville",
"Entity": "OPUNITS",
"EntityIDAttribute": "OPUNIT_SEQ_ID",
"EntityID": 14,
"parent": "Eastern Conf Business Unit"
}, {
"_id": "Nashville",
"Entity": "OPUNITS",
"EntityIDAttribute": "OPUNIT_SEQ_ID",
"EntityID": 4,
"parent": "Eastern Conf Business Unit"
}]
}]
};
$scope.itemSelect = function (ID) {
$scope.itemselected = ID;
}
}
app.directive('navtree', function () {
return {
template: '<ul><navtree-node ng-repeat="item in items" item="item" itemselected="itemselected"></navtree-node></ul>',
restrict: 'E',
replace: true,
scope: {
items: '='
}
};
});
app.directive('navtreeNode', function ($compile) {
return {
restrict: 'E',
template: '<li><a ng-click="itemSelect(item._id)">{{item._id}} - {{itemselected}}</a></li>',
scope: {
item: "=",
itemselected: '='
},
controller: 'MyCtrl',
link: function (scope, elm, attrs) {
if ((angular.isDefined(scope.item.children)) && (scope.item.children.length > 0)) {
var children = $compile('<navtree items="item.children"></navtree>')(scope);
elm.append(children);
}
}
};
});
这是 HTML
<div ng-controller="MyCtrl">
Selected: {{itemselected}}
<navtree items="organizations.children"></navtree>
</div>
请注意,该列表是从模型生成的。ng-click 调用函数来设置父作用域属性 (itemselected),但更改仅在本地发生。当我单击某个项目时,预期行为是“已选择:无”应更改为“已选择:xxx”,其中 xxx 是单击的项目。
我是否没有适当地绑定父作用域和指令之间的属性?如何将属性更改传递到父范围?
希望这是清楚的。
提前感谢您的任何帮助。
答:
这可能是因为每个指令都创建了自己的范围(实际上你告诉他们这样做)。
您可以在此处阅读有关指令的更多信息,尤其是“编写指令(长版)”一章。
scope - 如果设置为:
true - 然后将为此指令创建一个新作用域。如果 同一元素上的多个指令请求一个新作用域,只有一个 将创建新的范围。新的范围规则不适用于根 的模板,因为模板的根总是得到一个新的 范围。
{} (object hash) - 然后创建一个新的“隔离”范围。这 “隔离”范围与普通范围的不同之处在于,它没有 原型继承自父作用域。这在以下情况下很有用 创建可重用的组件,这些组件不应意外读取或 修改父作用域中的数据。
因此,您所做的更改不会反映在 MyCtrl 作用域中,因为每个指令都有自己的“隔离”作用域。
这就是为什么点击只会改变局部变量,而不是“全部”变量。$scope.itemselected
评论
scope: true
model.prop1
prop1
Maxdec 是对的,这与范围界定有关。不幸的是,这是一个足够复杂的情况,以至于 AngularJS 文档可能会误导初学者(比如我自己)。
警告:当我试图解释这一点时,请准备好我有点啰嗦。如果您只想查看代码,请转到此 JSFiddle。我还发现 egghead.io 视频对于学习 AngularJS 非常宝贵。
这是我对这个问题的理解:你有一个指令层次结构(navtree、navitem),你想把信息从导航项“树上”传递到根控制器。AngularJS 与一般编写良好的 Javascript 一样,被设置为严格控制变量的范围,这样您就不会意外地弄乱页面上运行的其他脚本。
Angular 中有一个特殊的语法 (),它允许你创建一个隔离的范围,并在父范围上调用一个函数:&
// in your directive
scope: {
parentFunc: '&'
}
目前为止,一切都好。当您有多个级别的指令时,事情会变得棘手,因为您本质上想要执行以下操作:
- 在根控制器中有一个接受变量的函数并更新模型
- 中级指令
- 可以与根控制器通信的子级指令
问题是,子级指令看不到根控制器。我的理解是,您必须在指令结构中设置一个“链”,其作用如下:
第一:在根控制器中有一个函数,该函数返回一个函数(该函数引用了根视图控制器的作用域):
$scope.selectFunctionRoot = function () {
return function (ID) {
$scope.itemselected = ID;
}
}
第二:将 mid-level 指令设置为具有自己的 select 函数(它将传递给子函数),该函数返回如下内容。请注意,我们必须保存中级指令的作用域,因为当实际执行此代码时,它将在子级指令的上下文中:
// in the link function of the mid-level directive. the 'navtreelist'
scope.selectFunctionMid = function () {
// if we don't capture our mid-level scope, then when we call the function in the navtreeNode it won't be able to find the mid-level-scope's functions
_scope = scope;
return function (item_id) {
console.log('mid');
console.log(item_id);
// this will be the "root" select function
parentSelectFunction = _scope.selectFunction();
parentSelectFunction(item_id);
};
};
第三:在子级指令 () 中,将一个函数绑定到调用本地函数的函数,该函数又会一直“调用链”到根控制器:navtreeNode
ng-click
// in 'navtreeNode' link function
scope.childSelect = function (item_id) {
console.log('child');
console.log(item_id);
// this will be the "mid" select function
parentSelectFunction = scope.selectFunction();
parentSelectFunction(item_id);
};
下面是 JSFiddle 的更新分支,其中代码中有注释。
评论
请看一下这把工作小提琴,http://jsfiddle.net/eeuSv/
我所做的是要求指令中的父控制器,并调用该控制器中定义的成员函数。
成员函数为 。请注意,它不是 .
然后定义一个 scope 方法。单击锚标记时,它将调用作用域上的方法。这反过来将调用传递所选 id 的 controllers 成员方法。navtree-node
setSelected
this.setSelected
$scope.setSelected
navtree-node
itemSelect
itemSelect
navtree-node
setSelected
scope.itemSelect = function(id){
myGreatParentControler.setSelected(id)
}
评论
require: "^ngController"
require: "^ngController"
MyCtrl
navtree
require: '^ngController'
上一个:指令和变量
下一个:使用指令和部分专用化
评论