1。数据
让我们首先以更易读的格式探索您提供的数据:
主要
{
ID: 4,
mainDescription: "foo foo foo",
SortOrder: "1"
}
子
{
ID: 21,
SubDescription: "bar bar bar",
SortOrder: "1.1"
}
2。视图模型
从您想要的输出中,我们了解到我们需要:
AppViewModel
{
mains: [ /* MainViewModel */ ]
}
一个MainViewModel
{
title: "foo foo foo",
numberLabel: "1",
subs: [ /* SubViewModel */ ],
// The label as a number
sortOrder: parseInt("1", 10)
}
一个SubViewModel
{
title: "bar bar bar",
numberLabel: "1.1",
// We sort by the second number in the label
sortOrder: parseInt("1.1".split(".")[1], 10),
// We group by the first part of the label
groupKey: "1.1".split(".")[0]
}
3。意见
使用建议的视图模型结构,我们可以创建视图:
<ul data-bind="foreach: mains">
<li>
<p data-bind="text: title + ' ' + numberLabel">
<ul data-bind="foreach: subs">
<li data-bind="text: title + ' ' + numberLabel"></li>
</ul>
</li>
</ul>
4。对视图模型实例进行排序
因为我们有数字 sortKey 属性,我们可以做一个数字比较来排序:
// Sort utils
const numericSort = (x, y) => x > y ? 1 : x < y ? -1 : 0;
const vmSort = (vm1, vm2) => numericSort(vm1.sortKey, vm2.sortKey);
5。分组视图模型
我们可以通过 groupKey 对 SubViewModel 实例列表进行分组:
const groupSubs = subs => subs.reduce(
(gs, s) => Object.assign(
gs,
{ [s.groupKey]: (gs[s.groupkey] || []).concat(s) }
), { }
)
现在,我们可以使用ko.pureComputed 实例创建一个从数据到应用的良好链。
const subData = ko.observableArray([]);
const subVMs = ko.pureComputed(
() => subData().map(SubViewModel)
);
const subGroups = ko.pureComputed(
() => groupSubs(subVMs())
);
const mainData = ko.observableArray([]);
const mainVMs = ko.pureComputed(
() => mainData().map(m => MainViewModel(m, subGroups))
);
ko.applyBindings(AppViewModel(mainVMs));
工作示例
这个流程在一个工作示例中说明:
const mains = ko.observableArray([]);
const subs = ko.observableArray([]);
const subGroup = sub => sub.sortOrder.split(".")[0]
const subsByMain = ko.pureComputed(() =>
subs()
.map(sub => [subGroup(sub), sub])
.reduce(
(groups, [id, sub]) => Object.assign(
groups, { [id]: (groups[id] || []).concat(sub) }
), {}
)
);
const Main = function(config, groups) {
this.title = config.description;
this.sortOrder = config.sortOrder;
this.subs = ko.pureComputed(() =>
groups()[config.sortOrder] || []
);
};
ko.applyBindings({
mains: ko.pureComputed(() =>
mains().map(cfg => new Main(cfg, subsByMain))
)
});
const getMainData = cb => setTimeout(() => cb([
{ id: 1, description: "foo foo foo", sortOrder: "1" },
{ id: 2, description: "bar bar bar", sortOrder: "2" }
]), 500);
const getSubData = cb => setTimeout(() => cb([
{ id: 3, description: "foo 1", sortOrder: "1.1" },
{ id: 4, description: "foo 2", sortOrder: "1.2" },
{ id: 5, description: "bar 1", sortOrder: "2.1" },
{ id: 6, description: "bar 2", sortOrder: "2.3" }
]), 300);
getMainData(mains);
getSubData(subs);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: mains">
<li>
<p data-bind="text: title + ' ' + sortOrder">
<ul data-bind="foreach: subs">
<li data-bind="text: description + ' ' + sortOrder"></li>
</ul>
</li>
</ul>