这是一个相当复杂的问题。我试图将代码尽可能地改进为仅演示问题所需的代码。这是一个很长的帖子 - 考虑一下自己被警告了!
这个问题的工作演示可以在这个jsFiddle中看到。
我要做的是根据数组中项目的共同属性将数组聚合成组。实际上将数组聚合成组是相当简单的:
function ViewModel() {
var self = this;
this.groupProperty = ko.observable('groupId');
this.data = ko.observableArray();
this.view = ko.computed(function () {
var i, element, value, grouped = {};
for (i = 0; i < self.data().length; i++) {
element = self.data()[i];
if (!element.hasOwnProperty(self.groupProperty()))
continue; //Exclude objects that don't have the grouping property...
value = ko.isObservable(element[self.groupProperty()]) ? element[self.groupProperty()]() : element[self.groupProperty];
if (!grouped.hasOwnProperty(value))
grouped[value] = new Group(value);
grouped[value].items().push(element);
}
return transformObjectIntoArray(grouped);
});
}
transformObjectIntoArray
可以在 jsFiddle 示例中看到。它只是通过基本摆脱属性将一个对象变成一个数组。
这需要一组数据,例如:
[
Item { label: 'Item 1', groupId: 'A' },
Item { label: 'Item 2', groupId: 'A' },
Item { label: 'Item 1', groupId: 'B' },
Item { label: 'Item 2', groupId: 'B' }
]
将其转换为以下对象:
{
'A': [
Item { label: 'Item 1', groupId: 'A' },
Item { label: 'Item 2', groupId: 'A' }
],
'B': [
Item { label: 'Item 1', groupId: 'B' },
Item { label: 'Item 2', groupId: 'B' }
]
}
然后将其转换为以下数组:
[
[
Item { label: 'Item 1', groupId: 'A' },
Item { label: 'Item 2', groupId: 'A' }
],
[
Item { label: 'Item 1', groupId: 'B' },
Item { label: 'Item 2', groupId: 'B' }
]
]
到目前为止,一切都按预期工作,如 jsFiddle 所示。
当我向数据数组添加一个新元素时,问题就开始了。vm
是我ViewModel
在以下代码中的实例
vm.data.push(new Item('Item X', 'A')); //Add a new item called 'Item X' to the 'A' group
正如您可能已经猜到的那样,这会导致计算的 observableview
执行,从而重新聚合底层数据数组。所有新Group
对象都被创建,这意味着与它们相关的任何状态信息都将丢失。
这对我来说是个问题,因为我存储了它们是否应该在Group
对象上展开或折叠的状态:
function Group(label) {
this.expanded = ko.observable(false); //Expanded state is stored here
this.items = ko.observableArray();
this.label = ko.observable(label);
}
因此,由于它们被重新创建,该状态信息就会丢失。因此,所有组都恢复到其默认状态(折叠)。
我知道我面临的问题。但是,我正在努力想出一个并不笨拙的解决方案。我想到的可能的解决方案包括:
维护组状态图
我的第一个想法是创建一个可以用作地图的对象。它将使用 groupId 作为键名,状态将是值:
{
'A': GroupState { expanded: true },
'B': GroupState { expanded: false }
}
即使每次添加元素时都会重新创建组,但GroupState
仍会持续存在。我无法解决的一个问题是如何从GroupState
地图中删除不再存在的组。
例如,
vm.data(someNewArray);
与当前地图someNewArray
中的项目相对应的没有 groupIds 的项目数组在哪里。GroupState
我将如何删除不再有参考的条目?这似乎是我的应用程序中的内存泄漏。
这个问题可以在这个jsFiddle看到。注意,单击按钮后,组状态大小增长到 5 个元素,但只有 3 个组。这是因为尽管不再使用原始的 2 组,但并未删除。
维护一个 viewState 数组并移除计算的 observable
我的第二个想法是删除计算的 observable view
。相反,我有第二个可观察的数组,称为它被聚合到当前“视图”之后viewState
的数组。data
然而,我很快就遇到了这个想法的问题。
首先,我必须编写几个方法来维护两个数组之间的状态:add()
、remove()
、clear()
等。考虑到必须这样做,我立即开始质疑拥有第二个数组是否是个好主意。
其次,删除计算数组意味着线性搜索viewState
数组以查看是否有任何当前元素包含与传入项相似的 groupId。虽然在实践中,这会非常快,但我不喜欢在每个 add ( O(n)
vs O(1)
) 上迭代整个数组的理论。在路上,我可能会处理成千上万的项目。
我觉得可能有一个简单的解决方案,但我正在努力解决这个问题。有没有人有任何想法可以帮助解决这个问题,或者知道用 KnockoutJS 完成这个更好的方法?也许,我上面的一个想法会增加一些洞察力(我仍然觉得GroupState
地图在正确的轨道上)
如果我遗漏了一些重要信息,请告诉我,我会尽力补充