I'm quite new with Vue, and webdev in general. Am creating a simple hiking pack list as a school project. Have a component 'PackItem' that consists of input fields for brand, model and weight. In the backend I have a database with some items that the user can pick from. When selecting a brand, the model input will show a list of models for that brand, and when a model is chosen the weight input gets filled automatically.
<template>
<div class="inputRow">
<h1> Brand: {{ this.brand }}</h1>
<input
type="text"
list="brands"
placeholder="Brand"
v-model="this.brand"
/>
<datalist id="brands">
<option
v-for="(suggested_brand, index) in getProductBrands"
:key="index"
:value="suggested_brand"/>
</datalist>
<input
type="text"
list="models"
v-model="this.model"
/>
<datalist id="models"> -->
<option
v-for="(suggested_model, index) in brandModels"
:key="index"
:value="suggested_model"/>
</datalist>
<input
class="product-inputs"
type="number"
name="Weight"
placeholder="Weight"
v-model="this.getProductWeight"
@change="this.updateItemOnStore($event.target.value);"/>
</div>
</template>
This is the component. The datalists and input gets their values from computed properties, that talk to vuex.
export default defineComponent({
name: 'PackItem',
props: {
},
data() {
return {
model: '',
brand: '',
weight: 0,
}
},
...
...
...
computed: {
getProductBrands(){
return this.$store.getters.productsBrandList
},
getProductWeight(){
let weight = this.$store.getters.productWeight(this.brand, this.model)
return weight
},
brandModels(){
if(this.brand === ''){
return []
}
console.debug('Item %d getting models for %s', this.uuid, this.brand)
let models = this.$store.getters.brandModels(this.brand)
return models
},
},
}
This all works great for the first PackItem, but when i spawn a second PackItem from the parent, the models list for the new PackItem will be the same as the first PackItem, regardless of choosing a different brand.
If I change computed property brandModels() to a method, and save the returning array from the store in local data and print it, I can see that the model list looks like it should, but those values are not showing up in the list for the user?
Image: Two items spawned with printouts
I can also see in the logs that I'm getting the correct models for the selected brand.
Item 0 getting models for Hyperlite Mountain Gear
index.js:209 Hyperlite Mountain Gear models: Windrider 3400,Junction 3400
index.js:221 Got weight 907g for product Hyperlite Mountain Gear Windrider 3400
index.js:64 Creating item 1 in category 0
index.js:199 Brands list: ZPacks,Hyperlite Mountain Gear,The OMM
index.js:225 Did not find weight for product in database!
index.js:199 Brands list: ZPacks,Hyperlite Mountain Gear,The OMM
PackItem.vue:119 Item 1 getting models for ZPacks
index.js:209 ZPacks models: Arc Blast
index.js:225 Did not find weight for product ZPacks Windrider 3400 in database!
So seems to me like I'm fetching all the correct data, and as far as I can see it should be displayed in the browser, but for some reason is not. I have no idea what is going on here...
As suggested by @yoduh I've fetched the whole products table into the component, and do computed values there instead of the supposedly broken computed values from getters in vuex. This has unfortunately not fixed it. Can still see in logs that brandModels() creates a correct list of models, but the datalist still shows the wrong ones.
computed: {
getProductBrands(){
let brands = []
this.dbProducts.forEach(product => {
//var json = JSON.parse(product);
var brand = product.brand;
if(!brands.includes(brand)){
console.debug('Adding brand %s to brands list', brand)
brands.push(brand)
}
} )
console.log('Brands list: %s', brands)
return brands
},
brandModels(){
if(this.brand === '') {return }
let models = []
this.dbProducts.filter(
product => product.brand === this.brand)
.forEach(product => models.push(product.model)
)
console.debug('%s models: %s', this.brand, models)
return models
},
getProductWeight(){
if(this.brand === '' || this.model === ''){ return }
let product = this.dbProducts.find(
product => (
product.brand === this.brand && product.model == this.model
))
if(product){
let weight = product.weightGrams
console.debug('Got weight %dg for product %s %s', weight, this.brand, this.model)
return weight
}
console.debug('Did not find weight for product %s %s in database!', this.brand, this.model)
return 0
},
},
Turns out the actual issue was with the pair of elements <InputText>
and <datalist>
on every item having the same id
and list
values. The value for list
and id
connect the two elements together, but once a second item is added, another pair of <InputText>
and <datalist>
with the same id
and list
values is created. Because no two elements should ever share the same id
, the connection between inputs and datalists becomes confused and broken.
The solution then is to bind unique values to the id
/list
attributes. I used the uuid
of each item since it's a unique number:
PackItem.vue
<InputText
type="text"
:list="`models${uuid}`"
placeholder="Model"
class="p-inputtext"
v-model="this.model"
/>
<datalist :id="`models${uuid}`">
<option
v-for="(suggested_model, index) in brandModels"
:key="index"
:value="suggested_model"
/>
</datalist>