9

我想将集合中的项目名称列表作为一个简单的数组用于自动完成用户输入和检查重复项等操作。我希望此列表具有反应性,以便数据的更改将反映在数组中。我根据 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

4 回答 4

6

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

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

于 2012-12-17T08:18:30.403 回答
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有在控制台上作为全局可用的@标志( Meteor 0.6.0 添加了文件级变量范围)。这样您就可以在控制台中输入新项目,例如,但这并不是代码工作所必需的。独立使用的另一个必要更改是 typeahead 函数现在将源设置为函数 ( ),以便在激活时查询而不是在渲染时设置,这允许它利用对.ItemsItems.insert({name: "joe"})@->itemsitems

@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:需要将 typeahead 设置为返回的函数 的原因是,当 Meteor 首次启动时,代码在 Minimongo 从服务器获取数据之前运行,并且设置为空数组。Minimongo 然后接收它的数据,并更新如果你在控制台打开的情况下运行上面的代码,你可以看到这个过程:如果你有任何数据存储,将记录两次。 ->itemsitemsitemsconsole.log items

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

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

除非你发现你的应用程序真的陷入了困境。

于 2012-12-21T20:16:31.867 回答
1

是我的 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
于 2013-01-28T22:30:26.523 回答
1

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

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

使用 自动完成用户@,其中在线用户显示为绿色:

在此处输入图像描述

在同一行中,使用元数据和引导图标自动完成其他内容:

在此处输入图像描述

请分叉、拉动和改进!

于 2013-09-22T22:10:11.923 回答