如何从Meteor集合中制作反应阵列?

时间:2012-08-17 18:27:36

标签: meteor

我想从集合中获取项目名称列表作为一个简单数组,用于自动完成用户输入和检查重复项等内容。我希望此列表具有反应性,以便数据中的更改将反映在数组中。我根据Meteor文档尝试了以下内容:

    setReactiveArray = (objName, Collection, field) ->
        update = ->
          context = new Meteor.deps.Context()
          context.on_invalidate update
          context.run -> 
            list = Collection.find({},{field: 1}).fetch()
            myapp[objName] = _(list).pluck field
        update()

    Meteor.startup ->
        if not app.items?
            setReactiveArray('items', Items, 'name')

    #set autocomplete using the array
    Template.myForm.set_typeahead =  ->
       Meteor.defer ->
        $('[name="item"]').typeahead {source: app.items}    

此代码似乎有效,但它会导致我的应用程序加载时间(在dev / localhost上加载需要5-10秒,而没有此代码需要~1秒)。难道我做错了什么?有没有更好的方法来实现这一目标?

4 个答案:

答案 0 :(得分:6)

你应该能够使用Items.find({},{name: 1}).fetch(),它将返回一个项目数组并且是被动的,因此只要在被动上下文中调用它,它就会在查询结果发生变化时重新运行它的封闭函数。

对于Template.myForm.set_typeahead助手,您可能希望在助手本身内调用该查询,将结果存储在局部变量中,然后使用引用该变量的函数调用Meteor.defer。否则,我不确定查询将在被调用时位于被动上下文中。

答案 1 :(得分:5)

编辑:我已经更新了下面的代码,因为它很脆弱,并且将它放在上下文中,因此更容易测试。我还添加了一个注意事项 - 在大多数情况下,你会想要使用@ zorlak或@ englandpost的方法(见下文)。


首先,感谢@zorlak挖掘了我没有回答的旧问题。我已经用一些insights gleaned from @David Wihl解决了这个问题,并将发布我自己的解决方案。我会推迟选择正确的答案,直到其他人有机会权衡。

@ zorlak的答案解决了单个字段的自动完成问题,但正如问题所述,我一直在寻找能够被动更新的数组,而自动完成只是其中一个例子将用于。拥有这个数组的优点是它可以在任何地方使用(不仅仅是在模板助手中),并且它可以在代码中多次使用而无需重新执行查询(和_.pluck()减少了查询数组)。在我的例子中,这个数组最终在多个自动完成字段以及验证和其他地方。在大多数Meteor应用程序中,我提出的优势可能并不重要(请留下评论),但这似乎对我有利。

要使数组具有反应性,只需在Meteor.autorun()回调中构建它 - 它将在目标集合发生更改时重新执行(但只有这样才能避免重复查询)。这是我一直在寻找的关键洞察力。此外,使用Template.rendered()回调比我在问题中使用的set_typeahead模板帮助程序更清晰,更少破解。下面的代码使用underscore.js's _.pluck()从集合中提取数组,并使用Twitter bootstrap's $.typeahead()创建自动完成。

更新了代码:我已编辑了代码,因此您可以使用库存meteor create d测试环境进行尝试。你的html在'hello'模板中需要一行<input id="typeahead" />@Items有一个@符号,可以Items在控制台(Meteor 0.6.0 added file-level variable scoping)上作为全局提供。这样您就可以在控制台中输入新项目,例如Items.insert({name: "joe"}),但代码无需@。独立使用的另一个必要变化是,typeahead函数现在将源设置为函数(->),以便在激活时查询items而不是在渲染时设置,这允许它采取items更改的优势。

@Items = new Meteor.Collection("items")
items = {}

if Meteor.isClient
  Meteor.startup ->
    Meteor.autorun ->
      items = _(Items.find().fetch()).pluck "name"
      console.log items  #first result will be empty - see caution below

  Template.hello.rendered = ->
    $('#typeahead').typeahead {source: -> _(Items.find().fetch()).pluck "name"}

注意!我们创建的数组不是本身一个反应式数据源。需要将预先source:设置为函数的原因 {{1返回->的是当Meteor首次启动时,代码在Minimongo从服务器获取数据之前运行,items设置为空数组。 Minimongo然后接收其数据,items更新如果您在控制台打开的情况下运行上述代码,您可以看到此过程:如果您存储了任何数据,items将记录两次。

console.log items调用不会设置反应性上下文,因此不会因为被动元素的更改而重新触发(要检查这一点,请在调试器中暂停代码并检查Template.x.rendered() - - 如果它是Deps.currentComputation,则您不处于被动上下文中,并且将忽略对被动元素的更改。但是您可能会惊讶地发现您的模板和帮助程序也不会对null更改做出反应 - 使用items迭代#each的模板将呈现为空并且永远不会重新呈现。您可以将其作为反应源(最简单的方法是使用itemsyou can do it yourself存储结果),但除非您正在进行非常昂贵的计算,应该尽可能少地运行,你最好使用@ zorlak或@ englandpost的方法。让你的应用程序重复查询数据库似乎很昂贵,但Minimongo正在本地缓存数据,避开了网络,所以它会非常快。因此,在大多数情况下,最好只使用

Session.set()

除非您发现自己的应用真的陷入困境。

答案 2 :(得分:1)

here是我对bootstrap typeahead的快速解决方案

在客户端:

Template.items.rendered = ->
  $("input#item").typeahead
    source: (query, process) ->
      subscription = Meteor.subscribe "autocompleteItems", query, ->
        process _(Items.find().fetch()).pluck("name")
      subscription.stop() # here may be a bit different logic,
      # such as keeping all opened subsriptions until autocomplete
      # will be successfully completed and so on
      items: 5

在服务器端:

Meteor.publish "autocompleteItems", (query) ->
  Items.find
    name: new RegExp(query, "i"),
      fields: { name: 1 },
      limit: 5

答案 3 :(得分:1)

我实际上完全以完全不同的方式接近自动完成问题,使用客户端代码而不是查询服务器。我认为这是优越的,因为Meteor的数据模型允许使用自定义渲染列表进行快速多规则搜索。

  

https://github.com/mizzao/meteor-autocomplete

使用@自动填充用户,其中在线用户以绿色显示:

enter image description here

在同一行中,使用元数据和引导程序图标自动填充其他内容:

enter image description here

请分叉,拉动和改进!