Frontend Development

Creating Multi-step Form With Vuejs and Vuex Part 1

vuejs multi-step form

Vuejs is one of the powerful frontend frameworks that makes use of components to separate application into small sections and each section have it’s own logic.

Let’s implement in this series how to create a multi-step form with Vuejs. You may already saw multi-step forms a lot using jquery but in this tutorial we will make use of Vue components to accomplish this in easy way.

 

Requirements:

  • Basic knowledge of html, css, and javascript.
  • Basic knowledge of Vuejs framework.
  • nodejs and npm installation required.
  • http server (apache for example).

 

For the sake of this tutorial you must have nodejs  and npm installed. Also we will need an http server (you can use any http server in my case i will use apache) and this is for the Vue router to work properly.

 

Setup up the project:

Let’s create a new folder named multi-step in your http server root directory with the following structure:

vuejs multi-step from

Package.json

Now add the below contents into your package.json:

{
  "name": "vuejsproject",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "WEBPACK_ENV=dev webpack --progress --colors --watch",
    "build": "WEBPACK_ENV=production webpack"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-polyfill": "^6.26.0",
    "babel-preset-es2015": "^6.24.1",
    "bootstrap": "^4.1.3",
    "css-loader": "^1.0.0",
    "path": "^0.12.7",
    "uglifyjs-webpack-plugin": "^1.2.7",
    "vue": "^2.5.16",
    "vue-loader": "^15.2.6",
    "vue-router": "^3.0.1",
    "vue-template-compiler": "^2.5.17",
    "vuex": "^3.0.1",
    "webpack": "^4.16.4"
  },
  "devDependencies": {
    "babel-plugin-transform-object-rest-spread": "^6.26.0",
    "webpack-cli": "^3.1.0"
  }
}

Then open the terminal and run this command:

npm install

Just wait until npm finishes installation then it’s time to setup webpack.

 

Setup webpack.config:

We will use new syntax of Ecmascript in this tutorial like imports and exports features so open your webpack.config.js and add the below code:

var webpack = require('webpack');
var path = require('path');
const { VueLoaderPlugin } = require('vue-loader')


// Naming and path settings
var appName = 'app';
var entryPoint = './app.js';
var exportPath = path.resolve(__dirname, './build');

// Enviroment flag
var plugins = [];
var env = process.env.WEBPACK_ENV;

// Differ settings based on production flag
if (env === 'production') {
  var mode = 'production';
  var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;

  plugins.push(new UglifyJsPlugin({ minimize: true }));
  plugins.push(new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }
  ));

  appName = appName + '.min.js';
} else {
  appName = appName + '.js';
  var mode = 'development';
}

// Main Settings config
module.exports = {
  mode: mode,
  entry: entryPoint,
  output: {
    path: exportPath,
    filename: appName
  },
  devServer: {
    historyApiFallback: true
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015']
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
        {
            test: /\.css$/,
            use: [
                'vue-style-loader',
                {
                    loader: 'css-loader',
                    options: {
                        // enable CSS Modules
                        modules: true,
                        // customize generated class names
                        localIdentName: '[local]_[hash:base64:8]'
                    }
                }
            ]
        }
    ]
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
  plugins: [
    new VueLoaderPlugin()
  ]
};

In the above tell webpack that the entry point will be app.js and the to store the bundled js file into build/app.js and also the loaders that compile the javascript and vue files.

 

Index.html:

Open up index.html and add the below contents:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Multi step form</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
  </head>
  <body>
    <base href="http://localhost/multi-step" />
    <div id="app"></div>
    <script src="build/app.js"></script>
  </body>
</html>

Note about this element:

<base href="http://localhost/multi-step" />

This tag is to tell vue router to load routes relative to this url.

As shown above i use bootstrap 4 css library and added a div with id of app and this will be the the root element that will render our Vue app and finally added the bundled javascript file.

 

Setup app.js:

