Input

Input component that simplifies accessibility and validation.

Features:

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 typesopen in new window 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>

checkbox group

<template>
  <VInput
    type="radio"
    label="radio"
    name="radio"
    :options="['option 1', 'option 2', 'option 3', 'option 4']"
  />
</template>

radio

<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>

[
  "option 2"
]

Validation

This component supports HTML5 input validationopen in new window.

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>

Radio Options
Checkbox Options

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 CSSopen in new window. 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>

Are there any other features you would like to see?

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,
}"