我正在使用Meteor上的反应依赖树(使用Blaze),但我找不到最佳方法。
首先,问题
在这个例子中,我有容器设置了一个item
对象,它有一个属性elements
(数组)。
elements
属性是循环,并为每个实例加载子模板。
元素可以在子模板上更新,但更新不会传播到容器。
另外,我添加了Add
按钮来更新item
ReactiveVar(来自container
)。在容器上更新item
后,每个字段呈现都会更新,因此对象已更新但未重新呈现。
HTML
<template name="container">
{{#let item=getItem}}
<h1>Update {{item._id}}</h1>
{{#each element in item.elements}}
<div>
{{> elementForm element}}
<small>{{element.name}}</small>
</div>
{{/each}}
{{/let}}
<button name="add">Add</button>
</template>
<template name="elementForm">
{{#let el=getEl}}
<input value="{{el.name}}"/>
{{/let}}
</template>
父模板
Template.container.onCreated(function onContainerCreated() {
// From data
let item = {
_id: '123abc',
elements: [
{name: "El #1"},
{name: "El #2"},
{name: "El #3"},
]
}
// Create master ReactiveVar
this.item = new ReactiveVar(item);
});
Template.container.helpers({
getItem() {
return Template.instance().item.get();
},
});
Template.container.events({
// Add a new element (Will refresh all elements render)
'click button[name="add"]'(e) {
let instance = Template.instance()
let item = instance.item.get()
item.elements.push({name: "new El"})
instance.item.set(item)
},
})
儿童模板
/* Element */
Template.elementForm.onCreated(function onElementCreated() {
this.el = new ReactiveVar(this.data)
});
Template.elementForm.helpers({
getEl() {
return Template.instance().el.get()
}
})
Template.elementForm.events({
'change input'(e) {
let instance = Template.instance()
let el = instance.el.get()
el.name = e.currentTarget.value
instance.el.set(el)
}
})
预览
由于meteor无法集成在Embed代码上,我会制作一些图像。
默认页面
更新输入不影响容器渲染
添加新项目,将重新渲染所有容器并获取新值
解决方案1?向孩子发送item
ReactiveVar
我知道,将整个ReactiveVar发送给孩子可以是对孩子使用.set()
方法的解决方案。
但如果我使用此hack,我必须将整个item
发送给每个elementForm
(不是最好的)
为例
/* Html */
<Template name="parentTemplate">
<small>{{getFoo}}</small>
{{> childTemplate getItem}}
</Template>
<Template name="childTemplate">
<input value="{{getFoo}}" />
</Template>
/* Parent */
Template.parentTemplate.onCreated(function onParentCreated() {
this.item = new ReactiveVar({ foo: bar})
});
Template.parentTemplate.helpers({
getFoo() {
return Template.instance().item.get().foo
},
getItem() {
return Template.instance().item // Send reactive var, net value (.get)
}
})
/* Child */
Template.childTemplate.onCreated(function onChildCreated() {
this.item = this.data.item // Use Parent Reactive var
});
Template.childTemplate.helpers({
getFoo() {
return Template.instance().item.get().foo
},
})
Template.childTemplate.events({
'change input'(e) {
let item = Template.instance().item.get()
item.foo = e.currentTarget.value
Template.instance().item.set(item) // Will update parent
},
})
解决方案2?回调
另一个解决方案是除了元素数据之外,还向子模板发送每个操作的回调(如更改),以通知父模板更新特定元素的主ReactiveVar。
示例
/* Html */
<Template name="parentTemplate">
<small>{{getFoo}}</small>
{{> childTemplate getData}}
</Template>
<Template name="childTemplate">
<input value="{{getFoo}}" />
</Template>
/* Parent */
Template.parentTemplate.onCreated(function onParentCreated() {
this.item = new ReactiveVar({ foo: bar})
});
Template.parentTemplate.helpers({
getFoo() {
return Template.instance().item.get().foo
},
getData() {
let instance = Template.instance()
return {
item: instance.item.get(),
onChange: function(val) {
// Update item here
let item = instance.item.get()
item.foo = val
instance.item.set(item)
}
}
}
})
/* Child */
Template.childTemplate.onCreated(function onChildCreated() {
});
Template.childTemplate.helpers({
getFoo() {
return Template.instance().data.item.foo
},
})
Template.childTemplate.events({
'change input'(e) {
// Call parent callback
Template.instance().data.onChange(e.currentTarget.value)
},
})
最终问题
处理父/子模板以处理项目对象中的元素列表的最佳方法是什么?
答案 0 :(得分:1)
其实是个好问题。
恕我直言,视情况而定,我大部分都是你的解决方案1。
类似的东西:
Template.container.onCreated(function(){
//each elements are ReactiveVar
let item = {
_id: '123abc',
elements: [
{name: new ReactiveVar("El #1")},
{name: new ReactiveVar("El #2")},
{name: new ReactiveVar("El #3")},
]
}
// I don't wrap the wole item on a ReactiveVar if I don't need it.
});
然后,当您使用#each
进行迭代时,您只会将当前元素数据注入您孩子的datacontext。从这里,你可以很容易地做到:
Template.childTemplate.helpers({
getName() {
return this.name.get();
},
})
Template.childTemplate.events({
'change input'(e, tp) {
newVal = e.currentTarget.value
tp.currentData().name.set(newVal);
}
});
我使用了一些“快捷方式”作为Template.currentData()
,并使用this.name.get()
在帮助程序上直接访问datacontext。
您还可以直接访问模板实例作为事件的第二个参数:
'change input'(event, templateInstance) {}
我没有测试它,我只是给你这个想法。
希望这会对你有所帮助。