我想从集合中获取项目名称列表作为一个简单数组,用于自动完成用户输入和检查重复项等内容。我希望此列表具有反应性,以便数据中的更改将反映在数组中。我根据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秒)。难道我做错了什么?有没有更好的方法来实现这一目标?
答案 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
的模板将呈现为空并且永远不会重新呈现。您可以将其作为反应源(最简单的方法是使用items
或you 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的数据模型允许使用自定义渲染列表进行快速多规则搜索。
使用@
自动填充用户,其中在线用户以绿色显示:
在同一行中,使用元数据和引导程序图标自动填充其他内容:
请分叉,拉动和改进!