Search code examples
vue.jsdata-bindingvuejs3vue-composition-apivue-script-setup

Why is my bound data in a Vue 3 form not being updated when printed on the console via a function?


I have created a form component in Vue 3 where I am trying to bind a reference object to input elements and from my understanding of two-way binding and how Vue behaved for my past works, I expected any changes to affect my reference object immediately without requiring any manipulation like I would have had to for a React component by triggering the "onChange" react event.

However, when I click on the "Save" button to trigger a function that outputs the reference object, it only seems to keep my value loaded initially but not any changes I make from within the input. Can anybody explain why?

Here's the code for my component. It loads inside a pop-up window.

<script setup lang="ts">
    import type { Activity } from '@/models/activity';
    import { ref } from "vue";
    import Button from '../../common/Button.vue';

    defineEmits([
        "close-form"
    ]);
    const props = defineProps<{
        title?: String,
        activity?: Activity,
        editMode: Boolean
    }>();

    const initialState = props.activity ?? {
        id: '',
        title: '',
        category: '',
        description: '',
        date: '',
        city: '',
        venue: ''
    }

    const currActivity = ref<Activity>(initialState);

    const handleSubmit = () => {
        console.log(currActivity.value);
    }

    // const handleInputChange = (event: Event) => {
    //     const {name, value} = event.target as HTMLInputElement;
    //     console.log(name, value);
    //     // currActivity.value[name] = value;
    // }
</script>

<template>
    <form
        class="grid grid-cols-5 gap-4 mb-8"
        @submit="handleSubmit"
        autocomplete="off"
    >
        <h2
            v-if="title"
            class="col-span-5 mb-4 text-white text-3xl font-semibold"
        >
            {{ title }}
        </h2>

        <input type="text" placeholder='Title' class='col-span-3 mb-4 px-2 py-1 rounded' :value="currActivity.title" name="title" />
        <input type="text" placeholder='Category' class='mb-4 px-2 py-1 rounded' :value="currActivity.category" name="category" />
        <textarea placeholder='Description' class='col-span-5 mb-4 px-2 py-1 rounded' :value="currActivity.description" name="description" />
        <input type="text" placeholder='Date' class='px-2 py-1 rounded' :value="currActivity.date" name="date" />
        <input type="text" placeholder='City' class='px-2 py-1 rounded' :value="currActivity.city" name="city" />
        <input type="text" placeholder='Venue' class='px-2 py-1 rounded' :value="currActivity.venue" name="venue" />
        
        <div
            v-if="editMode && activity"
            class="flex justify-end col-span-2"
        >
            <Button
                text="Save"
                icon="fa-floppy-disk"
                type="send"
                @click="handleSubmit"
            />
            <Button
                text="Close"
                icon="fa-xmark"
                type="cancel"
                @click="$emit('close-form')"
            />
        </div>
        <div 
            v-else 
            class='flex justify-end col-span-2'
        >
            <Button
                text="Post"
                icon="fa-paper-plane"
                type="send"
                @click="handleSubmit"
            />
        </div>
    </form>
</template>

You may notice a commented function in my script. I tried to replicate the React approach and handle the changes on the change event triggered using "@change" on the input elements. I removed these later as that didn't seem to work. I kept the commented function however in case anyone finds it useful to locate the issue.


Solution

  • You need to make two-way binding using v-model instead of :value :

       <input type="text" placeholder='Title' 
            class='col-span-3 mb-4 px-2 py-1 rounded' v-model="currActivity.title" name="title" />