javascript - 如何让具有多个值的子组件更新父数据
问题描述
代码如下。
我想我在这里错过了一个关键的部分。我已经通过文档并一步一步地观看了整个 vue2。到目前为止一切都说得通,但我被困在似乎是核心部分的东西上。任何帮助,将不胜感激。如果这是完全错误的,请告诉我,我没有嫁给这些东西。
期望的功能:有一个order
Vue 实例,它有line items
.
在order.mounted()
我们点击订单数据的 api 端点,包括可能的现有订单项。如果存在现有订单项,我们将设置该订单数据(this.lineitems = request.body.lineitems
或类似数据)。这部分工作正常,我可以获得订单总额,因为此时订单的订单项是最新的。
每个行项目都是一个带有数量和产品的可编辑表单。如果我更改任何行项目的数量或产品,我希望子行项目组件通知父组件它已更改,然后父组件将使用新值更新其自己的行项目数据数组,并执行 POST 请求所有当前行项目数据,以便服务器端可以计算新的行项目总数(许多特价、折扣等)。这将为订单的订单项数据返回一个完整的替换数组,然后将其传递给订单项以重新呈现。
问题:
- 行项目组件“更新...”方法显然是错误的,但我最大的问题是了解如何让父项使用新数据更新其自己的行项目数据数组。例如
lineitems = [
{id: 1000, quantity: 3, product: 555, total: 30.00},
{id: 1001, quantity: 2, product: 777, total: 10.00}
]
如果第二个 line item 更改为数量 1,我如何让 parent 的 lineitems 数据更改为这个?我的主要问题是我不知道父级如何知道需要修改它自己的哪些 lineitems 数据数组,以及如何从更改后的子级获取数据。我假设它是通过一个事件,通过发射进入的,但是我现在是否需要在任何地方传递主键,以便我可以进行循环和比较?如果它是一个新的行项目并且还没有主键怎么办?
上面提到,我使用现有订单项的数据库主键作为 v-for 键。如果我需要一个“新的 lineitem”,在现有的 lineitem 下方附加一个空白 lineitem,或者如果它是一个没有主键的新订单。这个一般是怎么处理的。
是否有用于道具而不是我的“初始......”风格的最佳实践?我假设只是在 $emit 上直接使用
v-on
,但我不确定如何获取相关信息以通过这种方式。
这似乎正是 VueJS 适合的任务,我只是觉得我一直在朝着错误的方向追赶我的尾巴。谢谢您的帮助!
订单项
Vue.component('line-item', {
props: ["initialQuantity", "initialProduct", "total"],
data () {
return {
// There are more but limiting for example
quantity: initialQuantity,
product: initialProduct,
productOptions = [
{ id: 333, text: "Product A"},
{ id: 555, text: "Product B"},
{ id: 777, text: "Product C"},
]
}
},
updateQuantity(event) {
item = {
quantity: event.target.value,
product: this.product
}
this.$emit('update-item', item)
},
updateProduct(event) {
item = {
quantity: this.quantity,
product: event.target.value
}
this.$emit('update-item', item)
}
template: `
<input :value="quantity" type="number" @input="updateQuantity">
<select :value="product" @input="updateProduct">
<option v-for="option in productOptions" v-bind:value="option.id"> {{ option.text }} </option>
</select>
Line Item Price: {{ total }}
<hr />
`
})
订单/应用
var order = new Vue({
el: '#app',
data: {
orderPK: orderPK,
lineitems: []
},
mounted() {
this.fetchLineItems()
},
computed: {
total() {
// This should sum the line items, like (li.total for li in this.lineitems)
return 0.0
},
methods: {
updateOrder(item) {
// First, somehow update this.lineitems with the passed in item, then
fetch(`domain.com/orders/${this.orderPK}/calculate`, this.lineitems)
.then(resp => resp.json())
.then(data => {
this.lineitems = data.lineitems;
})
},
fetchLineItems() {
fetch(`domain.com/api/orders/${this.orderPK}`)
.then(resp => resp.json())
.then(data => {
this.lineitems = data.lineitems;
})
},
},
template: `
<div>
<h2 id="total">Order total: {{ total }}</h2>
<line-item v-for="item in lineitems"
@update-item="updateOrder"
:key="item.id"
:quantity="item.quantity"
:product="item.product"
:total="item.total"
></line-item>
</div>
`
})
解决方案
这是您尝试中的问题列表,这些问题将阻止它显示任何内容,即
quantity: initialQuantity
, - 你quantity: this.initialQuantity
的意思是,... 等所有其他此类数据}
计算总数缺失- 您的
line-item
模板无效 - 您有多个“根”元素
然后还有一些小问题:
- 你想要
@change
选择的处理程序,而不是@input
,如果你的代码运行,你会看到区别, - 同样,您需要
@change
输入,否则您将发出获取请求以更改每次击键时的项目,可能不是您想要的
所以,尽管如此,我还是生成了一些可以满足你所有需要的工作代码——不过,公平地说,主要是为了我自己的“学习”:p
// ******** some dummy data and functions to emulate fetches
const products = [
{ id: 333, text: "Product A", unitPrice: 10},
{ id: 555, text: "Product B", unitPrice: 11},
{ id: 777, text: "Product C", unitPrice: 12},
];
let dummy = [
{id: 1, quantity:2, product: 333, total: 20},
{id: 2, quantity:3, product: 777, total: 36},
];
const getLineItems = () => new Promise(resolve => setTimeout(resolve, 1000, JSON.stringify({lineitems: dummy})));
const update = items => {
return new Promise(resolve => setTimeout(() => {
dummy = JSON.parse(items);
dummy.forEach(item =>
item.total = parseFloat(
(
item.quantity *
(products.find(p => p.id === item.product) || {unitPrice: 0}).unitPrice *
(item.quantity > 4 ? 0.9 : 1.0)
).toFixed(2)
)
);
let res = JSON.stringify({lineitems: dummy});
resolve(res);
}, 50));
}
//********* lineItem component
Vue.component('line-item', {
props: ["value"],
data () {
return {
productOptions: [
{ id: 333, text: "Product A"},
{ id: 555, text: "Product B"},
{ id: 777, text: "Product C"},
]
}
},
methods: {
doupdate() {
this.$emit('update-item', this.value.product);
}
},
template: `
<p>
<input v-model="value.quantity" type="number" @change="doupdate()"/>
<select v-model="value.product" @change="doupdate()">
<option v-for="option in productOptions" v-bind:value="option.id"> {{ option.text }} </option>
</select>
Line Item Price: {{ '$' + value.total.toFixed(2) }}
</p>
`
})
//********* Order/App
const orderPK = '';
var order = new Vue({
el: '#app',
data: {
orderPK: orderPK,
lineitems: []
},
mounted() {
// initial load
this.fetchLineItems();
},
computed: {
carttotal() {
return this.lineitems.reduce((a, {total}) => a + total, 0)
}
},
methods: {
updateOrder(productCode) {
// only call update if the updated item has a product code
if (productCode) {
// real code would be
// fetch(`domain.com/orders/${this.orderPK}/calculate`, this.lineitems).then(resp => resp.json())
// dummy code is
update(JSON.stringify(this.lineitems)).then(data => JSON.parse(data))
.then(data => this.lineitems = data.lineitems);
}
},
fetchLineItems() {
// real code would be
//fetch(`domain.com/api/orders/${this.orderPK}`).then(resp => resp.json())
// dummy code is
getLineItems().then(data => JSON.parse(data))
.then(data => this.lineitems = data.lineitems);
},
addLine() {
this.lineitems.push({
id: Math.max([this.lineitems.map(({id}) => id)]) + 1,
quantity:0,
product: 0,
total: 0
});
}
},
template: `
<div>
<h2 id="total">Order: {{lineitems.length}} items, total: {{'$'+carttotal.toFixed(2)}}</h2>
<line-item v-for="(item, index) in lineitems"
:key="item.id"
v-model="lineitems[index]"
@update-item="updateOrder"
/>
<button @click="addLine()">
Add item
</button>
</div>
`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
</div>
注意:里面可能有一些效率低下的代码,请不要判断太苛刻,我才用vuejs一周
推荐阅读
- ios - 在另一个 View Xcode 中显示 ViewController
- c# - PDFClown 无法编辑创建的 PDF
- scala - 具有树结构的 Akka 流
- javascript - 无法在 XmlHttpRequest 中分配“Access-Control-Allow-Origin”
- dart - 有什么方法可以在颤振中创建 3D 图表
- ios - 在 ARKit 中何时使用 worldTransform() 以及何时使用 transform()
- javascript - 值未更新
- python - 多个异步循环
- javascript - 开玩笑不能在反应中转换静态
- php - url-masking 使用 .htaccess 将 http 重定向到 https