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

rdi*_*ert 9 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}    
Run Code Online (Sandbox Code Playgroud)

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

zor*_*lak 6

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

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


rdi*_*ert 5

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


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

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

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

更新的代码:我已编辑了代码,因此您可以使用stock meteor created测试环境进行尝试.你的html <input id="typeahead" />在'hello'模板中需要一行.@Items@标志Items在控制台上作为全局可用(Meteor 0.6.0添加了文件级变量作用域).这样您就可以在控制台中输入新项目,例如Items.insert({name: "joe"}),但@代码无需工作.独立使用的另一个必要变化是,typeahead函数现在将source设置为function(->),以便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"}
Run Code Online (Sandbox Code Playgroud)

警告!我们创建的数组本身不是一个反应数据源.该预输入的原因source:需要被设置为一个函数 ->即返回items是,流星第一开始时,代码运行Minimongo已经从服务器得到它的数据之前,并且items被设置为一个空数组.Minimongo然后接收其数据,并items更新如果您在控制台打开的情况下运行上述代码,您可以看到此过程:console.log items如果您存储了任何数据,将记录两次.

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

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

除非你发现你的应用真的陷入困境.