在我的客户端UI中,我有一个带有不同搜索标准的表单,我想被动更新结果列表。搜索查询被转换为一个经典的minimongo选择器,保存在Session变量中,然后我让观察者对结果进行处理:
// Think of a AirBnb-like application
// The session variable `search-query` is updated via a form
// example: Session.set('search-query', {price: {$lt: 100}});
Offers = new Meteor.Collection('offers');
Session.setDefault('search-query', {});
resultsCursor = Offers.find(Session.get('search-query'));
// I want to add and remove pins on a map
resultCursor.observe({
added: Map.addPin,
removed: Map.removePin
});
Deps.autorun(function() {
// I want to modify the cursor selector and keep the observers
// so that I would only have the diff between the old search and
// the new one
// This `modifySelector` method doesn't exist
resultsCursor.modifySelector(Session.get('search-query'));
});
如何在游标对象上实现这个modifySelector
方法?
基本上,我认为这种方法需要更新游标的编译版本,即selector_f
属性,然后重新运行观察程序(而不会丢失以前结果的缓存)。或者有更好的解决方案吗?
编辑:你们中的一些人误解了我要做的事情。让我提供一个完整的例子:
Offers = new Meteor.Collection('offers');
if (Meteor.isServer && Offers.find().count() === 0) {
for (var i = 1; i < 4; i++) {
// Inserting documents {price: 1}, {price: 2} and {price: 3}
Offers.insert({price:i})
}
}
if (Meteor.isClient) {
Session.setDefault('search-query', {price:1});
resultsCursor = Offers.find(Session.get('search-query'));
resultsCursor.observe({
added: function (doc) {
// First, this added observer is fired once with the document
// matching the default query {price: 1}
console.log('added:', doc);
}
});
setTimeout(function() {
console.log('new search query');
// Then one second later, I'd like to have my "added observer" fired
// twice with docs {price: 2} and {price: 3}.
Session.set('search-query', {});
}, 1000);
}
这并没有以您想要的方式解决问题,但我认为结果仍然是一样的。如果这是你明确不想要的解决方案,请告诉我,我可以删除答案。我只是不想把代码放在评论中。
Offers = new Meteor.Collection('offers');
Session.setDefault('search-query', {});
Template.map.pins = function() {
return Offers.find(Session.get('search-query'));
}
Template.map.placepins = function(pins) {
// use d3 or whatever to clear the map and then place all pins on the map
}
假设你的模板是这样的:
<template name="map">
{{placepins pins}}
</template>
一种解决方案是手动区分新旧游标:
# Every time the query change, do a diff to add, move and remove pins on the screen
# Assuming that the pins order are always the same, this use a single loop of complexity
# o(n) rather than the naive loop in loop of complexity o(n^2)
Deps.autorun =>
old_pins = @pins
new_pins = []
position = 0
old_pin = undefined # This variable needs to be in the Deps.autorun scope
# This is a simple algo to implement a kind of "reactive cursor"
# Sorting is done on the server, it's important to keep the order
collection.find(Session.get('search-query'), sort: [['mark', 'desc']]).forEach (product) =>
if not old_pin?
old_pin = old_pins.shift()
while old_pin?.mark > product.mark
@removePin(old_pin)
old_pin = old_pins.shift()
if old_pin?._id == product._id
@movePin(old_pin, position++)
new_pins.push(old_pin)
old_pin = old_pins.shift()
else
newPin = @render(product, position++)
new_pins.push(newPin)
# Finish the job
if old_pin?
@removePin(old_pin)
for old_pin in old_pins
@removePin(old_pin)
@pins = new_pins
但它有点粗糙,效率也不高。此外,diff逻辑已经在minimongo中实现,因此最好重用它
也许一个可以接受的解决方案是在本地集合中跟踪旧引脚?类似这样的东西:
Session.setDefault('search-query', {});
var Offers = new Meteor.Collection('offers');
var OldOffers = new Meteor.Collection(null);
var addNewPin = function(offer) {
// Add a pin only if it's a new offer, and then mark it as an old offer
if (!OldOffers.findOne({_id: offer._id})) {
Map.addPin(offer);
OldOffers.insert(offer);
}
};
var removePinsExcept = function(ids) {
// Clean out the pins that no longer exist in the updated query,
// and remove them from the OldOffers collection
OldOffers.find({_id: {$nin: ids}}).forEach(function(offer) {
Map.removePin(offer);
OldOffers.remove({_id: offer._id});
});
};
Deps.autorun(function() {
var offers = Offers.find(Session.get('search-query'));
removePinsExcept(offers.map(function(offer) {
return offer._id;
}));
offers.observe({
added: addNewPin,
removed: Map.removePin
});
});
我不确定这比你的数组答案快多少,尽管我认为它可读性更强。您需要考虑的是,随着查询的变化,对结果进行区分是否真的比每次删除所有引脚并重新绘制要快得多。我怀疑这可能是一个过早优化的情况。您希望用户多久更改一次搜索查询,以便新旧查询的结果之间有大量重叠?
我在自己的业余爱好Meteor项目中也遇到了同样的问题。
选择器存储的是filter
会话变量。触发任何复选框或按钮都会更改filter
和所有UI重新呈现。
该解决方案有一些缺点,主要是你不能与其他用户共享应用程序状态。
所以我意识到更好的方法是将应用程序状态存储在URL中。
也许对你来说也更好?
点击按钮现在可以改变URL和基于它的UI呈现。我用FlowRouter实现了这一点。
有用的阅读:在URL 上保持应用程序状态