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>
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.
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
Create the demo app
For the app creation let's use Vite⚡️
yarn create @vitejs/app my-login-demo --template vue-ts
Installation
To install just run:
yarn add @asigloo/vue-dynamic-forms
# or, using NPM
npm install @asigloo/vue-dynamic-forms
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);
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';
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>
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,
};
},
});
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.
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?. 🤭
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:
- Use a
submit
button to trigger asubmit
event. (Recommended option, it also check if the form is valid). - 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>
For this road, we have two (2) possible events:
-
submitted
in case the validation went well and the form isvalid
(retrieve all values) ☑️ -
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"
/>
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,
};
},
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`;
Then add it to your field definition:
email: EmailField({
label: 'Email',
validations: [
Validator({ validator: required, text: 'This field is required' }),
],
}),
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
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
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
- 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';
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,
],
}),
}
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';
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,
],
}),
}
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.
Top comments (0)