DEV Community

Cover image for Create Dynamic Forms in Vue3.
Alvaro Saburido
Alvaro Saburido

Posted on • Edited on

Create Dynamic Forms in Vue3.

New Year, first article! Let's get started. 🤩

So after several months of lockdown in Spain and really relaxing holidays, I worked hard on a plugin to bring Dynamic Forms to Vue3 and Composition API and finally, the stable 3.x version was released yesterday 🥳.

But... what Dynamic Forms even mean? Well is basically a vue component that renders forms and inputs dynamically based on a data object/schema that represents the business logic.

<template>
  <dynamic-form :form="testForm" />
</template>

<script lang="ts">
...
setup() {
  const form = ref({
    id: 'test-form',
    fields: {
        username: TextField({
            label: 'Username',
        }),
        email: EmailField({
            label: 'email',
        }),
    }
  })

  return {
    form
  }
}
...
</script>
Enter fullscreen mode Exit fullscreen mode

No more huge template files, no more "We need to do a new release because the client wants to change the username input label" 🤯. Forms schemas can be async and forms generated on go with an easy-going validation approach. All that for only 26kB.

Still interested in quickly create forms in Vue 3.x? This article is for you.

quickly

What we are going to build?

Just a simple login form similar to the one here. We are going to cover

  • Email input with validation,
  • Password input with validation
  • Checkboxes
  • Form submission

Login Example

Create the demo app

For the app creation let's use Vite⚡️

yarn create @vitejs/app my-login-demo --template vue-ts
Enter fullscreen mode Exit fullscreen mode

Installation

To install just run:

yarn add @asigloo/vue-dynamic-forms

# or, using NPM
npm install @asigloo/vue-dynamic-forms
Enter fullscreen mode Exit fullscreen mode

The installation and usage have changed to align with the new Vue 3 initialization process.

To create a new plugin instance, use the createDynamicForms function.

// main.ts

import { createApp } from 'vue';
import { createDynamicForms } from '@asigloo/vue-dynamic-forms';

const VueDynamicForms = createDynamicForms({
  // Global Options go here
});

export const app = createApp(App);

app.use(VueDynamicForms);
Enter fullscreen mode Exit fullscreen mode

Theming

Vue Dynamic Forms is style agnostic, which means components have no predefined styles by default, so you can set them as you like. If you prefer a more ready-to-go solution for styling you can import a default theme file from the package like this and override the variables like this.

// styles.scss
$input-bg: #e2eb5d52;
$input-border-color: #aec64c;

@import '~@asigloo/vue-dynamic-forms/dist/themes/default.scss';
Enter fullscreen mode Exit fullscreen mode

Login Form

Go to your App.vue and add the <dynamic-forms /> component into your template:

<template>
  <div class="app">
    <dynamic-form
      :form="form"
    />
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Next step is to write down the form schema using a ref in the setup method

You can also define form as a computed property in case using vue-i18n labels or to reactively assign a value to any form property such as visibility or dropdown options

import { defineComponent, ref } from 'vue';
import { EmailField, PasswordField, CheckboxField } from '@asigloo/vue-dynamic-forms';

export default defineComponent({
  name: 'App',
  setup() {
    const form = ref({
      id: 'login-form',
      fields: {
        email: EmailField({
          label: 'Email',
        }),
        password: PasswordField({
          label: 'Password',
          autocomplete: 'current-password',
        }),
        rememberMe: CheckboxField({
          label: 'Remember Me',
        }),
      },
    });
    return {
      form,
    };
  },
});
Enter fullscreen mode Exit fullscreen mode

Let's open our browser and check our new form with the vue-devtools. If you don't have it install it yet and want to work with Vue3 support, I recommend you to install the Beta version on the chrome store here. It includes awesome new stuff such as a Timeline for component events.

Vue dynamic forms dev-tools

As you can see in the image above, each field was transformed into a FormControl object containing crucial info for its rendering and behavior. With this, you can easily check and debug your forms.

It took what? 5 minutes?. 🤭

I have spoken

Form submission

Now that we have our form in place, we want to do something with the input data. There are two ways to do it:

  1. Use a submit button to trigger a submit event. (Recommended option, it also check if the form is valid).
  2. Use the change event to get the latest state of the form values. (This one doesn't care about validation but it's helpful for autosave features for example)

Using a submit button.

