0

I have a Vue component, that creates a number of inputs based on the data fetched by the API. I run the values from the JSON through a method, that in turn converts this value to an input field if a certain condition matches. Here's how my component looks:

<template>
    <div>
        <div v-for="(val, index) in data">
            <span>{{val.key}}</span>
            <span :inner-html.prop="checkForEdit(val)"></span>
        </div>
        <b-button @click="submitData">Save</b-button>
    <div>
</template>

export default {
    name: "SomeComp",
    data() {
        return {
            dynamicVars: {}
        }
    },
    methods: {
        ...mapActions("api", ["getData"]),
        checkForEdit(value) {
            if(!value) return '';
            if(value.mustEdit) {
                this.dynamicVars[value.key] = '';
                return '<input type="text" value= "'+ value.text +'" :model='+ this.dynamicVars[value.key]+'>';
            } else {
                return value.text;
            }
        },
        submitData() {
            console.log(this.dynamicVars); //Only the key is present, no value updated
        }
    },
    created() {
        this.getData();
    },
    computed: {
        ...mapState("api", ["data"]),
    }
};

Here's how the data looks:

[
    {key: 'name', text: 'John', mustEdit: false},
    {key: 'age', text: '100', mustEdit: true}
]

This data can be anything, the fields are not fixed, only the format is. So I want to create dynamic vars object on the fly to send to the API that saves it.

Right now it only creates the variable inside dynamicVars however it doesn't seem that it actually reacts when I change the field value.

1 Answer 1

3

innerHTML has no clue about Vue templates, bindings and reactivity - if you set domNode.innerHTML = '<input v-model="variable">'; the browser will simply ignore the attribute v-model as it is not a standard HTML5 attribute.

You need to switch your mental model and instead of thinking in imperative terms try to think declaratively - just write in your template what you want to do and Vue will do the magic for you. It is such a blessing that we have Vue to do things for us instead of writing code in the old jQuery-style.

<template>
    <div>
        <div v-for="(val, index) in data" :key="index">
            <label :for="'input_' + index">{{val.key}}</label>
            <input v-if="val.mustEdit" :id="'input_' + index" :model='val.text'>
            <span v-else>{{ val.text }}</span>
        </div>
        <b-button @click="submitData">Save</b-button>
    <div>
</template>

export default {
    name: "SomeComp",
    methods: {
        ...mapActions("api", ["getData"]),
        submitData() {
            console.log(this.data);
        }
    },
    created() {
        this.getData();
    },
    computed: {
        ...mapState("api", ["data"]),
    }
};
Sign up to request clarification or add additional context in comments.

3 Comments

Aha, I never thought in this way. Let me try this! Btw I ended up using Vue.set to no avail.
But, I have to create a separate object with the edited fields, API constraints. How would I do it then? I am trying your solution as we speak.
You can clone your original object and when you are ready to save the changes - you will iterate your cloned object key by key and compare its text value with the corresponding value in the original object and thus you will find which fields have been actually edited and then send to API only those. Or you can handle the @input event of the INPUT and mark the value as dirty - @input="val.modified = true" and then you will send to API data.filter(obj => obj.modified) elements

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.