javascript - 误解 `each...in` 渲染与反应变量
问题描述
我是Meteor/Blaze的新手,但这是我公司正在使用的。
我很难理解 Blaze 如何决定渲染基于ReactiveDict
TL;博士
ReactiveDict
我从父数组中的数组创建了一些子模板。更改时子模板中的数据不会刷新,ReactiveDict
我不明白为什么。
我可能对 Blaze 渲染有误解。你能帮帮我吗?
家长
父 Html 模板
<template name="parent">
{{#each child in getChildren}}
{{> childTemplate (childArgs child)}}
{{/each}}
</template>
父 Javascript
反应变量该模板从getChildren
仅检索ReactiveDict
.
// Child data object
const child = () => ({
id: uuid.v4(),
value: ""
});
// Create a reactive dictionary
Template.parent.onCreated(function() {
this.state = new ReactiveDict();
this.state.setDefault({ children: [child()] });
});
// Return the children from the reactive dictionary
Template.parent.helpers({
getChildren() {
return Template.instance().state.get('children');
}
});
子模板参数(来自父模板)
父模板为子模板提供了一些用于设置默认值和回调的数据。每个都使用一个函数实例化,该childArgs
函数使用子id
设置正确的数据和回调。单击add
按钮时,它会将一个子项添加children
到ReactiveDict
. 单击delete
按钮时,它会children
从ReactiveDict
.
Template.parent.helpers({
// Set the children arguments: default values and callbacks
childArgs(child) {
const instance = Template.instance();
const state = instance.state;
const children = state.get('children');
return {
id: child.id,
// Default values
value: child.value,
// Just adding a child to the reactive variable using callback
onAddClick() {
const newChildren = [...children, child()];
state.set('children', newChildren);
},
// Just deleting the child in the reactive variable in the callback
onDeleteClick(childId) {
const childIndex = children.findIndex(child => child.id === childId);
children.splice(childIndex, 1);
state.set('children', children);
}
}
}
})
孩子
子 html 模板
该模板显示来自父级和 2 个按钮的数据,add
以及delete
。
<template name="child">
<div>{{value}}</div>
<button class="add_row" type="button">add</button>
<button class="delete_row" type="button">delete</button>
</template>
子 javascript(事件)
这里调用的两个函数是作为参数从父模板传递的回调。
// The two functions are callbacks passed as parameters to the child template
Template.child.events({
'click .add_row'(event, templateInstance) {
templateInstance.data.onAddClick();
},
'click .delete_row'(event, templateInstance) {
templateInstance.data.onDeleteClick(templateInstance.data.id);
},
问题
我的问题是,当我删除一个孩子(使用回调设置ReactiveDict
likeonAddClick()
函数)时,我的数据没有正确呈现。
文本示例:
我添加这样的行。
child 1 | value 1
child 2 | value 2
child 3 | value 3
当我删除child 2
,我得到这个:
child 1 | value 1
child 3 | value 2
我想要这个:
child 1 | value 1
child 3 | value 3
我正在使用函数中的child
数据初始化。childArgs
Template.child.onRendered()
- 好:删除in
getChildren()
时调用该函数,并且我在变量 ( in ) 中有正确的数据。child
ReactiveDict
children
ReactiveDict
- 好:如果我有 3 个孩子并且我删除了一个,则父模板只呈现 2 个孩子。
- 坏:然而孩子的
onRendered()
从来没有被调用过(孩子的onCreated()
功能也没有)。这意味着为子模板显示的数据是错误的。
图片示例
我正在添加图片以帮助理解:
正确的html显示的 HTML 是正确的:我有 3 个孩子,我删除了第二个。在我的 HTML 中,我可以看到显示的两个孩子在其 div 中具有正确的 ID。然而显示的数据是错误的。
过时的数据我已经删除了第一张照片中的第二个孩子。显示的孩子应该是第一个和第三个。在控制台日志中,我的数据是正确的。红色数据是第一位的。紫色是第三个。
然而我们可以看到被删除的孩子的数据被显示(asd
和asdasd
)。删除标签时,我可以在日志中看到第二个孩子的 ID,尽管它不应该再存在了。第二个孩子 ID 为绿色。
我可能误会了什么。你能帮帮我吗?
解决方案
我不知道从哪里开始,但有很多错误,我宁愿在这里提供一个运行中的解决方案并附上评论。
首先,该each
函数应该正确传递 id 而不是整个孩子,否则find
将导致错误:
<template name="parent">
{{#each child in getChildren}}
{{#with (childArgs child.id)}}
{{> childTemplate this}}
{{/with}}
{{/each}}
</template>
Template.instance()
在帮助器中,您可以通过使用词法作用域来避免调用过多的函数:
childArgs (childId) {
const instance = Template.instance()
const children = instance.state.get('children')
const childData = children.find(child => child.id === childId)
const value = {
// Default values
data: childData,
// Just adding a child to the reactive variable using callback
onAddClick () {
const children = instance.state.get('children')
const length = children ? children.length : 1
const newChild = { id: `data ${length}` }
const newChildren = [...children, newChild]
instance.state.set('children', newChildren)
},
// Just deleting the child in the reactive variable in the callback
onDeleteClick (childId) {
const children = instance.state.get('children')
const childIndex = children.findIndex(child => child.id === childId)
children.splice(childIndex, 1)
instance.state.set('children', children)
}
}
return value
}
然后请注意,在'click .delete_row'
您使用的事件回调中,templateInstance.data.id
但这始终 undefined
与您当前的结构一致。应该是templateInstance.data.data.id
因为data
总是为模板实例中的所有数据定义,如果你命名一个属性data
,那么你必须通过以下方式访问它data.data
:
Template.childTemplate.events({
'click .add_row' (event, templateInstance) {
templateInstance.data.onAddClick()
},
'click .delete_row' (event, templateInstance) {
templateInstance.data.onDeleteClick(templateInstance.data.id)
}
})
现在,为什么您的数据被奇怪地排序也是有道理的。看一下onDeleteClick
回调:
onDeleteClick (childId) {
// childId is always undefined in your code
const children = instance.state.get('children')
const childIndex = children.findIndex(child => child.id === childId)
// childIndex is always -1 in your code
// next time make a dead switch in such situations:
if (childIndex === -1) {
throw new Error(`Expected child by id ${childId}`)
}
children.splice(childIndex, 1)
// if index is -1 this removes the LAST element
instance.state.set('children', children)
}
所以你的问题是拼接行为并将未经检查的索引传递给拼接:
开始更改数组的索引。如果大于数组的长度,start 将被设置为数组的长度。如果为负数,它将从数组末尾开始那么多元素(原点 -1,意味着 -n 是倒数第 n 个元素的索引,因此等效于 array.length - n 的索引)。如果 array.length + start 小于 0,它将从索引 0 开始。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
推荐阅读
- php - 如何使用常量创建 mysqli 表?
- sql - 冲突中的 Postgres 会更新复合主键
- php - 有没有办法转换文本框值并分配给 php 变量?
- iis - S3 图像 url 不适用于水晶报告 2008
- javascript - 从键/值对创建复杂对象
- python - 如何比较现在时间和选择时间
- r - 将日期转换为月份和年份月份的虚拟日期
- routing - 如何将流量从我的 VM 上的 IP 路由到 Kubernetes 的 NodePort?
- javascript - 使用 AJAX 和 JQUERY 和 Rails 的错误评论位置
- xamarin.forms - 无法使用正确的设置访问 AzureAD 中的公司租户