假设以下情况:
self.watchedRecords = ko.observableArray();
//Get watchlist entries from server
dataService.getWatched().then(function (resolve) {
//Push all items to the observable array
self.watchedRecords(resolve.entries);
});
在我的resolve
我有另一个密钥likes
,其中包含有关登录用户喜欢哪些条目的信息(作为一个数组):
{
watchedItemGuid: 572, //user liked item in which the guid is 572
id: 3 //the like is saved in another table as item with the id 3
}
我尝试将有关喜欢或不喜欢的信息添加到watchedRecords
observable中,以便稍后使用(例如删除等)。
我不确定是否循环使用喜欢并过滤原始数据是一种好方法。关于这个主题是否有最佳实践?
答案 0 :(得分:1)
我首先要实现一种方法来合并likes
和entries
的列表。如果没有性能优化或架构影响,这可能是:
entries
liked
属性添加到从服务器收到的现有对象true
false
或likes
)
在下面的代码段中,您会看到这个实现很难阅读,并且一旦您的列表增长就会变慢(O(n*m)
我相信......但不要引用我的话)< / em>的
var watchedRecords = ko.observableArray([]);
var setWatchedRecords = function(resolve) {
watchedRecords(
// Map over the entries, this is our base set of data
resolve.entries.map(
function(record) {
// Add one property toeach entry: "liked"
return Object.assign(record, {
// The value is liked is determined by our array
// of liked items.
liked: resolve.likes.some(function(like) {
// If some like contains our record id, we return true
return like.watchedItemId === record.id;
})
})
}
)
);
};
setWatchedRecords(getWatched());
console.log(watchedRecords().map(JSON.stringify));
// Mock data
function getEntries() {
return [ { id: 572, name: "Entry 2" }, { id: 573, name: "Entry 3" }, { id: 574, name: "Entry 4" }, { id: 575, name: "Entry 5" }, { id: 576, name: "Entry 6" } ]
};
function getLikes() {
return [ { watchedItemId: 572, id: 1 }, { watchedItemId: 576, id: 2 } ];
};
function getWatched() {
return { entries: getEntries(), likes: getLikes() };
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
我个人做的初步优化(切换到ES2015语法):
Set
存储所有liked
ID(因此您只需循环浏览一次)
var get = key => obj => obj[key];
var watchedRecords = ko.observableArray([]);
var getLikedIds = (resolve) => new Set(
resolve.likes.map(get("watchedItemId"))
);
var getWatchedRecords = (resolve) => {
var likes = getLikedIds(resolve);
return resolve.entries.map(
e => Object.assign(e, { liked: likes.has(e.id) })
);
};
watchedRecords(getWatchedRecords(getWatched()));
console.log(watchedRecords().map(JSON.stringify));
// Mock data
function getEntries() {
return [ { id: 572, name: "Entry 2" }, { id: 573, name: "Entry 3" }, { id: 574, name: "Entry 4" }, { id: 575, name: "Entry 5" }, { id: 576, name: "Entry 6" } ]
};
function getLikes() {
return [ { watchedItemId: 572, id: 1 }, { watchedItemId: 576, id: 2 } ];
};
function getWatched() {
return { entries: getEntries(), likes: getLikes() };
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
现在,要解决的最后一个问题是您是否要重新使用视图模型。目前,每次更新时都会完全替换您的项目数组。如果淘汰赛必须呈现列表或进行其他计算,那么增加一些复杂性以获得性能可能是值得的...例如:
Record
视图模型,可以使用id
,entry
进行实例化,是否为liked
liked
状态编辑:为了好玩,我也把它作为一个例子:
var Record = function(data, likeMap) {
Object.assign(this, data);
// Each `Record` instance has a reference to the
// manager's `Set` of liked ids. Now, we can
// compute whether we're a liked Record automatically!
this.liked = ko.pureComputed(
() => likeMap().has(this.id)
);
this.toString = () => "Id: " + this.id + ", liked: " + this.liked();
};
var RecordManager = function() {
const likes = ko.observable(new Set());
const recordMap = ko.observable(new Map());
// Our record viewmodels in a list
this.records = ko.pureComputed(
() => Array.from(recordMap().values())
).extend({ "deferred": true });
// This takes your server side likes and transforms
// it to a set of liked ids
const setLikes = likeData => {
likes(
new Set(likeData.map(get("watchedItemId")))
);
};
// This takes your server side records and transforms
// them to a Map of `id: Record`
const setRecords = recordData => {
recordMap(
recordData.reduce(
// Re-use our previously createdviewmodel if there is any
(map, r) => map.set(r.id, recordMap().get(r.id) || new Record(r, likes))
, new Map())
);
};
// Updating is now independent of order.
// Our Record instances contain a reference to the `likes` set,
// thereby automatically updating their like status
this.updateRecords = data => {
setRecords(data.entries);
setLikes(data.likes);
}
};
var rm = new RecordManager();
rm.updateRecords(getWatched());
console.log(rm.records().map(r => r.toString()));
// Mock data
function getEntries() {
return [ { id: 572, name: "Entry 2" }, { id: 573, name: "Entry 3" }, { id: 574, name: "Entry 4" }, { id: 575, name: "Entry 5" }, { id: 576, name: "Entry 6" } ]
};
function getLikes() {
return [ { watchedItemId: 572, id: 1 }, { watchedItemId: 576, id: 2 } ];
};
function getWatched() {
return { entries: getEntries(), likes: getLikes() };
};
// Utils
function get(key) { return function(obj) { return obj[key]; }; };
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>