我正在尝试使用anugularJS生成一个n级分层无序列表,并且已经能够成功完成。但是现在,我在指令和控制器之间存在范围问题。我需要在指令模板中通过ng-click调用的函数中更改父类的scope属性。
见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),但更改仅在本地进行。当我单击某个项目时,预期的行为是“Selected:None”应更改为“Selected:xxx”,其中xxx是单击的项目。
我是否正确地绑定父范围和指令之间的属性?如何将属性更改传递给父作用域?
希望这很清楚。
提前感谢您的帮助。
答案 0 :(得分:18)
请看看这个工作小提琴,http://jsfiddle.net/eeuSv/
我所做的是要求navtree-node
指令内的父控制器,并调用该控制器中定义的成员函数。
成员函数是setSelected
。请注意其this.setSelected
而非$scope.setSelected
。
然后定义navtree-node
范围方法itemSelect
。当您单击锚标记时,它将调用itemSelect
范围内的navtree-node
方法。此转换将调用控制器成员方法setSelected
传递所选的id。
scope.itemSelect = function(id){
myGreatParentControler.setSelected(id)
}
答案 1 :(得分:11)
Maxdec是对的,这与范围有关。不幸的是,这个案例非常复杂,以至于AngularJS文档可能会误导初学者(比如我自己)。
警告:在我试图解释这个问题时,让我感到有些啰嗦。如果您只想查看代码,请转到此JSFiddle。我还发现egghead.io视频在学习AngularJS方面非常宝贵。
以下是我对该问题的理解:您有一个指令层次结构(navtree,navitem),并且您希望将信息从navitem“从树上”传递到根控制器。 AngularJS,就像编写良好的Javascript一样,设置为严格控制变量的范围,这样你就不会意外地弄乱在页面上运行的其他脚本。
Angular中有一个特殊的语法(&
),它允许您创建隔离范围并在父范围上调用函数:
// in your directive
scope: {
parentFunc: '&'
}
到目前为止一切顺利。当你有多个指令级别时,事情变得棘手,因为你基本上想要做以下事情:
问题是,子级指令无法看到根控制器。我的理解是你必须在你的指令结构中建立一个“链”,其作用如下:
首先:在根控制器中有一个函数返回一个函数(它引用了根视图控制器的作用域):
$scope.selectFunctionRoot = function () {
return function (ID) {
$scope.itemselected = ID;
}
}
第二:设置中级指令,使其拥有自己的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);
};
这是更新的fork of your JSFiddle,其中包含代码中的注释。
答案 2 :(得分:3)
这可能是因为每个指令都创建了自己的范围(实际上你告诉他们这样做) 您可以阅读有关指令here的更多信息,尤其是“编写指令(长版本)”一章。
范围 - 如果设置为:
true - 然后将为此指令创建新范围。如果 同一元素上的多个指令请求一个新的范围,只有一个 新范围已创建。新范围规则不适用于根 因为模板的根总是得到一个新的模板 范围。
{}(对象哈希) - 然后创建一个新的“隔离”范围。该 'isolate'范围与正常范围不同,因为它没有 原型继承自父范围。这很有用 创建可重复使用的组件,不应该意外地读取或 修改父范围内的数据。
因此,您所做的更改不会反映在MyCtrl范围内,因为每个指令都有自己的“隔离”范围。
这就是为什么点击只会更改本地$scope.itemselected
变量而不是“全部”变量。