Search code examples
vuejs3vue-composition-apipinia

Handling form input fields with Vue Composition API and Pinia store, can all v-model variables be organized into a single object?


I am using Vue3 with the Composition API and Pinia. When building a form component with various input fields, I've been handling it like this example, where each input is assigned it's own variable (binding to v-model) and then returned:

// UserForm.vue

<template>
  <div>
    <form @submit.prevent="addUser">
        <div><input v-model="firstName" type="text" /></div>
        <div><input v-model="lastName" type="text" /></div>
        <div><input v-model="email" type="text" /></div>
        // Lots more inputs
    </form>
  </div>
</template>

<script>
import { ref } from "vue";
import { useUserStore } from "../store/useUserStore";
    
export default {
  setup() {
    const firstName = ref("");
    const lastName = ref("");
    const email = ref("");
    
    const userStore = useUserStore();

    function addUser(firstName, lastName, email) {
        userStore.addUser(firstName, lastName, email);
    }

    return { firstName, lastName, email, addUser, userStore };
  },
};
</script>
// useUserStore.js
import { defineStore } from "pinia";

export const useUserStore = defineStore('user', {
  state: () => ({
    users: [],
  }),
  actions: {
    addUser(firstName, lastName, email) {
      this.users.push({ firstName, lastName, email })
    },
  },
});

I would prefer to assign all form inputs to a formData object and simply return the object (instead of a long list of individual variables). Then the template would look like this:

<template>
  <div>
    <form @submit.prevent="addUser">
        <div><input v-model="formData.firstName" type="text" /></div>
        <div><input v-model="formData.lastName" type="text" /></div>
        <div><input v-model="formData.email" type="text" /></div>
        // Lots more inputs
    </form>
  </div>
</template>

Is this possible? How do I use an object for v-model bindings to store all form inputs?

I tried using a formData object like this but it's not correct:

const formData = {
    firstName: ref(""),
    lastName: ref(""),
    email: ref("")
};

Solution

  • You can achieve the desired result using Vue's reactive() method. Here's a link to the documentation

    So you would do this instead:

    const formData = reactive({
        firstName: "",
        lastName: "",
        email: ""
    });
    

    and can access the values in the template as such:

    <template>
      <div>
        <form @submit.prevent="addUser">
            <div><input v-model="formData.firstName" type="text" /></div>
            <div><input v-model="formData.lastName" type="text" /></div>
            <div><input v-model="formData.email" type="text" /></div>
            // Lots more inputs
        </form>
      </div>
    </template>
    

    I hope that helps.