【问题标题】:Is it possible to sort a collection only once and then keep that order intact despite reactivity?是否可以仅对集合进行一次排序,然后尽管反应性仍然保持该顺序不变?
【发布时间】:2017-02-09 08:55:37
【问题描述】:

我有一个评论列表。这些 cmets 有一个名为“vote”的属性(用户可以对 cme​​ts 进行投票),它们最初按投票排序(降序,onRender)。

现在,当用户投票时,cmets 的顺序会响应更新以反映新投票。

是否有可能以某种方式保持初始排序顺序不变?我想避免将用户与 cmets 在页面上时自动交换顺序混淆。

有什么好的方法可以解决这个问题吗?我在渲染页面时可能会考虑一次性排序,或者以某种方式保存订单,然后在集合被响应式刷新时重新应用它。

【问题讨论】:

  • 这是一个有趣的问题。假设您找到了一种方法,现在出现了一个新的评论。它在列表中的哪个位置?
  • 我会做一个方法调用来拉入按您想要的方式排序的初始集合。然后我还有一个订阅,检查数据库中是否有更新,并通知用户它们类似于 twitter 的“2 个新 cmets”,并让该 UI 元素在点击时再次调用流星方法。
  • @zim 要么在列表的最后,要么根本不在。如果我考虑一下,我需要保持反应性的唯一原因是因为我希望用户的新投票反映在页面上。他不需要立即查看新的或编辑的 cmets 或其他用户的新投票。因此,对我来说,一个简单的解决方案可能是通过简单地增加客户端的投票计数来“伪造它”。用户几乎不会注意到差异。但是,为了知识起见,我仍然想知道提议的方案(列表末尾有新的 cmets)是否可行。

标签: sorting meteor meteor-collection2


【解决方案1】:

您可以使用函数对 Minimongo 查询进行排序。所以像:

const initialVotes = new Map();
Comments.find({}, {sort: (a, b) => {
  if (!initialVotes.has(a._id)) initialVotes.set(a._id, a.votes);
  if (!initialVotes.has(b._id)) initialVotes.set(b._id, b.votes);

  return initialVotes.get(b._id) - initialVotes.get(a._id);
});

这将使 cmets 按初始投票排序。如果有任何其他更改(例如用户编辑 cmets),则会反应性地传播,如果做出新评论,则会反应性地添加它。但是如果票数发生变化,顺序不会改变(但可能呈现的票数仍然会更新)。

【讨论】:

  • 这听起来很完美!我稍后会尝试并告诉你。谢谢!
  • 不过有一个问题:如何让这个工作适用于任意数量的 cmets?
  • 你这是什么意思?这适用于任意数量的 cmets。
  • 我的错,我认为排序参数是单个 cmets。感谢您的解决方案!
  • 函数获取两个cmets文件进行比较。但整个排序适用于任意数量的 cmets。
【解决方案2】:

您可以使用reactive: false 选项执行non-reactive find,例如:

Comments.find({},{sort: {vote: -1}, reactive: false});

如果您想在原始列表之后添加新的 cmets,那么我将呈现两个 cmets 列表,一个接一个。首先渲染现有 cmets 的排序列表,然后按创建顺序反应性地渲染在初始渲染时间之后创建的 cmets 列表。第二个列表最初是空的,根本不会渲染,但最终新的 cmets 会出现在那里,并有自己的投票数。

现在,由于上面的光标是非反应性的,我们如何在不改变顺序的情况下让投票计数反应性地更新?答:(在 Blaze 的情况下)使用响应式助手!

Template.myTemplate.helpers({
  currentVote(){
    return Comments.findOne(this._id).vote;
  }
});

从性能角度来看,这实际上是非常便宜的。

【讨论】:

  • 如果我让它不反应,用户的新投票在页面上不可见。正如我在上面的评论中提到的,这可能是一种解决方案,如果我只是增加客户端显示的投票计数。但是为了这个问题,我可以让评论更改仍然是反应性的(除了排序顺序,甚至在必要时忽略新的 cmets)?
  • 您可以使用响应式 helper 来显示投票数。查看更新的答案。
【解决方案3】:

因为您不关心新评论,只关心新投票,所以我会使用 Meteor.call() 按照您想要的方式进行初始填充,正如@sdybskiy 所述。然后我会为这些 cmets 设置订阅以获取票数。

在模板的 onCreated() 中,您可以像这样设置订阅:

let voteCursor = Votes.find({commentIds: comments});
    voteCursor.observe({
        added: function(newDocument, oldDocument) {
            // the published vote would have a commentId, so here you
            // would go to your client store for the comments, find the 
            // comment, and increment the count
        },
        removed: function(oldDocument) {
            // same idea here, but process a vote being removed
        }
    });

请注意,我正在传递从该方法返回的所有 cmets 的 ID,因此发布将仅发布这些 cmets 的投票。

【讨论】:

  • 哦,也许我没有说清楚:投票不是他们自己的集合,而是一个简单的数字字段,它是评论集合的一部分。
  • @Cos,啊哈,那没关系!看起来米歇尔弗洛伊德的答案是你想要的。
猜你喜欢
  • 2015-05-05
  • 1970-01-01
  • 2020-01-06
  • 2019-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-28
相关资源
最近更新 更多