Forms are one of the most important aspects of any application. It is considered a good UX practice to keep the Save/Submit button disabled until the form contents have not changed. In this tutorial, you’ll take a look at how can you accomplish this behavior in a Nuxt/Vue app.
Contents
Creating a Form Template
First, you need to create a simple form that will help you to understand the concepts of computed and watch properties. In your index.vue file in the pages directory, add the following form template:
<template>
<form>
<label>Name</label>
<input v-model='form.name' />
<label>Age</label>
<input v-model='form.age' />
<button :disabled="!changed">Save</button>
<form>
</template>
In the above template, you have bounded your form data model to form inputs using v-model. The Save button will be disabled if the form fields have not changed.
Writing Vuex Code
In this example, you’ll use Vuex Store’s state, actions, and mutations to store state and fetch your form data. In your index.js file in the store directory, add the following code:
// initialize the state variables
export const state = () => ({
form: {}
})
export const actions = {
// action to setup form data
// you can also do an api call as well
init({ commit }) {
const data = {
name: 'Ravgeet',
age: '21',
}
// add a commit mutuation for changing the state
commit('setFormData', data)
}
}
export const mutations = {
// mutation to change the state
setFormData(state, data) {
state.form = data
}
}
Writing Computed and Watch properties
At this point, your template and store are set. Next, you need to implement your application logic in your template’s script. In the pages/index.vue file, add the following code below the template:
<script>
import { cloneDeep, isEqual } from 'lodash'
export default {
data() {
return {
changed: false, // useful for storing form change state
form: {}, // data variable to store current form data binding
}
},
computed: {
// store the original form data
originalForm() {
return this.$store.state.form
}
},
watch: {
// by watching the original form data
// create a clone of original form data
// and assign it to the form data variable
originalForm() {
this.form = cloneDeep(this.originalForm)
},
// watch the changes on the form data variable
form: {
handler() {
// using lodash to compare original form data and current form data
this.changed = !isEqual(this.form, this.originalForm)
},
// useful to watch deeply nested properties on a data variable
deep: true,
},
},
created() {
// dispatch an action to fill the store with data
this.$store.dispatch('init')
}
}
</script>
In your terminal, run the following command to install lodash:
$ npm install lodash
In the above code:
- When the component is created, the
initaction is dispatched using thecreatedhook. This action causes a mutation in the store and fills theformstate variable. - The value of the computed property,
originalFormis calculated as it is dependent upon theformstate variable. - As the value of
originalFormis being watched using thewatchhook, the code inside it is executed. A deep clone oforiginalFormis made and assigned to theformdata variable. - Since the value of
formis being watched, you use ahandleranddeepproperty to run your business logic. You simply check whether theformandoriginalFormvariables are equal using Lodash.
At first, it looks like something very complex is going on but once you break down the things it makes sense.
Results
Navigate to your browser and check whether you have been able to achieve your purpose of disabling the form submit button if the form fields have not changed at all.

Voila! You have successfully implemented your workflow. This adds to the UX of your application and saves the user from frustration especially in long forms.