# Developing a UI with Vue, Part 5: The Multi-Step Form

 [In my last post](https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-4-slots-inputs-and-buttons) , we developed the Login page by building custom input and button components. Now, we're going to focus on building a **multi-step registration form** to sign up new users. 

The first time I built one of these I thought it was *the coolest*. I was also intimidated by the process, but after a few attempts and an understanding of what was happening, it became quite fun and simple to pull off. 

## Defining data properties

We're going to start by defining what fields we'll need to capture, and how many steps the user will have to go through:

- **Step 1** will gather the user's username and email
- **Step 2** will gather their password and confirmed password

In our Vue instance, we'll define those values as data properties. We're also going to add a number property called `step`, which we'll use to handle which piece of the form gets rendered to the user, as well as a boolean `error` flag.

```jsx
// Register.vue

export default {
  name: 'Register',
  data() {
    return {
      step: 1,
      name: '',
      email: '',
      password: '',
      confirm_password: '',
	  error: false,
    }
  },
}
```

## Using v-if

To build the each step of our form, we'll create two `<div>` elements, each containing the inputs we'll want displayed depending on the step. We can use the `v-if` directive to provide this conditional logic.

```html
<template v-slot:content>
  <form>
     <div v-if="step === 1">
        <BaseInput 
		  label="Name" 
	      placeholder="Enter your name..." 				
          v-model="name" 
	      required
		/>
        <BaseInput 
		  label="Email"
		  placeholder="Enter your email..." 
		  v-model="email" 
		  required
		/>
     </div>
     <div v-if="step === 2">
       <BaseInput 
		 label="Password" 
		 placeholder="Enter your password..." 
		 v-model="password" 
		 type="password" 
		 required
	   />
       <BaseInput 
	     label="Confirm password" 
		 placeholder="Confirm password..." 
		 v-model="confirmed_password" 
		 type="password" 
		 required
	   />
    </div>
  </form>
</template>
```

Notice we're using `v-model` to bind each value to its property in the `data()` object, and that we've set up our input component to accept the `required` prop. That prop is getting passed to the component's native HTML input, which takes some validation work off of our plate!

With that, we've got two steps available to display:


![vue_5_img2_register@2x.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1629246837517/bxrXy8GdN.jpeg)

**Now we need a way to change the step.** Let's take a look at how to create handlers to get that done.

## Handling the step

Each button component is set up to receive a `@click` handler as a prop. Here, we're going to define two handlers inside `methods`: one to increment the step, and one to decrement it.

```jsx
export default {
  // ...rest of the object
  methods: {
    incrementStep() {
	  const formFilled = this.username && this.email;
      if (this.step === 1 && formFilled) {
        this.step = this.step + 1;
      }
    },
    decrementStep() {
      if (this.step === 2) {
        this.step = this.step - 1;
      }
    },
  }
}
```

Each handler is set up with the following logic:

1. `incrementStep` only works when `step` is equal to 1, so that it can only get as far as 2.  I've also set up logic to ensure that `username` and `email` are provided before the user is able to move onto step 2.
2. `decrementStep` only works when `step` is equal to 2, so that it can only get as far as 1.

When we attach those handlers to the buttons, we'll use some `v-if` directives too. On step 1,  we want to allow the user to increment:

```html
<BaseButton v-if="step === 1" @click="incrementStep">Continue</BaseButton>
```

And on step 2 the user can either go back, or submit.

```html
<BaseButton v-if="step === 2" @click="decrementStep" variant="secondary">Back</BaseButton>
<BaseButton v-if="step === 2" type="submit">Confirm</BaseButton>
```

We'll place the buttons inside of a wrapper div in order to align them to the right with some spacing.

```html
<form>
  <div v-if="step === 1"><!-- Step 1 inputs --></div>
  <div v-if="step === 2"><!-- Step 2 inputs --></div>
  <div class="button-wrapper">
    <BaseButton 
	  v-if="step === 1" 
	  @click="incrementStep">Continue</BaseButton>
    <BaseButton 
	  v-if="step === 2" 
	  @click="decrementStep" 
	  variant="secondary">Back</BaseButton>
    <BaseButton 
	  v-if="step === 2" 
	  type="submit">Confirm</BaseButton>
  </div>
</form>
```

Now we can we interact with the form:

![vue_5_img1_register@2x.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1629246302419/yeLrs73Qo.jpeg)

## Handling form submission

To bring it all together, we need to handle the submission of our form. Without a back-end, there's nowhere to really send it. But we can create the handler with some validation to get us started.

Just as we did with the Login page, we are going to add a `@submit` handler to our form with the `prevent` event modifier. Then, we'll include the handler as a method in our export object:

```html
<form @submit.prevent="handleRegister">
	<!-- the form we've just built -->
</form>
```

```jsx
export default {
	methods: {
	  handleRegister() {
        const passwordsFilled = this.password && this.confirm_password;
        const passswordsMatch = this.password === this.confirm_password;
        if (passwordsFilled && passswordsMatch) {
	      this.error = false;
          // perform form submission...
        } else if (!passswordsMatch) {
          this.error = true;
        }
      },
	}
}
```

Let's break down the above validation in `handleRegister`:

1. We're making sure that `password` and `confirm_password` have values
2. We're making sure that `password` and `confirm_password` match.

If both are truthy, we'll continue on with form submission and clear errors. Otherwise, we set error to true and inform the user of the error, which appears right above the first input inside of the "header" named slot.

```html
<template v-slot:header>
  <h1>Register</h1>
  <p>Try out our watch list for free today and see what you think.</p>
  <p class="error" v-if="error">
    Either a password field is missing, or they do not match.
  </p>
 </template>
```

Now when there's a problem with the password, the user will see a message like this:

![vue_5_img3_register – 1@2x.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1629247125102/DRHkHD52o.jpeg)

At this point, we've basically hit a dead end until we get login working and build the actual app.

That's why I'm going to be be turning my attention to the much discussed **Supabase**: the open-source alternative to Google's Firebase that's becoming quite popular. I've always been drawn to UI, so platforms like this are *always* exciting for me to work with. See you soon  ✌️

---
### You can find me on [Twitter](https://twitter.com/matt_pigs) and  [Github](https://github.com/mpaitgt) , or you can check out my portfolio  [here](https://mattpignatore.dev/) . Feel free to reach out, I'd love to hear from you!
