Search code examples
javascriptvue.jsvuejs2vuex

Edit form for data stored in Vuex and mapped as computed computed properties


I have a form component used for client registration and edit. When the form component is created I first check if the URL has an ID or not to determine what type of form to display. If the form has no ID ( meaning register new client ) all fields should be empty. And if the URL has an ID then I load the client info via an Axios request then store the data in Vuex and display the data in the fields for modifications.

     <Form enctype="multipart/form-data" @submit="$emit('submit')">
         <validation-provider
                     name="Compnay name"
                          >
                      <v-text-field
                      v-model="details.company_name"
                              label="Compnay name"
                            />
                  <span class="text-sm text-red-500">{{ errors[0] }}</span>
          </validation-provider>
     <validation-provider
                     name="Compnay name"
                          >
                      <v-text-field
                      v-model="details.email"
                              label="Compnay email"
                            />
                  <span class="text-sm text-red-500">{{ errors[0] }}</span>
          </validation-provider>
     <validation-provider
                     name="Compnay phone"
                          >
                      <v-text-field
                      v-model="details.phone"
                              label="Compnay name"
                            />
                  <span class="text-sm text-red-500">{{ errors[0] }}</span>
          </validation-provider>
        ...

Then I set data properties here

    data: function () {
        return {
          details: {
            type: Object,
            default: () => ({})
    
          },

In create I did await this.$store.dispatch("clients/getClientById", { id: id}) to get the client info

The challenge I'm facing is after I get the client from computed I couldn't find a way to add them in v-model to send them with FormData or edit them. Can somebody guide me, please.

I attempted to do this. but I got an error saying Error: [vuex] do not mutate vuex store state outside mutation handlers. Once I type something in the field

    async getClientById(id){
          await this.$store.dispatch("clients/getClientById", {
            id: id})
            .then(res => {
            if (res.status === 200 ){
              let clientData = res.data.data
              this.details = clientData
              this.status = clientData.status
            }
          })

Solution

  • 1st

    Your "last attempt" doesn't work because of objects are Passed by Reference in JS so this.details in the end points to the object inside Vuex state and changing data inside Vuex directly outside of a mutation (by v-model in this case) is prohibited in strict mode (which is a good thing)

    You can fix it by making the copy of the object (will work only if members are simple values, not objects as spread operator is doing only shallow copy) ie:

    if (res.status === 200 ){
              let clientData = { ...res.data.data }
              this.details = clientData
              this.status = clientData.status
            }
    

    On forms

    In most Create/Edit forms you want some features:

    1. Form has Save/Cancel buttons so the user has a choice to "undo all the changes by single click"
    2. Validation - "Save" operation is allowed only if data entered is valid

    Best way to do it imho is to make the form so that all data are first copied from Vuex into local data of the form component and all inputs are bound to the local data. This has multiple benefits:

    1. When user types something, only local data changes and original data in Vuex are intact. Implementing "Cancel" button is then as easy as simply closing the form (and discarding all local data).

    2. Implementing "Save" is also easy - just check all validations are passing and then use mutation to commit changed data into Vuex store

    3. If the data under edit (stored in Vuex) are used in multiple places in your app, other "places" (components) doesn't "see" intermediate data which can be invalid during editing (for example you have two numbers in the store - A and B. Editing form for them and other component which displays result of A/B. If editing form is bound to the Vuex data directly and user changes B to 0, form displays validation error but your display component still sees the 0 in the store and throws an error)

    4. Form component code is simpler because binding to local reactive data is much easier then binding form controls directly to Vuex