At this point let’s open app.js and add the below code:

import 'babel-polyfill'
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './components/App.vue'

Vue.use(VueRouter);

import { routes } from './routes/index'

let router = new VueRouter({
	mode: 'hash',
	routes
});

new Vue({
	el: "#app",
	router,
	render: h => h(App)
})

In the above code we imported Vue, VueRouter, and the base App component. Then we loaded the vue router and provided the mode and routes. Finally we create a new Vue instance and we passed the el parameter and the router.

new Vue({
	el: "#app",
	router,
	render: h => h(App)
})

 

Now open components/App.vue and add the below code:

<template>
	
	<div class="container">

		<div class="row justify-content-md-center">
			
			<div class="col-sm-8">

				Form will be shown here

			</div>
		</div>
	</div>

</template>

<script>
	export default {

	}
</script>

Now run this command into terminal:

npm run dev

Then go to the browser and type http://localhost/multistep/ you will see the landing page working.

 

Setup The Form:

Let’s setup our form steps. For this we will create sub components and add them to the Vue router.

Go to components/steps folder and create four vue components:

  • FirstStep.vue
  • SecondStep.vue
  • ThirdStep.vue
  • Buttons.vue (this will hold the previous and next buttons)

Now open routes/index.js to add the routes as shown below:

import FirstStep from '../components/steps/FirstStep.vue';
import SecondStep from '../components/steps/SecondStep.vue';
import ThirdStep from '../components/steps/ThirdStep.vue';

export const routes = [
	{ path: '/first-step', name: 'firstStep', component: FirstStep },
	{ path: '/second-step', name: 'secondStep', component: SecondStep },
	{ path: '/third-step', name: 'thirdStep', component: ThirdStep }
];

Open components/steps/FirstStep.vue and add this code:

<template>
	
	<div class="text-center border border-light p-5">

		    <p class="h4 mb-4">Basic Details</p>


		    <div class="form-row mb-4">
		        <div class="col">
		            <!-- First name -->
		            <input type="text" id="firstname" name="firstname" class="form-control" placeholder="First name">
		        </div>
		        <div class="col">
		            <!-- Last name -->
		            <input type="text" id="lastname" name="lastname" class="form-control" placeholder="Last name">
		        </div>
			</div>

		    <!-- Email -->
		    <input type="email" id="useremail" name="email" class="form-control mb-4" placeholder="E-mail">

		    <!-- Password -->
		    <input type="password" id="password" name="password" class="form-control" placeholder="Password" aria-describedby="defaultRegisterFormPasswordHelpBlock">
		    <small id="defaultRegisterFormPasswordHelpBlock" class="form-text text-muted mb-4">
		        At least 8 characters and 1 digit
		    </small>

		    <!-- Gender -->
		    <select class="browser-default custom-select mb-4" name="gender">
		        <option value="" disabled selected>Choose gender</option>
		        <option value="1">Male</option>
		        <option value="2">Female</option>
		    </select>

		    <input type="text" id="phone" name="phone" class="form-control mb-4" placeholder="Phone">


		    <input type="date" id="birthdate" name="birthdate" class="form-control mb-4" placeholder="Birth date">

		    <buttons></buttons>

	</div>

</template>

<script>
	import Buttons from './Buttons.vue';

	export default {
		components: {
		    Buttons
		}
	}
</script>

components/steps/SecondStep.vue

<template>
	
	<div class="text-center border border-light p-5">

		    <p class="h4 mb-4">Address Details</p>


			<input type="text" id="country" name="country" class="form-control mb-4" placeholder="Country">

			<input type="text" id="state" name="state" class="form-control mb-4" placeholder="State">

		    <input type="text" id="city" name="city" class="form-control mb-4" placeholder="City">


            <div class="form-row mb-4">
		        <div class="col">
		            <input type="text" id="street" name="street" class="form-control" placeholder="Street Address">
		        </div>
		        <div class="col">
		            <input type="text" id="zipcode" name="zipcode" class="form-control" placeholder="ZipCode">
		        </div>
			</div>


            <buttons></buttons>

	</div>

