向现有集合添加或修改对象时,ng-repeat within 指令不会更新
ng-repeat within directive will not update when adding or modifying objects to the existing collection
我有一个树视图,其中包含我试图填充的带有嵌套数组的对象。
A working plunker may be found here.
这里是代码结构的细分以及我到目前为止所做的尝试。
在我的 treeview.html 视图中,我正在加载我创建的 treeview 指令:
我目前有两个只是为了测试绑定。在选中事件复选框之前,不会填充第二个。
<H3>Tree View Sameple</H3>
<div>
<h3>Treeview 1</h3>
<tree src="tree.treeData" iobj="object" filter="tree.getAggregateInfo(object, isSelected)"></tree>
<h3>Treeview 2 once object has been updated</h3>
<tree src="tree.treeData2" iobj="object" filter="tree.getAggregateInfo(object, isSelected)"></tree>
</div>
<hr/>
<p>javascript object data</p>
{{tree.treeData}}
底部绑定只是显示js文件的输出。当您单击事件复选框时在树视图中,我正在模拟一个正在检索的新嵌套集合,该集合应作为子元素插入到树的事件节点下。
这是treeviewController.js
中使用的代码
(function () {
angular.module('app')
.controller('treeviewController', ['$log', '$scope', '$timeout', treeviewController]);
function treeviewController($log, $scope, $timeout) {
var vm = this;
//storage container for last node selected in tree
vm.lastNodeSelected = '';
vm.treeData2 = {};//test container to reflect object once updated
//original data not an arry but an object holding an array
vm.treeData =
{
"children": [
{
"name": "Document Type",
"children": [
{
"name": "Incident",
"documentType": "Document Type",
"aggregateUponField": "_ocommon.documenttype",
"parent": "",
"count": 2950,
"children": null
},
{
"name": "Some Event",
"documentType": "Document Type",
"aggregateUponField": "_ocommon.documenttype",
"parent": "",
"count": 2736,
"children": [{
"name": "Some Event Date",
"children": [
{
"name": "2008",
"documentType": "Incident Date",
"aggregateUponField": "dateincidentstart",
"parent": "",
"count": 451,
"children": null
},
{
"name": "2009",
"documentType": "Incident Date",
"aggregateUponField": "dateincidentstart",
"parent": "",
"count": 407,
"children": null
},
{
"name": "2010",
"documentType": "Incident Date",
"aggregateUponField": "dateincidentstart",
"parent": "",
"count": 426,
"children": null
}
]
}]
}
]
}
]
};
//aggregate data to be nested under incident
vm.newChildData = [
{
"name": "Incident Date",
"children": [
{
"name": "2008",
"documentType": "Incident Date",
"aggregateUponField": "dateincidentstart",
"parent": "",
"count": 451,
"children": null
},
{
"name": "2009",
"documentType": "Incident Date",
"aggregateUponField": "dateincidentstart",
"parent": "",
"count": 407,
"children": null
},
{
"name": "2010",
"documentType": "Incident Date",
"aggregateUponField": "dateincidentstart",
"parent": "",
"count": 426,
"children": null
}
]
}
];
var loadData = function () {
//stip out array
var children = vm.treeData.children;
//search array for matching parent
$log.info(vm.lastNodeSelected);
var cnt = children.length;
for (var i = 0; i < cnt; i++) {
if (children[i].children) {
var innerCnt = children[i].children.length;
for (var c = 0; c < innerCnt; c++) {
if (children[i].children[c].name === vm.lastNodeSelected) {
children[i].children[c].children = vm.newChildData;
}
}
}
}
//wrap back in object and assign to ngModel
vm.updatedList = { children };
// $log.info(updatedList);
vm.treeData2 = vm.updatedList;
$timeout(function() {
$scope.$apply(function() {
vm.treeData = vm.updatedList;
})} , 0);
}
vm.getAggregateInfo = function (node) {
vm.lastNodeSelected = node.name;
loadData();
// $log.info(vm.lastNodeSelected);
};
}
})();
以下是 treeview 指令。
angular.module('app')
.directive('tree', function () {
return {
restrict: 'E',
replace: true,
scope: {
t: '=src',
filter: '&'
},
template: '<ul><branch ng-repeat="c in t.children track by $index" src="c" filter="filter({ object: object, isSelected: isSelected })"></branch></ul>'
};
});
angular.module('app')
.directive('branch', function ($compile) {
return {
restrict: 'E',
replace: true,
scope: {
b: '=src',
filter: '&',
checked: '=ngModel'
},
template: '<li class="dir-tree"><input type="checkbox" ng-click="innerCall()" ng-model="b.$$hashKey" ng-change="stateChanged(b.$$hashKey)" />{{ b.name }} <span ng-hide="visible"> ({{ b.count }})</span></li>',
link: function (scope, element, attrs) {
var clicked = '';
var hasChildren = angular.isArray(scope.b.children);
scope.visible = hasChildren;
if (hasChildren) {
element.append('<tree src="b" filter="filter({ object: object, isSelected: isSelected })"></tree>');
$compile(element.contents())(scope);
}
element.on('click', function (event) {
event.stopPropagation();
if (hasChildren) {
element.toggleClass('collapsed');
}
});
scope.stateChanged = function (b) {
clicked = b;
};
scope.innerCall = function () {
scope.filter({ object: scope.b, isSelected: clicked });
};
}
};
});
我看到的是,当我单击事件复选框时,javascript 对象会更新。您可以在输出中看到这一点,也可以在选中复选框后看到绑定的第二个树视图。
我怀疑这个问题与事件的 $digest 周期有关,并且没有通知树实际更新。我已经尝试过,正如您在控制器中看到的那样,使用 $scope.$apply()
包裹在超时中,但这无法更新原始树。
对数组更新执行 .push()
之类的附加操作,但是尝试将新对象插入现有数组似乎不起作用。
关于如何让树更新和反映子对象有什么建议吗?
此处稍作修改 Plunker 以突出显示(并部分修复)问题。
我在 branch
指令中的 link
函数中添加了以下行:
scope.$watch('b', function (newvalue, oldvalue) {
if (!oldvalue.children && newvalue.children) {
scope.visible = true;
element.append('<tree src="b" filter="filter({ object: object, isSelected: isSelected })"></tree>');
$compile(element.contents())(scope);
}
}, true);
当树结构更新时,link
函数不会再次调用。因此,如果要添加新的子项,则不会为 Angular.
编译这些子项
我有一个树视图,其中包含我试图填充的带有嵌套数组的对象。
A working plunker may be found here.
这里是代码结构的细分以及我到目前为止所做的尝试。
在我的 treeview.html 视图中,我正在加载我创建的 treeview 指令: 我目前有两个只是为了测试绑定。在选中事件复选框之前,不会填充第二个。
<H3>Tree View Sameple</H3>
<div>
<h3>Treeview 1</h3>
<tree src="tree.treeData" iobj="object" filter="tree.getAggregateInfo(object, isSelected)"></tree>
<h3>Treeview 2 once object has been updated</h3>
<tree src="tree.treeData2" iobj="object" filter="tree.getAggregateInfo(object, isSelected)"></tree>
</div>
<hr/>
<p>javascript object data</p>
{{tree.treeData}}
底部绑定只是显示js文件的输出。当您单击事件复选框时在树视图中,我正在模拟一个正在检索的新嵌套集合,该集合应作为子元素插入到树的事件节点下。
这是treeviewController.js
中使用的代码(function () {
angular.module('app')
.controller('treeviewController', ['$log', '$scope', '$timeout', treeviewController]);
function treeviewController($log, $scope, $timeout) {
var vm = this;
//storage container for last node selected in tree
vm.lastNodeSelected = '';
vm.treeData2 = {};//test container to reflect object once updated
//original data not an arry but an object holding an array
vm.treeData =
{
"children": [
{
"name": "Document Type",
"children": [
{
"name": "Incident",
"documentType": "Document Type",
"aggregateUponField": "_ocommon.documenttype",
"parent": "",
"count": 2950,
"children": null
},
{
"name": "Some Event",
"documentType": "Document Type",
"aggregateUponField": "_ocommon.documenttype",
"parent": "",
"count": 2736,
"children": [{
"name": "Some Event Date",
"children": [
{
"name": "2008",
"documentType": "Incident Date",
"aggregateUponField": "dateincidentstart",
"parent": "",
"count": 451,
"children": null
},
{
"name": "2009",
"documentType": "Incident Date",
"aggregateUponField": "dateincidentstart",
"parent": "",
"count": 407,
"children": null
},
{
"name": "2010",
"documentType": "Incident Date",
"aggregateUponField": "dateincidentstart",
"parent": "",
"count": 426,
"children": null
}
]
}]
}
]
}
]
};
//aggregate data to be nested under incident
vm.newChildData = [
{
"name": "Incident Date",
"children": [
{
"name": "2008",
"documentType": "Incident Date",
"aggregateUponField": "dateincidentstart",
"parent": "",
"count": 451,
"children": null
},
{
"name": "2009",
"documentType": "Incident Date",
"aggregateUponField": "dateincidentstart",
"parent": "",
"count": 407,
"children": null
},
{
"name": "2010",
"documentType": "Incident Date",
"aggregateUponField": "dateincidentstart",
"parent": "",
"count": 426,
"children": null
}
]
}
];
var loadData = function () {
//stip out array
var children = vm.treeData.children;
//search array for matching parent
$log.info(vm.lastNodeSelected);
var cnt = children.length;
for (var i = 0; i < cnt; i++) {
if (children[i].children) {
var innerCnt = children[i].children.length;
for (var c = 0; c < innerCnt; c++) {
if (children[i].children[c].name === vm.lastNodeSelected) {
children[i].children[c].children = vm.newChildData;
}
}
}
}
//wrap back in object and assign to ngModel
vm.updatedList = { children };
// $log.info(updatedList);
vm.treeData2 = vm.updatedList;
$timeout(function() {
$scope.$apply(function() {
vm.treeData = vm.updatedList;
})} , 0);
}
vm.getAggregateInfo = function (node) {
vm.lastNodeSelected = node.name;
loadData();
// $log.info(vm.lastNodeSelected);
};
}
})();
以下是 treeview 指令。
angular.module('app')
.directive('tree', function () {
return {
restrict: 'E',
replace: true,
scope: {
t: '=src',
filter: '&'
},
template: '<ul><branch ng-repeat="c in t.children track by $index" src="c" filter="filter({ object: object, isSelected: isSelected })"></branch></ul>'
};
});
angular.module('app')
.directive('branch', function ($compile) {
return {
restrict: 'E',
replace: true,
scope: {
b: '=src',
filter: '&',
checked: '=ngModel'
},
template: '<li class="dir-tree"><input type="checkbox" ng-click="innerCall()" ng-model="b.$$hashKey" ng-change="stateChanged(b.$$hashKey)" />{{ b.name }} <span ng-hide="visible"> ({{ b.count }})</span></li>',
link: function (scope, element, attrs) {
var clicked = '';
var hasChildren = angular.isArray(scope.b.children);
scope.visible = hasChildren;
if (hasChildren) {
element.append('<tree src="b" filter="filter({ object: object, isSelected: isSelected })"></tree>');
$compile(element.contents())(scope);
}
element.on('click', function (event) {
event.stopPropagation();
if (hasChildren) {
element.toggleClass('collapsed');
}
});
scope.stateChanged = function (b) {
clicked = b;
};
scope.innerCall = function () {
scope.filter({ object: scope.b, isSelected: clicked });
};
}
};
});
我看到的是,当我单击事件复选框时,javascript 对象会更新。您可以在输出中看到这一点,也可以在选中复选框后看到绑定的第二个树视图。
我怀疑这个问题与事件的 $digest 周期有关,并且没有通知树实际更新。我已经尝试过,正如您在控制器中看到的那样,使用 $scope.$apply()
包裹在超时中,但这无法更新原始树。
对数组更新执行 .push()
之类的附加操作,但是尝试将新对象插入现有数组似乎不起作用。
关于如何让树更新和反映子对象有什么建议吗?
此处稍作修改 Plunker 以突出显示(并部分修复)问题。
我在 branch
指令中的 link
函数中添加了以下行:
scope.$watch('b', function (newvalue, oldvalue) {
if (!oldvalue.children && newvalue.children) {
scope.visible = true;
element.append('<tree src="b" filter="filter({ object: object, isSelected: isSelected })"></tree>');
$compile(element.contents())(scope);
}
}, true);
当树结构更新时,link
函数不会再次调用。因此,如果要添加新的子项,则不会为 Angular.