Skip to content

Components

@vuetils/form comes with two helper components UField and UForm that make it easier to create forms. These components handle the two-way data binding and add validation classes to the inputs and the form element so differnt form states can be styled with CSS.

UField

UField is the basic building block of a form. It is a wrapper around the native label element and can be used in the exact same way. UField needs exactly one input element as a slot and can have optional label content too. The label content can either be just plain text like in the example below or any other Vue components or HTML elements.

If used outside a UForm component UField needs a Field instance as a prop.

Validation Classes

Depending on the validation state of the Field instance these classes are added to the input element:

  • v-valid - added when the field is valid
  • v-invalid - added when the field is invalid
  • v-pristine - added when the field's value has not been changed yet
  • v-dirty - added when the field's value has been changed
  • v-pending - added when the field has async validators that are being resolved
  • v-touched - added when the field has been blurred yet
  • v-untouched - added when the field hasn't been blurred yet

There are also the classes v-label and v-input that are added to the label and input elements respectively. Play around with the example and see how different classes get added or removed.

Example

vue
<template>
	<UField :field="field">
		Label:
		<input name="field" type="text" required />
	</UField>
</template>

<script setup>
import { UField, required, useField } from '@vuetils/form';

const field = useField('', { validators: [required] });
</script>

Markup

This will create the following clean and minimal HTML markup

html
// model value: 
<label class="v-label">
	Label:
	<input
    name="field"
    type="text"
    required
    class="v-invalid v-pristine v-untouched v-input"
  />
</label>

UForm

UForm is a wrapper around the native form element and can be used in the exact same way. UForm needs UField components as child slots. The name attribute of the input element is used to bind the input to the matching Field instance.

Validation Classes

Similar how it works with UField these classes are added depending on the validation state of the Form instance:

  • v-valid - added when all fields are valid
  • v-invalid - added when on ore more fields are invalid
  • v-pristine - added when all fields are pristine
  • v-dirty - added when one or more fields are dirty
  • v-pending - added when one or more fields are pending
  • v-submitted - added when the form has been submitted no matter if it was valid or not
  • v-touched - added when one or more fields have been blurred
  • v-untouched - added when no fields haven been blurred

There is also the class v-form that is added to the form element. Play around with the example and see how differnt classes get added or removed.

Example

vue
<template>
	<UForm :form="form">
		<UField>
			Firstname:
			<input name="firstname" type="text" required />
		</UField>

		<UField>
			Lastname:
			<input name="lastname" type="text" required />
		</UField>

		<button>Submit</button>
	</UForm>
</template>

<script setup>
import { UField, UForm, required, useForm } from '@vuetils/form';

const form = useForm({
	firstname: ['', [required]],
	lastname: ['', [required]],
});
</script>

Markup

This will create the following clean and minimal HTML markup

html
<form class="v-invalid v-pristine v-untouched v-form">
  // model value: 
  <label class="v-label">
    Email:
    <input
      name="firstname"
      type="firstname"
      required
      class="v-invalid v-pristine v-untouched v-input"
    />
  </label>

  // model value: 
  <label class="v-label">
    Password:
    <input
      name="lastname"
      type="lastname"
      required
      class="v-invalid v-pristine v-untouched v-input"
    />
  </label>

  <button>Submit</button>
</form>

Submit event

UForm emits the custom event v-submit every time the form is submitted. The event handler receives the current values of the form. This can also be typed with typeof form.values as shown in the example below.

If you want to you can also just listen to the native submit event.

vue
<template>
	<UForm :form="form" v-submit="onSubmit">
		<!-- Form inputs -->
	</UForm>
</template>

<script setup lang="ts">
import { UForm, useForm } from '@vuetils/form';

const form = useForm({
	firstname: [''],
	lastname: [''],
});

function onSubmit(values: typeof form.values) {
	if (form.invalid) {
		//handle errors
	}

	// do something with the values
}
</script>

Released under the MIT License.