'How do I watch for onChange of specific select menu that was dynamically generated?

I have a row of 3 inputs. It lay out like this:

Attribute (select) > Operators (select) > Value (text)

UI look like this:

enter image description here

Code for that:

<v-row v-for="(rule, index) in rules" :key="index">
    <v-col cols="12" sm="6" md="3">
        <v-select
            dense
            outlined
            item-text="name"
            item-value="id"
            label="Attribute"
            v-model="form.values.attributes"
            :items="attributes"
        ></v-select>
    </v-col>

    <v-col cols="12" sm="6" md="2">
        <v-select
            outlined
            dense
            item-text="name"
            item-value="id"
            label="Operator"
            v-model="rule.operator"
            :items="operators"
        ></v-select>
    </v-col>

    <v-col cols="12" sm="6" md="4">
        <v-text-field
            dense
            outlined
            label="Values"
            v-model="rule.value"
        ></v-text-field>
    </v-col>
</v-row>

The operator menu is dynamically populated based on API based on the selected attribute.

I have v-model="form.values.attributes", and my watch to call the API for the operators

watch: {
    'form.values.attributes'() {
        let data = {
            $root: 'rules'
        }
        axios.defaults.headers['Content-Type'] = 'application/json'
        axios.post(window.MTS_URL, data).then((response) => {
            this.operators = response.data.operators
        })
    }
}

This works great with one row.

But when I clicked Add, I appended another row, and I need to support multiple rows.

enter image description here

How do I watch for onChange of specific select menu ?


I was thinking to move my

v-model="form.values.attributes"

to

v-model="rule.attribute"

but is it the right thing to do ?



Solution 1:[1]

You can try remodeling your data to experience the power of Vue.

Your template:

<template>
    <v-app>
        <v-row v-for="(rule, index) in rules" :key="index">
            <v-col cols="12" sm="6" md="3">
                <v-select
                    dense
                    outlined
                    item-text="name"
                    item-value="id"
                    label="Attribute"
                    :items="rule.attributes"
                    @change="onChange($event, rule, index)"
                ></v-select>
            </v-col>

            <v-col cols="12" sm="6" md="2">
                <v-select
                    outlined
                    dense
                    item-text="name"
                    item-value="id"
                    label="Operator"
                    :items="rule.operators"
                ></v-select>
            </v-col>

            <v-col cols="12" sm="6" md="4">
                <v-text-field dense outlined label="Values"></v-text-field>
            </v-col>
        </v-row>
    </v-app>
</template>

Notice how everything is rendered in the rules data. Here the rules data will be an array containing all the rows:

rules = [
    { 
        attributes: ["11", "22"],
        operators: []
    }
]

Your script should look like below:

<script>
import axios from "axios"

export default {
    name: "App",
    data() {
      return {
        rules: [{
           attributes: ["11", "22", "33"],
           operators: [],
        }],
      };
    },
    methods: {
        async onChange(changed, rule, index) {
            // where:
            // `changed` is the value selected
            // `rule` is the attributes and rule in the selected row
            // `index` is the index in the rules array that just got changed
            console.log(changed, rule, index);
            const operators = await this.fetchApiData(changed);

            this.$set(this.rules, index, { ...rule, operators: operators });
        },
    },
    async fetchApiData(data) {
        const response = await axios.post(window.MTS_URL, data);
        return response.data.operators;
    },
};
</script>

if you want to add more rows, you just need to push an object to the rules data:

this.rules.push({
    attributes: ["new attribute 1", "new attribute 2"],
    operators: []
})

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Onwuka Gideon