If you add a button of type submit just below the <dynamic-form /> with the attribute form equal to the form.id it will trigger the form submission internally.

 <dynamic-form
   :form="form"
   @submitted="handleSubmit"
   @error="handleError"
 />
 <button
  class="btn"
  submit="true"
  :form="form?.id"
 >
  Sign In
 </button>
Enter fullscreen mode Exit fullscreen mode

For this road, we have two (2) possible events:

  1. submitted in case the validation went well and the form is valid (retrieve all values) ☑️
  2. error in case there is an error in the form (retrieves all errors) ❌

On change detection

The DynamicForm component also offers a change event in case you want to get the latest state of the form values right away. Is important to consider it will retrieve the values without considering validation, (errors will still be shown at UI level) so you probably want to do a second validation outside.

 <dynamic-form
   :form="form"
   @change="valuesChanged"
 />
Enter fullscreen mode Exit fullscreen mode
 setup() {
    const formValues = reactive({});
    const form = ref({
      id: 'login-form',
      fields: {
        // Form-fields
      },
    });

    function valuesChanged(values) {
      Object.assign(formValues, values);
      console.log('Values', values);
    }

    return {
      form,
      valuesChanged,
    };
  },
Enter fullscreen mode Exit fullscreen mode

Vue Dynamic Forms change detection

Validation

Normally, forms submit data to a backend service, we want to make sure that the data required is sent, and is sent correctly so we don't end with errors in the console or "limbo" states in our application.

Let's make our email and password fields required for the submission. Just add a property validations with an array of all the validation you want the field to have, in this case, let's import the required validator like this:

import { required, EmailField, Validator } from '@asigloo/vue-dynamic-forms`;
Enter fullscreen mode Exit fullscreen mode

Then add it to your field definition:

email: EmailField({
  label: 'Email',
  validations: [
     Validator({ validator: required, text: 'This field is required' }),
  ],
}),

Enter fullscreen mode Exit fullscreen mode

If you try to submit the form empty, or you touch and blur the input without value it will add error classes to your component so you can style it accordingly

Vue Dynamic Forms validation error

If you correct the validation, which in this case, it's just adding a value to the field and you blur, a success class will be added to the control

Vue Dynamic Forms validation success

What about checking if the email format is correct and adding a complex validation to your password?

By default, Vue Dynamic Forms contains the following validations:

  • required
  • min
  • max
  • email
  • URL
  • minLength
  • maxLength
  • pattern.

So let's use the email and pattern validator to our cause:

import {
  required,
  email,
  FormValidator,
  // ...
} from '@asigloo/vue-dynamic-forms';
Enter fullscreen mode Exit fullscreen mode
setup() {
    const emailValidator: FormValidator = {
      validator: email,
      text: 'Email format is incorrect',
    };
   // ...

   email: EmailField({
     label: 'Email',
     validations: [
       Validator({ validator: required, text: 'This field is required' }),
       emailValidator,
     ],
   }),
}
Enter fullscreen mode Exit fullscreen mode

Vue Dynamic Form email validation

Similar to this, let's use the pattern validation, this function is special because it takes an argument which is the regex pattern you want to apply to the validation.

import {
  required,
  email,
  FormValidator,
  // ...
} from '@asigloo/vue-dynamic-forms';
Enter fullscreen mode Exit fullscreen mode
setup() {
    const passwordValidator: FormValidator = {
      validator: pattern(
        '^(?=.*[a-z])(?=.*[A-Z])(?=.*)(?=.*[#$^+=!*()@%&]).{8,10}$',
      ),
      text:
        'Password must contain at least 1 Uppercase, 1 Lowercase, 1 number, 1 special character and 
        min 8 characters max 10',
    };
   // ...

    password: PasswordField({
      label: 'Password',
      autocomplete: 'current-password',
      validations: [
         Validator({ validator: required, text: 'This field is required' }),
         passwordValidator,
      ],
    }),
}
Enter fullscreen mode Exit fullscreen mode

Vue Dynamic Form password validation

Wrap Up

So that's pretty much it, you can check the complete solution here (it also shows how to use with TailwindCSS)

Of course, this is a pretty simple example, but I will post more use cases in the near future, such as async form data,i18n, custom fields, and third-party components

If you have any questions feel free to open a discussion on the comment section or ping me on Twitter @alvarosaburido. I'm always hanging around.

We're also looking for contributors to improve and maintain the repo, If you are interested in the challenge please DM me.

This is the way

Top comments (0)