</template>

<script>
    import Buttons from './Buttons.vue';

	export default {
        components: {
            Buttons
        }
	}
</script>

components/steps/ThirdStep.vue

<template>
	
		<div class="text-center border border-light p-5">

		    <p class="h4 mb-4">Social Links</p>


			<input type="text" id="facebook" name="facebook" class="form-control mb-4" placeholder="Facebook">

		    <input type="text" id="google" name="google" class="form-control mb-4" placeholder="Google">

		    <input type="text" id="twitter" name="twitter" class="form-control mb-4" placeholder="Twitter">

		    <input type="text" id="linkedin" name="linkedin" class="form-control mb-4" placeholder="Linkedin">

		    <input type="text" id="instagram" name="instagram" class="form-control mb-4" placeholder="Instagram">

			<buttons></buttons>

		</div>

</template>

<script>

	import Buttons from './Buttons.vue';

	export default {
        components: {
            Buttons
        }
	}
</script>

components/steps/Buttons.vue

<template>

    <div>
        <button class="btn btn-warning" @click="navigatePrev" type="button" v-show="togglePrevious()">Previous</button>

        <button class="btn btn-info" @click="navigateNext" type="button">{{ nextBtnTxt() }}</button>
    </div>

</template>

<script>
    export default {

        data() {

            return {

            }
        },
        methods: {
            nextBtnTxt() {
              if(this.$route.name == 'thirdStep') {

                  return 'Finish';
              }

              return 'Next';
            },
            togglePrevious() {
                if(this.$route.name == 'firstStep') {

                    return false;
                }

                return true;
            },
            navigateNext() {

                this.nextBtnTxt = 'Next';

                if(this.$route.name == 'firstStep') {

                    this.$router.push('/second-step/');
                } else if(this.$route.name == 'secondStep') {

                    this.$router.push('/third-step/');

                } else {

                    this.nextBtnTxt = 'Finish';

                    // handle saving of the form
                }
            },
            navigatePrev() {
                this.nextBtnTxt = 'Next';

                if(this.$route.name == 'thirdStep') {

                    this.$router.push('/second-step/');
                } else if(this.$route.name == 'secondStep') {

                    this.$router.push('/first-step/');
                }
            }
        }

    }
</script>

As shown above we created three components that will represent our steps then we added another component for the buttons then we setup the click handler for buttons like so:

navigateNext() {

                this.nextBtnTxt = 'Next';

                if(this.$route.name == 'firstStep') {

                    this.$router.push('/second-step/');
                } else if(this.$route.name == 'secondStep') {

                    this.$router.push('/third-step/');

                } else {

                    this.nextBtnTxt = 'Finish';

                    // handle saving of the form
                }
            },
            navigatePrev() {
                this.nextBtnTxt = 'Next';

                if(this.$route.name == 'thirdStep') {

                    this.$router.push('/second-step/');
                } else if(this.$route.name == 'secondStep') {

                    this.$router.push('/first-step/');
                }
            }

Here i just do a check for the current page then navigate to the next. And if i click on the previous button it will navigate backwards.



Now let’s modify our components/App.vue and add the router-view like this:

<template>
	
	<div class="container">

		<div class="row justify-content-md-center">
			
			<div class="col-sm-8">

				<router-view></router-view>

			</div>
		</div>
	</div>

</template>

<script>
	export default {

	}
</script>

Now refresh the browser and navigate to

http://localhost/multi-step/#/first-step/

if you click next or previous it will move to next and previous steps.

 

At this point there no way to get the data out of the form and this that we will do in the next part of the series just click here.

3.6 13 votes
Article Rating

What's your reaction?

Excited
4
Happy
3
Not Sure
3
Confused
5

You may also like

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments