Form
Form input component with input validation rules and validation states and feedback texts for each HLFormItem
Basic Form
Bind your data object to HLForm with :model, then wrap each field in an HLFormItem — its label renders the field label and its path maps to a key on the model. Use size to scale the whole form's fields at once.
<template>
<HLForm :model="basicForm" style="width: 400px" size="lg">
<HLFormItem label="Name" path="name">
<HLInput id="name" v-model:model-value="basicForm.name" placeholder="Input Name" />
</HLFormItem>
<HLFormItem label="Age" path="age">
<HLInputNumber id="age" v-model:value="basicForm.age" placeholder="Input Age" />
</HLFormItem>
<HLFormItem label="Address" path="address">
<HLInput id="address" v-model:model-value="basicForm.address" placeholder="Input Address" type="textarea" />
</HLFormItem>
</HLForm>
</template>
<script setup lang="ts">
import { HLForm, HLFormItem, HLInput, HLInputNumber } from '@platform-ui/highrise'
import { ref } from 'vue'
const basicForm = ref({
name: '',
age: null,
address: '',
})
</script>Form Layout
Control label and item arrangement with form-level props:
label-placement—top(default) stacks the label above the field,leftputs it beside the field.label-align— aligns label textleftorright(applies whenlabel-placement="left"; defaults to left-aligned).label-width— fixes the label column width so all fields align.inline— lays items out in a row instead of stacked.
<template>
<!-- Labels sit in a fixed 90px column, left-aligned, so every field lines up -->
<HLForm id="shipping-form" :model="shippingForm" label-placement="left" label-align="left" :label-width="90" style="width: 440px">
<HLFormItem label="Full name" path="fullName">
<HLInput id="ship-name" v-model:model-value="shippingForm.fullName" placeholder="Jane Doe" />
</HLFormItem>
<HLFormItem label="Street" path="street">
<HLInput id="ship-street" v-model:model-value="shippingForm.street" placeholder="123 Market St" />
</HLFormItem>
<HLFormItem label="City" path="city">
<HLInput id="ship-city" v-model:model-value="shippingForm.city" placeholder="San Francisco" />
</HLFormItem>
<HLFormItem label="ZIP code" path="zip">
<HLInput id="ship-zip" v-model:model-value="shippingForm.zip" placeholder="94103" />
</HLFormItem>
</HLForm>
</template>
<script setup lang="ts">
import { HLForm, HLFormItem, HLInput } from '@platform-ui/highrise'
import { ref } from 'vue'
const shippingForm = ref({ fullName: '', street: '', city: '', zip: '' })
</script>Labels, Feedback & Custom Slots
Use the #label and #feedback slots to render custom markup instead of the plain label / feedback strings. Per item, toggle show-label / show-feedback / show-require-mark, and set require-mark-placement to move the required mark to the left or right.
<template>
<HLForm id="account-form" :model="accountForm" style="width: 420px">
<HLFormItem path="password" required require-mark-placement="left">
<!-- #label: pair the label with an inline requirement hint -->
<template #label>
<span style="display: flex; align-items: center; gap: 6px;">
<span style="font-weight: 600;">Password</span>
<span style="font-size: 12px; color: var(--gray-500);">(min 8 characters)</span>
</span>
</template>
<HLInput id="account-password" v-model:model-value="accountForm.password" type="password" placeholder="Create a password" />
<!-- #feedback: styled helper guidance instead of a plain string -->
<template #feedback>
<span style="color: var(--gray-500); font-size: 12px;">
Use a mix of letters, numbers, and symbols for a stronger password.
</span>
</template>
</HLFormItem>
</HLForm>
</template>
<script setup lang="ts">
import { HLForm, HLFormItem, HLInput } from '@platform-ui/highrise'
import { ref } from 'vue'
const accountForm = ref({ password: '' })
</script>Per-Item Rules
When only one field needs validation, skip the form-level rules map and attach the rule directly with the HLFormItem rule prop. Here a work email must match an email pattern, validated as the user types:
<template>
<HLForm id="signup-form" :model="signupForm" style="width: 420px">
<HLFormItem
label="Work email"
path="workEmail"
:rule="{ required: true, type: 'email', message: 'Enter a valid work email', trigger: ['input', 'blur'] }"
>
<HLInput id="signup-email" v-model:model-value="signupForm.workEmail" placeholder="[email protected]" />
</HLFormItem>
</HLForm>
</template>
<script setup lang="ts">
import { HLForm, HLFormItem, HLInput } from '@platform-ui/highrise'
import { ref } from 'vue'
const signupForm = ref({ workEmail: '' })
</script>Accessing the Form Instance
Call getForm() on a form ref to reach the underlying form instance (e.g. for advanced validation control). validate() and restoreValidation() are also exposed directly on the ref.
<template>
<HLForm id="inspect-form" ref="getFormRef" :model="model">
<HLFormItem label="Name" path="name">
<HLInput id="inspect-name" v-model:model-value="model.name" />
</HLFormItem>
<HLButton @click="inspectForm">Log form instance</HLButton>
</HLForm>
</template>
<script setup lang="ts">
import { HLForm, HLFormItem, HLInput, HLButton } from '@platform-ui/highrise'
import { ref } from 'vue'
const getFormRef = ref()
const model = ref({ name: '' })
const inspectForm = () => {
const instance = getFormRef.value?.getForm() // underlying form instance
console.log(instance)
}
</script>Form validation
The HLForm component provides flexible validation capabilities through its rules prop. While you can implement validation using various approaches, we strongly recommend using Joi as the validation library.
Here's how to implement form validation using our recommended approach with Joi:
<HLForm ref="formRefValidation" :model="formValueValidation" :rules="rulesValidation" style="width: 400px">
<HLFormItem label="Name" path="name" required :validation-status="validationStatus.name" :feedback="validationFeedback.name">
<HLInput id="name" v-model:model-value="formValueValidation.name" placeholder="Input Name" />
</HLFormItem>
<HLFormItem label="Age" path="age" required :validation-status="validationStatus.age" :feedback="validationFeedback.age">
<HLInputNumber id="age" v-model:value="formValueValidation.age" placeholder="Input Age" />
</HLFormItem>
<HLFormItem label="Address" path="address" required :validation-status="validationStatus.address" :feedback="validationFeedback.address">
<HLInput id="address" v-model:model-value="formValueValidation.address" placeholder="Input Address" type="textarea" />
</HLFormItem>
<HLFormItem>
<HLSpace>
<HLButton id="validate" @click="handleValidateClick">Validate</HLButton>
<HLButton id="restore" @click="handleRestore" type="default">Reset</HLButton>
</HLSpace>
</HLFormItem>
</HLForm>import { HLForm, HLFormItem, HLInput, HLInputNumber, HLSpace, HLButton } from '@platform-ui/highrise'
import { ref } from 'vue'
import Joi from 'joi'
// Form state
const formRefValidation = ref()
const formValueValidation = ref({
name: '',
age: null,
address: '',
})
// Validation state
const validationStatus = ref({
name: undefined as 'error' | 'warning' | undefined,
age: undefined as 'error' | 'warning' | undefined,
address: undefined as 'error' | 'warning' | undefined,
})
const validationFeedback = ref({
name: '',
age: '',
address: '',
})
// Joi validation schema
const validationSchema = Joi.object({
name: Joi.string().required().min(2).max(50).messages({
'string.empty': 'Name is required',
'string.min': 'Name should be at least 2 characters',
'string.max': 'Name should be at most 50 characters',
'any.required': 'Name is required',
}),
age: Joi.number().required().min(18).max(100).messages({
'number.base': 'Age is required',
'number.min': 'Age should be at least 18',
'number.max': 'Age should be at most 100',
'any.required': 'Age is required',
}),
address: Joi.string().required().min(10).max(200).messages({
'string.empty': 'Address is required',
'string.min': 'Address should be at least 10 characters',
'string.max': 'Address should be at most 200 characters',
'any.required': 'Address is required',
}),
})
// Validator function
const createValidator = (field: keyof typeof formValueValidation.value) => {
return async (rule: any, value: any) => {
const resultSchema = validationSchema.extract(field)
const result = resultSchema.validate(value)
if (result.error) {
const error = result.error.details[0]
// Check if the error is for empty/required field
if (error.type === 'string.empty' || error.type === 'any.required' || error.type === 'number.base') {
validationStatus.value[field] = 'error'
validationFeedback.value[field] = error.message
throw new Error(error.message)
} else {
// For other validation cases (min/max length, age range), return warning
validationStatus.value[field] = 'warning'
validationFeedback.value[field] = error.message
return {
status: 'warning',
message: error.message,
}
}
}
// Clear validation status and feedback if valid
validationStatus.value[field] = undefined
validationFeedback.value[field] = ''
}
}
// Form validation rules
const rulesValidation = {
name: [{ validator: createValidator('name'), trigger: ['input', 'blur'] }],
age: [{ validator: createValidator('age'), trigger: ['input', 'blur'] }],
address: [{ validator: createValidator('address'), trigger: ['input', 'blur'] }],
}
// Validation handler
async function handleValidateClick(e: MouseEvent) {
e.preventDefault()
try {
await formRefValidation.value?.validate((errors: any) => {
if (!errors) {
console.log('Valid form:', formValueValidation.value)
// Clear all validation statuses and feedback on success
Object.keys(validationStatus.value).forEach(key => {
const field = key as keyof typeof validationStatus.value
validationStatus.value[field] = undefined
validationFeedback.value[field] = ''
})
} else {
console.log('Validation results:', errors)
}
})
} catch (error) {
console.log('Validation failed:', error)
}
}
const handleRestore = () => {
formRefValidation.value?.restoreValidation()
// Reset form values
Object.keys(formValueValidation.value).forEach(key => {
formValueValidation.value[key as keyof typeof formValueValidation.value] = key === 'age' ? null : ''
})
// Clear validation states and feedback
Object.keys(validationStatus.value).forEach(key => {
const field = key as keyof typeof validationStatus.value
validationStatus.value[field] = undefined
validationFeedback.value[field] = ''
})
}All Inputs
A complete form: every highrise input inside an HLFormItem, composed in a single HLForm bound to one model object — each item's path maps to a key on that model. Each field is validated with a Joi rule (via the form's rules map), so Submit runs formRef.validate() first — invalid fields show inline feedback and block submission; once everything passes, it simulates an async request and shows the collected payload. Reset clears the values and validation state. The form is laid out as a two-column CSS grid, with the footer spanning both columns via grid-column: 1 / -1.
<template>
<div class="all-inputs-container">
<HLForm id="all-inputs-form" ref="formRef" :model="formValue" :rules="allInputsRules" class="all-inputs-grid">
<!-- Single-line text inputs -->
<HLFormItem label="Name" path="text" feedback="This is a hint text to help user.">
<HLInput id="all-inputs-text" v-model:model-value="formValue.text" placeholder="Input Text" />
</HLFormItem>
<HLFormItem label="Input Tags" path="tags">
<HLInputTag id="all-inputs-tags" v-model:value="formValue.tags" placeholder="Add tags" />
</HLFormItem>
<HLFormItem label="Input Phone" path="phone">
<HLInputPhone id="all-inputs-phone" v-model:value="formValue.phone" placeholder="Input phone" />
</HLFormItem>
<HLFormItem label="Input Number" path="number">
<HLInputNumber id="all-inputs-number" v-model:value="formValue.number" placeholder="Input number" />
</HLFormItem>
<HLFormItem label="Select" path="selectValue">
<HLSelect id="all-inputs-select" v-model:value="formValue.selectValue" placeholder="Select an option" :options="simpleSelectOptions" />
</HLFormItem>
<HLFormItem label="Date Picker" path="datePicker">
<HLDatePicker id="all-inputs-date-picker" v-model:value="formValue.datePicker" placeholder="Select Date" />
</HLFormItem>
<HLFormItem label="Time Picker" path="time">
<HLTimePicker id="all-inputs-time-picker" v-model:value="formValue.time" placeholder="Select Time" />
</HLFormItem>
<HLFormItem label="OTP" path="otp">
<HLInputOtp id="all-inputs-otp" v-model:value="formValue.otp" placeholder="0" @on-complete="completeOTPHandler" />
</HLFormItem>
<HLFormItem label="Slider" path="sliderValue">
<HLInputSlider id="all-inputs-slider" type="single" v-model:value="formValue.sliderValue" size="xs" />
</HLFormItem>
<HLFormItem label="Color Picker" path="color">
<HLColorPicker id="all-inputs-color-picker" v-model:value="formValue.color" type="picker" placeholder="Select Color" size="xs" />
</HLFormItem>
<HLFormItem label="Toggle Group" path="toggleGroup">
<HLToggleGroup id="all-inputs-toggle-group" group-label="">
<HLSpace>
<HLToggle id="all-inputs-toggle-1" v-model:value="formValue.toggleGroup[0]" :checked-value="true" :unchecked-value="false" label="Random 1" />
<HLToggle id="all-inputs-toggle-2" v-model:value="formValue.toggleGroup[1]" :checked-value="true" :unchecked-value="false" label="Random 2" />
</HLSpace>
</HLToggleGroup>
</HLFormItem>
<HLFormItem label="Radio Group" path="radioGroup">
<HLRadioGroup id="all-inputs-radio-group" v-model:value="formValue.radioGroup">
<HLSpace>
<HLRadio id="all-inputs-radio-1" value="radio-1">Steve Smith</HLRadio>
<HLRadio id="all-inputs-radio-2" value="radio-2">Virat Kohli</HLRadio>
</HLSpace>
</HLRadioGroup>
</HLFormItem>
<HLFormItem label="Checkbox Group" path="checkboxGroup">
<HLCheckboxGroup id="all-inputs-checkbox-group" v-model:value="formValue.checkboxGroup">
<HLSpace>
<HLCheckbox id="all-inputs-checkbox-1" value="facebook">Facebook</HLCheckbox>
<HLCheckbox id="all-inputs-checkbox-2" value="twitter">Twitter</HLCheckbox>
<HLCheckbox id="all-inputs-checkbox-3" value="instagram">Instagram</HLCheckbox>
</HLSpace>
</HLCheckboxGroup>
</HLFormItem>
<HLFormItem label="Text Area" path="textArea">
<HLInput id="all-inputs-textarea" v-model:model-value="formValue.textArea" placeholder="Input Text" type="textarea" />
</HLFormItem>
<!-- The two card groups line up (equal height) -->
<HLFormItem label="Radio Card Group" path="radioCardGroup">
<HLRadioGroup id="all-inputs-radio-card-group" v-model:value="formValue.radioCardGroup">
<HLSpace vertical>
<HLRadioCard id="all-inputs-radio-card-1" value="radiocard-1" title="Hugo Behean" description="Perfect support dreamer" />
<HLRadioCard id="all-inputs-radio-card-2" value="radiocard-2" title="Koss Vyane" description="School teacher" />
</HLSpace>
</HLRadioGroup>
</HLFormItem>
<HLFormItem label="Checkbox Card Group" path="checkboxCardGroup">
<HLCheckboxGroup id="all-inputs-checkbox-card-group" v-model:value="formValue.checkboxCardGroup">
<HLSpace vertical>
<HLCheckboxCard id="all-inputs-checkbox-card-1" value="checkboxcard-1" title="Hugo Behean" description="Perfect support dreamer" />
<HLCheckboxCard id="all-inputs-checkbox-card-2" value="checkboxcard-2" title="Koss Vyane" description="School teacher" />
</HLSpace>
</HLCheckboxGroup>
</HLFormItem>
<!-- Upload + actions span the full width -->
<HLFormItem label="Upload" path="upload" style="grid-column: 1 / -1">
<HLUpload id="all-inputs-upload" v-model:file-list="formValue.upload" placeholder="Upload" />
</HLFormItem>
<HLFormItem style="grid-column: 1 / -1">
<div style="display: flex; justify-content: flex-end; gap: 8px; width: 100%;">
<HLButton id="all-inputs-reset" size="md" @click="handleReset">Reset</HLButton>
<HLButton id="all-inputs-submit" size="md" variant="primary" color="blue" :loading="submitting" @click="handleSubmit">Submit</HLButton>
</div>
</HLFormItem>
</HLForm>
<!-- Result of the simulated submission -->
<pre v-if="submittedData">{{ JSON.stringify(submittedData, null, 2) }}</pre>
</div>
</template>
<script setup lang="ts">
import {
HLForm, HLFormItem, HLInput, HLInputTag, HLInputPhone, HLInputNumber, HLSelect,
HLInputOtp, HLInputSlider, HLDatePicker, HLTimePicker, HLToggleGroup, HLToggle,
HLRadioGroup, HLRadio, HLRadioCard, HLColorPicker, HLCheckboxGroup, HLCheckbox,
HLCheckboxCard, HLUpload, HLSpace, HLButton,
} from '@platform-ui/highrise'
import { ref, watch, nextTick } from 'vue'
import Joi from 'joi'
const formRef = ref()
const formValue = ref({
text: null, tags: [], phone: '9989898987', number: null, textArea: null, otp: null,
selectValue: null, radioGroup: null, radioCardGroup: null, checkboxGroup: ['facebook'],
checkboxCardGroup: null, toggleGroup: [true, true], sliderValue: 7, datePicker: null,
upload: [], time: null, color: 'red',
})
const simpleSelectOptions = [
{ label: 'Option 1', value: 'option1' },
{ label: 'Option 2', value: 'option2' },
{ label: 'Option 3', value: 'option3' },
]
const completeOTPHandler = (value: { otp: string; state: string }) => {
formValue.value.otp = +value.otp
}
// A Joi schema covering every field
const allInputsSchema = Joi.object({
text: Joi.string().required().messages({ 'string.empty': 'Name is required', 'any.required': 'Name is required' }),
tags: Joi.array().min(1).messages({ 'array.min': 'Add at least one tag' }),
phone: Joi.string().required().messages({ 'string.empty': 'Phone is required', 'any.required': 'Phone is required' }),
number: Joi.number().required().messages({ 'number.base': 'Enter a number', 'any.required': 'Number is required' }),
textArea: Joi.string().max(200).allow('', null).messages({ 'string.max': 'Keep it under 200 characters' }),
selectValue: Joi.string().empty(null).required().messages({ 'any.required': 'Select an option', 'string.empty': 'Select an option' }),
otp: Joi.number().required().messages({ 'number.base': 'Enter the OTP', 'any.required': 'OTP is required' }),
sliderValue: Joi.number().min(1).messages({ 'number.min': 'Pick a value above 0' }),
datePicker: Joi.number().required().messages({ 'number.base': 'Pick a date', 'any.required': 'Date is required' }),
time: Joi.number().required().messages({ 'number.base': 'Pick a time', 'any.required': 'Time is required' }),
toggleGroup: Joi.array(),
radioGroup: Joi.string().empty(null).required().messages({ 'any.required': 'Choose one', 'string.empty': 'Choose one' }),
radioCardGroup: Joi.string().empty(null).required().messages({ 'any.required': 'Choose a card', 'string.empty': 'Choose a card' }),
color: Joi.string().empty(null).required().messages({ 'any.required': 'Pick a color', 'string.empty': 'Pick a color' }),
checkboxGroup: Joi.array().min(1).messages({ 'array.min': 'Select at least one' }),
checkboxCardGroup: Joi.array().min(1).messages({ 'array.min': 'Select at least one card' }),
upload: Joi.array().min(1).messages({ 'array.min': 'Upload at least one file' }),
})
// One validator per field, keyed by the item's `path`.
// Naive expects a sync validator to RETURN an Error to fail (not throw).
const makeValidator = (field) => (rule, value) => {
const { error } = allInputsSchema.extract(field).validate(value)
return error ? new Error(error.details[0].message) : true
}
const allInputsRules = Object.fromEntries(
Object.keys(formValue.value).map(field => [
field,
[{ validator: makeValidator(field), trigger: ['blur', 'change', 'input'] }],
])
)
const submitting = ref(false)
const submittedData = ref(null)
const hasSubmitted = ref(false)
const handleSubmit = () => {
submittedData.value = null
hasSubmitted.value = true
formRef.value?.validate((errors) => {
if (errors) return // invalid — inline feedback shown per field
submitting.value = true
setTimeout(() => {
submitting.value = false
submittedData.value = JSON.parse(JSON.stringify(formValue.value))
}, 1200)
})
}
// Some inputs (time picker, tag input, OTP) update the model without firing a
// field trigger, so re-validate the whole form on any change once submit was tried.
watch(formValue, () => {
if (!hasSubmitted.value) return
nextTick(() => formRef.value?.validate(() => {}).catch(() => {}))
}, { deep: true })
const handleReset = () => {
formValue.value = {
text: null, tags: [], phone: '', number: null, textArea: null, otp: null,
selectValue: null, radioGroup: null, radioCardGroup: null, checkboxGroup: [],
checkboxCardGroup: null, toggleGroup: [false, false], sliderValue: 0, datePicker: null,
upload: [], time: null, color: 'red',
}
submittedData.value = null
hasSubmitted.value = false
formRef.value?.restoreValidation()
}
</script>
<style scoped>
.all-inputs-container {
max-width: 880px;
padding: 24px;
border: 1px solid var(--gray-200);
border-radius: 12px;
background: var(--gray-50);
}
.all-inputs-grid {
display: grid;
grid-template-columns: 1fr 1fr;
align-items: start;
column-gap: 24px;
row-gap: 4px;
}
</style>Field Tooltip
Pass a tooltip object to HLFormItem to render a help icon beside the label that reveals guidance on hover. It accepts tooltipContent (required) plus optional trigger, placement, icon, and iconClass (see FormItemTooltipProps).
<template>
<HLForm id="tooltip-form" :model="tooltipForm" style="width: 420px">
<HLFormItem
label="API key"
path="apiKey"
:tooltip="{ tooltipContent: 'Find this under Settings → Developers. Keep it secret.', placement: 'top' }"
>
<HLInput id="tooltip-api-key" v-model:model-value="tooltipForm.apiKey" placeholder="sk_live_…" />
</HLFormItem>
</HLForm>
</template>
<script setup lang="ts">
import { HLForm, HLFormItem, HLInput } from '@platform-ui/highrise'
import { ref } from 'vue'
const tooltipForm = ref({ apiKey: '' })
</script>Clamped Feedback with Tooltip
Long feedback text can be clamped to a fixed number of lines with feedback-line-clamp; set show-feedback-tooltip to reveal the full text in a tooltip when it overflows. Tune that tooltip with feedback-tooltip-props.
<template>
<HLForm id="clamp-form" :model="clampForm" style="width: 420px">
<HLFormItem
label="Notes"
path="notes"
:feedback-line-clamp="1"
:show-feedback-tooltip="true"
feedback="This is a long feedback message that is clamped to a single line; hover it to read the rest of the guidance in a tooltip."
>
<HLInput id="clamp-notes" v-model:model-value="clampForm.notes" placeholder="Add a note" />
</HLFormItem>
</HLForm>
</template>
<script setup lang="ts">
import { HLForm, HLFormItem, HLInput } from '@platform-ui/highrise'
import { ref } from 'vue'
const clampForm = ref({ notes: '' })
</script>Disabled Fields
disabled set on HLForm is not inherited by the fields — set disabled on each input instead. Bind them all to a single flag to disable the whole form at once, e.g. for a read-only view or while a submission is in flight.
<template>
<!-- Bind every field's `disabled` to one flag -->
<HLForm id="disabled-form" :model="disabledForm" style="width: 420px">
<HLFormItem label="Name" path="name">
<HLInput id="disabled-name" v-model:model-value="disabledForm.name" :disabled="formDisabled" />
</HLFormItem>
<HLFormItem label="Email" path="email">
<HLInput id="disabled-email" v-model:model-value="disabledForm.email" :disabled="formDisabled" />
</HLFormItem>
</HLForm>
</template>
<script setup lang="ts">
import { HLForm, HLFormItem, HLInput } from '@platform-ui/highrise'
import { ref } from 'vue'
const formDisabled = ref(true)
const disabledForm = ref({ name: 'Read only', email: '[email protected]' })
</script>Accessibility
- Provide
aria-labelledby(oraria-label) at the form level so the entire collection has an accessible name. - Route form-level helper or error messages through
aria-describedby, and push submission errors into arole="alert"so they announce immediately. - Set
aria-busy="true"while async submissions run to indicate the form is processing.
Imports
import { HLForm, HLFormItem, HLInput } from '@platform-ui/highrise'
import { ref } from 'vue'import { HLForm, HLFormItem, HLInput } from '@platform-ui/highrise'
import { ref } from 'vue'
import Joi from 'joi'Props
HLForm Props
| Name | Type | Default | Description |
|---|---|---|---|
| id | string | undefined | undefined | The id of the element (passed through to the rendered form via attribute fall-through; not a declared prop) |
| disabled | boolean | false | Whether to disable the form |
| label-width | number | string | undefined | undefined | Width of all form item labels in the form |
| label-align | 'left' | 'right' | undefined | undefined | Alignment of all form item labels in the form (unset = left-aligned) |
| label-placement | 'left' | 'top' | 'top' | Position of all form item labels in the form |
| model | Object | undefined | undefined | Form data object |
| rules | type FormRules = Record<string, FormItemRule | Array<FormItemRule>> | undefined | undefined | Validation rules for form items |
| show-feedback | boolean | true | Whether to show feedback in form items |
| show-label | boolean | true | Whether to show labels in form items |
| show-require-mark | boolean | true | Whether to show required mark on form items |
| require-mark-placement | 'left' | 'right' | 'right' | Placement of the required mark for all form items |
| inline | boolean | false | Lay form items out in a row instead of stacked |
| validate-messages | FormValidateMessages | undefined | Default validation messages applied to all form items |
| size | 'sm' | 'md' | 'lg' | 'md' | Size of form items |
FormItemRule Interface
interface FormItemRule {
required?: boolean
validator?: (rule: FormItemRule, value: any) => boolean | Error | Promise<void>
trigger?: Array<'input' | 'blur' | 'change'> | 'input' | 'blur' | 'change'
message?: string
type?: 'string' | 'number' | 'array' | 'object' | 'email'
min?: number // For string length, array length or number value
max?: number // For string length, array length or number value
pattern?: RegExp
}FormItemTooltipProps Interface
Shape of the tooltip prop on HLFormItem.
interface FormItemTooltipProps {
tooltipContent: string
trigger?: 'hover' | 'click' | 'focus' | 'manual'
placement?:
| 'top'
| 'top-start'
| 'top-end'
| 'right'
| 'right-start'
| 'right-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'left'
| 'left-start'
| 'left-end'
icon?: Function
iconClass?: string
}HLFormItem Props
| Name | Type | Default | Description |
|---|---|---|---|
| feedback | string | undefined | Feedback/hint text content |
| label | string | undefined | Label of the form item |
| path | string | undefined | Validation path of the form item |
| required | boolean | false | Whether the form item is required |
| rule | FormItemRule | Array<FormItemRule> | undefined | Validation rules for the form item |
| showFeedback | boolean | undefined | Whether to show feedback, overrides form's show-feedback |
| showLabel | boolean | undefined | Whether to show label, overrides form's show-label |
| showRequireMark | boolean | undefined | Whether to show required mark, overrides form's show-require-mark |
| validationStatus | 'success' | 'warning' | 'error' | undefined | Validation status |
| tooltip | Object | null | tooltip Object |
| showFeedbackTooltip | boolean | undefined | Whether to show feedback tooltip |
| feedbackLineClamp | number | 1 | Lines to show without elipsis |
| feedbackTooltipProps | Object | null | Properties of the feedback tooltip |
| requireMarkPlacement | 'left' | 'right' | 'right' | Placement of the required mark |
| labelProps | Object | {} | Props to be attached to the label element of a HLFormItem |
Slots
HLForm Slots
| Name | Parameters | Description |
|---|---|---|
| default | () | The default content slot |
HLFormItem Slots
| Name | Parameters | Description |
|---|---|---|
| default | () | The default content slot |
| label | () | Label content for the form item |
| feedback | () | Feedback / validation message content below the form item |
Methods
| Name | Parameters | Returns | Description |
|---|---|---|---|
getForm | () | FormInst | null | Returns the form instance |
validate | (callback?: (errors?: Array<FormValidationError>) => void) => Promise<void> | Promise<void> | Validates all form items. Returns a promise that resolves when validation is complete |
restoreValidation | () | void | Restores form validation to initial state |
Accessibility
When using the
labelprop:- Use the
labelPropsprop to assign a uniqueidto the label - Add an
aria-labelledbyattribute to the form control component insideHLFormItem - Set
aria-labelledbyto match the label'sidvalue
- Use the
When using the
labelslot:- Ensure your custom label element has a unique
idattribute - Add an
aria-labelledbyattribute to the form control component insideHLFormItem - Set
aria-labelledbyto match the custom label'sidvalue
- Ensure your custom label element has a unique