Multi-step form using Vue, Vuex, Computed Setters And v-model
by darkylmnx
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.min.js"></script>
<div id="app">
<h1>Easily mail your package</h1>
<p>
<progress max="4" :value="percent"></progress>
</p>
<component :is="step_cmp"></component>
</div>
<script type="text/template" id="cmp-infos">
<form @submit.prevent="nextStep">
<h3>Who are you sending to</h3>
<p><input placeholder="firstname" v-model.lazy="firstname" required/></p>
<p><input placeholder="lastname" v-model.lazy="lastname" required/></p>
<button>next</button>
</form>
</script>
<script type="text/template" id="cmp-what">
<form @submit.prevent="nextStep">
<h3>what are you sending to : {{ $store.state.form.firstname }}</h3>
<p><input placeholder="item" v-model.lazy="item" required/></p>
<button type="button" @click="prevStep">prev</button>
<button>next</button>
</form>
</script>
<script type="text/template" id="cmp-where">
<form @submit.prevent="nextStep">
<h3>where are you sending : {{ $store.state.form.item }}</h3>
<p><input placeholder="enter address" v-model.lazy="to" required/></p>
<button type="button" @click="prevStep">prev</button>
<button>next</button>
</form>
</script>
<script type="text/template" id="cmp-final">
<form @submit.prevent="send">
<h3>please confirm : </h3>
<p>
{{ $store.state.form.firstname }}
{{ $store.state.form.lastname }}
<br/>
{{ $store.state.form.item }}
<br/>
{{ $store.state.form.to }}
</p>
<button>confirm</button>
</form>
</script>
CSS
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
h1, h2 {
font-weight: bold;
}
Vue
const STEPS = {
INFOS: 'INFOS',
WHAT: 'WHAT',
WHERE: 'WHERE',
FINAL: 'FINAL'
}
// infos component
Vue.component('cmp-infos', {
template: "#cmp-infos",
computed: {
firstname: {
get() {
return this.$store.state.form.firstname
},
set(firstname) {
this.$store.commit('SET_FORM', {
firstname
})
}
},
lastname: {
get() {
return this.$store.state.form.lastname
},
set(lastname) {
this.$store.commit('SET_FORM', {
lastname
})
}
}
},
methods: {
nextStep() {
this.$store.commit('SET_STEP', STEPS.WHAT)
}
}
})
Vue.component('cmp-what', {
template: '#cmp-what',
computed: {
item: {
get() {
return this.$store.state.form.item
},
set(item) {
this.$store.commit('SET_FORM', {
item
})
}
}
},
methods: {
prevStep() {
this.$store.commit('SET_STEP', STEPS.INFOS)
},
nextStep() {
this.$store.commit('SET_STEP', STEPS.WHERE)
}
}
})
Vue.component('cmp-where', {
template: '#cmp-where',
computed: {
to: {
get() {
return this.$store.state.form.to
},
set(to) {
this.$store.commit('SET_FORM', {
to
})
}
}
},
methods: {
prevStep() {
this.$store.commit('SET_STEP', STEPS.WHAT)
},
nextStep() {
this.$store.commit('SET_STEP', STEPS.FINAL)
}
}
})
Vue.component('cmp-final', {
template: '#cmp-final',
methods: {
send() {
// do what you want like api calls and reset the form
alert('thanks')
}
}
})
const store = new Vuex.Store({
state: {
step: STEPS.INFOS,
form: {
firstname: '',
lastname: '',
item: '',
to: ''
}
},
mutations: {
SET_FORM(state, payload) {
// merge previous state with new state
state.form = Object.assign({}, state.form, payload)
},
...