Check out the code snippet below.
I seem to be stuck on a crucial component. Despite going through the documentation and watching tutorials on Vue2, I can't seem to grasp this key element. Any assistance would be greatly appreciated. If my approach is completely off, please feel free to point that out as I'm open to suggestions.
Desired functionality: I have a Vue instance named order
which contains line items
.
Upon order.mounted()
, we make an API call to fetch the order data, including any existing line items. If there are existing line items, we update the order data with this information (
this.lineitems = request.body.lineitems
or something similar). This part is working correctly, and I can calculate the order total since the line items are updated at this stage.
Each line item consists of an editable form with a quantity and a product field. Whenever the quantity or product of a line item is changed, I want the child component (line-item) to notify the parent component of the change. Subsequently, the parent component should update its line items data array with the new values and make a POST request with the current line item data to allow the server to recalculate the total (considering specials, discounts, etc). The updated line item data array returned by the server will then be passed down to re-render the line items.
Issues:
- The "update..." methods in the line-items components don't feel right, and I'm struggling to figure out how to update the parent's line items data array with the new data. For example:
lineitems = [
{id: 1000, quantity: 3, product: 555, total: 30.00},
{id: 1001, quantity: 2, product: 777, total: 10.00}
]
If the quantity of the second line item is changed to 1, how do I update the parent's line items data accordingly? My main challenge is understanding how the parent component can identify which line items in its data array need to be modified and how to retrieve data from the changed child. I assume the data comes in through an event emitted by the child component, but do I need to pass around the primary key everywhere for comparisons? What if it's a new line item without a primary key yet?
As mentioned above, I'm using the existing line item's DB primary key as the key in v-for. How should I handle adding a "new line item" that appends a blank line item below existing ones or in cases of a new order without primary keys?
Is there a recommended practice for handling props other than the "initial..." style I'm using? Should I directly use $emit on the
v-on
, and if so, how can I pass the relevant information through?
This task seems tailor-made for VueJS, yet I'm feeling like I'm going in circles. Any assistance would be greatly appreciated!
LineItem
Vue.component('line-item', {
props: ["initialQuantity", "initialProduct", "total"],
data () {
return {
// More properties here, but kept to a minimum 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 />
`
})
Order/App
var order = new Vue({
el: '#app',
data: {
orderPK: orderPK,
lineitems: []
},
mounted() {
this.fetchLineItems()
},
computed: {
total() {
// Should calculate the sum of line items, something like (li.total for li in this.lineitems)
return 0.0
},
methods: {
updateOrder(item) {
// First, update this.lineitems with the passed 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>
`
})