Search code examples
vue.jsvuex

Vue & Vuex - How to enable form submit on any input / state change?


I have a form with a submit button that is disabled by default. How do I make it enabled by any changes, without setting up a watcher for each input or some other way??

The Vuex mapState is empty when the template is mounted or created, so I can not deep clone that and compare with deep-diff for instance.. (also I did read its bad practice to deep clone states into variables).

My button:

<v-btn color="green" @click="onUpdateProfile" :disabled="!anyInputsChanged" :loading="false">Save</v-btn>

My input fields:

<InputField
  label="Förnamn"
  v-model="first_name"
/>
<InputField
  label="Efternamn"
  v-model="last_name"
/>

My script

import { mapState } from 'vuex'
import { mapFields } from 'vuex-map-fields'

import FormCard from '@/components/FormCard.vue'
import InputField from '@/components/base_components/InputField.vue'

export default {
  components: {
    FormCard,
    InputField,
  },
  data () {
    return {
      loading: true,
      isDisabled: true,
    }
  },
  computed: {
    ...mapState(['user', 'userProfile']),
    ...mapFields([
        'userProfile.first_name',
        'userProfile.last_name',
        'userProfile.fortnox_data.api_key'
    ]),
  },
  watch: {
  },
  mounted: function() {
    this.loading = false;
  },
  methods: {
    onUpdateProfile () {
      this.$store.dispatch('updateUserData', {
        id: this.user.uid,
        first_name: this.userProfile.first_name,
        last_name: this.userProfile.last_name,
        updated_at: new Date().toISOString(),
      })
    },
  }
}

the "anyInputsChanged" variable / computed / whatever I have not defined because I do not know how to. And bascially thats my question.. I want to enable "Submit" if any input element has changed, and disable if they all are unchanged.


Solution

  • Assuming you're using the basic updateField mutation, you could add a Vuex subscriber to listen for any commits and update an extra piece of state.

    For example

    import Vue from 'vue';
    import Vuex from 'vuex';
    
    import { getField, updateField } from 'vuex-map-fields';
    
    Vue.use(Vuex);
    
    const store = new Vuex.Store({
      strict: true,
      state: {
        modified: false, // 👈 add this one
        userProfile: {
          // whatever you got
        }
      },
      getters: {
        getField,
      },
      mutations: {
        updateField,
        setModified: (state, modified) => state.modified = !!modified
      },
      // actions, etc
    });
    
    // subscribe to "updateField" mutations
    store.subscribe((mutation, state) => {
      if (mutation.type === "updateField") {
        store.commit("setModified", true)
      }
    })
    
    export default store
    

    You can then use the modified state to control your button. Super simple example but you'd probably want to use mapState or similar.

    <v-btn 
      color="green"
      @click="onUpdateProfile"
      :disabled="!$store.state.modified"
      :loading="false"
    >Save</v-btn>
    

    The last thing to do after that would be to commit modified back to false in your updateUserAction action to reset the state.