Input
Input component that simplifies accessibility and validation.
Features:
- Enforces including labels.
- Built in validation using HTML5 form validation API.
- Automatic aria-invalid attribute.
- Automatic aria-describedby attribute.
- Supports all input types including
radio
,checkbox
,select
, andtextarea
.
Styled Example
<template> <form class="styled" @submit.prevent> <VInput label="Name" name="name" required minlength="2" :errors="{ required: 'This field is required.', minlength: (min) => `Must be greater than ${min} characters.` }" > </VInput> <VInput label="Email" name="email" type="email" required > <template #description="input"> <template v-if="input.error"> <span v-if="input.invalid.required" class="error"> This field is required. </span> <span v-if="input.invalid.type" class="error"> Must be an email </span> </template> </template> </VInput> <button type="submit"> Submit </button> </form> </template> <style> .vts-input { margin-bottom: 1rem; } .vts-input--required .vts-input__text:after { content: "*"; speak: none; color: red; } .vts-input__input { display: block; width: 100%; border: 1px solid #CCC; border-radius: 3px; padding: 5px; } .vts-input--error .vts-input__input { border-color: red; } .vts-input__error, .vts-input__description .error { color: red; } </style>
Input Types
Supports all HTML input types except the file
type. Additionally accepts textarea
and select
.
<template> <VInput label="text:" name="text" /> </template>
<template> <VInput label="email:" name="email" type="email" /> </template>
<template> <VInput label="textarea:" name="textarea" type="textarea" /> </template>
<template> <VInput label="checkbox" name="checkbox" type="checkbox" /> </template>
<template> <VInput label="checkbox group" name="checkbox-group" type="checkbox" :options="['option 1', 'option 2', 'option 3', 'option 4']" /> </template>
<template> <VInput type="radio" label="radio" name="radio" :options="['option 1', 'option 2', 'option 3', 'option 4']" /> </template>
<template> <VInput label="select" name="select" type="select" :options="['option 1', 'option 2', 'option 3', 'option 4']" /> </template>
<template> <form class="styled" @submit.prevent> <VInput v-model="selected" label="select (multiple)" name="select-multi" type="select" :options="['option 1', 'option 2', 'option 3', 'option 4']" multiple /> <pre>{{ selected }}</pre> <button type="submit"> Submit </button> </form> </template> <script> export default { data: () => ({ selected: ['option 2'], }), }; </script>
Validation
This component supports HTML5 input validation.
Note that client-side validation is never a substitute for server-side validation.
The simplest validation method is to pass an object to the errors
prop. The object keys should match valitaion attributes, and the values should either be strings or functions returning strings. In the case of functions, there is a single parameter with the value of the respective attribute.
<template> <VInput label="Pick a number between 1 and 10" name="one-to-ten" type="number" required min="1" max="10" :errors="{ required: 'This field is required.', min: (min) => `Must be greater than ${min}.`, max: (max) => `Must be less than ${max}.`, }" > </VInput> </template>
For more advanced needs, the input's invalid
status is provided to the description
slot.
<template> <VInput label="Username" name="username" required minlength="6" class="mb-3" > <template #description="input"> <pre>{{ input }}</pre> </template> </VInput> </template>
{ "valid": false, "dirty": false, "error": false, "invalid": { "type": false, "required": true, "minlength": false, "maxlength": false, "min": false, "max": false, "pattern": false }, "anyInvalid": true, "errors": [] }
Custom Options
Some input support multiple options: radio
, checkbox
, select
. In the case of radio
and checkbox
, input will be placed withing a fieldset
element and each option will be represented by an input, all with the same name.
You can provide a list of options as an array of strings or objects to explicitly specify the label, value, and any other attributes you want on the element.
<template> <div> <VInput type="radio" :options="options" label="Radio Options" name="options-1" /> <VInput type="checkbox" :options="options" label="Checkbox Options" name="options-2" /> <VInput type="select" :options="options" label="Select Options" name="options-3" /> <select> <template v-for="o in options"> <option v-if="typeof o === 'object'" :value="o.value" selected>{{ o.label }}</option> <option v-else>{{ o }}</option> </template> </select> </div> </template> <script> export default { data: () => ({ options: [ 'first', 2, { label: "Third", value: 3, checked: true, } ] }) } </script>
Hidden Label
Sometimes you may want to hide your label and only show the input. Excluding the label causes an accessibility issue, but you can visually hide the label text with CSS. Note that you will need to add the styles to your project.
<template> <VInput label="Input Label" name="features" :classes="{ label: 'visually-hidden' }" /> </template> <style> .visually-hidden { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(1px 1px 1px 1px); clip: rect(1px, 1px, 1px, 1px); white-space: nowrap; } </style>
Description
If you want to add a description to your input, the best practice is to include an aria-describedby
attribute in combination with an ID on the description element. Fortunately, with this component you can simply use the description slot.
<template> <VInput label="Features:" name="features"> <template #description> Are there any other features you would like to see? </template> </VInput> </template>
Custom Classes
This component can accept a classes
prop to customize the output HTML classes:
:classes="{
root: string,
fieldset: string,
fieldsetItems: string,
fieldsetItem: string,
legend: string,
label: string,
input: string,
description: string,
errors: string,
error: string,
}"