如何从 Meteor 集合中制作反应式数组? [英] How can I make a reactive array from a Meteor collection?

查看:36
本文介绍了如何从 Meteor 集合中制作反应式数组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to take a list of item names from a collection as a simple array to use for things like autocompleting user input and checking for duplicates. I would like this list to be reactive so that changes in the data will be reflected in the array. I have tried the following based on the Meteor documentation:

    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 秒).难道我做错了什么?有没有更好的方法来实现这一点?

This code seems to work, but it kills my app's load time (takes 5-10 seconds to load on dev/localhost vs. ~1 second without this code). Am I doing something wrong? Is there a better way to accomplish this?

推荐答案

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

Edit: I have updated the code below both because it was fragile, and to put it in context so it's easier to test. I have also added a caution - in most cases, you will want to use @zorlak's or @englandpost's methods (see below).

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

First of all, kudos to @zorlak for digging up my old question that nobody answered. I have since solved this with a couple of insights gleaned from @David Wihl and will post my own solution. I will hold off on selecting the correct answer until others have a chance to weigh in.

@zorlak 的回答解决了单个字段的自动完成问题,但正如问题中所述,我正在寻找可以被动更新的数组,而自动完成只是它的一个示例将用于.拥有这个数组的好处是它可以在任何地方使用(不仅仅是在模板助手中)并且它可以在代码中多次使用而无需重新执行查询(以及 _.pluck() 将查询简化为数组).就我而言,这个数组最终出现在多个自动完成字段以及验证和其他地方.可能我提出的优势在大多数 Meteor 应用程序中并不显着(请发表评论),但这对我来说似乎是一个优势.

@zorlak's answer solves the autocomplete issue for a single field, but as stated in the question, I was looking for an array that would update reactively, and the autocomplete was just one example of what it would be used for. The advantage of having this array is that it can be used anywhere (not just in template helpers) and that it can be used multiple times in the code without having to re-execute the query (and the _.pluck() that reduces the query to an array). In my case, this array ends up in multiple autocomplete fields as well as validation and other places. It's possible that the advantages I'm putting forth are not significant in most Meteor apps (please leave comments), but this seems like an advantage to me.

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

To make the array reactive, simply build it inside a Meteor.autorun() callback - it will re-execute any time the target collection changes (but only then, avoiding repetitive queries). This was the key insight I was looking for. Also, using the Template.rendered() callback is cleaner and less of a hack than the set_typeahead template helper I used in the question. The code below uses underscore.js's _.pluck() to extract the array from the collection and uses Twitter bootstrap's $.typeahead() to create the autocomplete.

更新的代码:我已经编辑了代码,因此您可以在常用的 meteor created 测试环境中进行尝试.您的 html 将需要一行 <input id="typeahead"/> 在hello"模板中.@Items 具有 @ 符号,使 Items 在控制台上作为全局可用(Meteor 0.6.0 添加了文件级变量范围).这样你就可以在控制台中输入新的项目,例如 Items.insert({name: "joe"}),但是 @ 对于代码来说不是必需的工作.独立使用的另一个必要更改是 typeahead 函数现在将源设置为函数 (->),以便它在激活时查询 items 而不是被设置在渲染时,这允许它利用对 items 的更改.

Updated code: I have edited the code so you can try this with a stock meteor created test environment. Your html will need a line <input id="typeahead" /> in the 'hello' template. @Items has the @ sign to make Items available as a global on the console (Meteor 0.6.0 added file-level variable scoping). That way you can enter new items in the console, such as Items.insert({name: "joe"}), but the @ is not necessary for the code to work. The other necessary change for standalone use is that the typeahead function now sets the source to a function (->) so that it will query items when activated instead of being set at rendering, which allows it to take advantage of changes to 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"}

注意!我们创建的数组本身不是响应式数据源.需要将 typeahead source: 设置为函数的原因> -> 返回的 items 是当 Meteor 第一次启动时,代码在 Minimongo 从服务器获取数据之前运行,并且 items设置为空数组.Minimongo 然后接收它的数据,并更新 items 如果你在控制台打开的情况下运行上面的代码你可以看到这个过程:console.log items 将记录两次如果你有存储的任何数据.

Caution! The array we created is not itself a reactive data source. The reason that the typeahead source: needed to be set to a function -> that returned items is that when Meteor first starts, the code runs before Minimongo has gotten its data from the server, and items is set to an empty array. Minimongo then receives its data, and items is updated You can see this process if you run the above code with the console open: console.log items will log twice if you have any data stored.

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

Template.x.rendered() calls don't don't set a reactivity context and so won't retrigger due to changes in reactive elements (to check this, pause your code in the debugger and examine Deps.currentComputation -- if it's null, you are not in a reactive context and changes to reactive elements will be ignored). But you might be surprised to learn that your templates and helpers will also not react to items changing -- a template using #each to iterate over items will render empty and never rerender. You could make it act as a reactive source (the simplest way being to store the result with Session.set(), or you can do it yourself), but unless you are doing a very expensive calculation that should be run as seldom as possible, you are better off using @zorlak's or @englandpost's methods. It may seem expensive to have your app querying the database repetitively, but Minimongo is caching the data locally, avoiding the network, so it will be quite fast. Thus in most situations, it's better just to use

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

除非您发现您的应用程序真的陷入困境.

unless you find that your app is really bogging down.

这篇关于如何从 Meteor 集合中制作反应式数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