useCwaFormInput
useCwaFormInput binds a single field inside a Form component to a reactive value and handles per-field validation, error display, and value synchronisation with the shared form store.
const { value, vars, errors, valid, displayErrors, onBlur, validate, onInput } = useCwaFormInput(iri, fullName)
Parameters
| Parameter | Type | Description |
|---|---|---|
iri | Ref<string | undefined> | IRI of the Form component resource |
fullName | string | Symfony field full_name (e.g. contact_form[name]) |
Return value
| Property | Type | Description |
|---|---|---|
value | Ref<any> | Reactive local value — bind to your <input> with v-model |
vars | ComputedRef | Full field vars from the API (label, attr, errors, valid, etc.) |
errors | ComputedRef<string[]> | Field-level error messages from the API |
valid | ComputedRef<boolean | null> | true = valid, false = invalid, null = not yet evaluated |
displayErrors | ComputedRef<boolean> | Whether errors should be shown — see below |
onBlur | () => void | Call on @blur to enable error display after the user leaves the field |
validate | (extraData?) => Promise<void> | Trigger PATCH validation immediately |
onInput | () => void | Debounced (300 ms) wrapper around validate — call on @input |
displayErrors
Errors should be displayed when any of the following is true:
- The user has blurred the field (
onBlurwas called) - The field was previously valid and has since become invalid (regression)
- The form has been submitted and failed (
isSubmitAttemptedistruefor this IRI)
Bind it to a conditional wrapper around your error list so errors only surface at the right moment.
validate()
For PATCH forms only: sends the current field value to the form's submit endpoint via PATCH so the API can validate it and return per-field errors. The store is updated automatically, and errors / valid react accordingly.
For POST forms, validate() is a no-op — validation only happens on full submission.
Field full_name
Symfony serialises form fields using their full_name, which follows the pattern form_name[field_name] (or form_name[child][field_name] for nested fields). The value comes from vars.value.full_name in the form resource response.
Value lifecycle
- Initialised from
vars.value(the API's current value for this field) - Resets to the API value when
irichanges (e.g. navigating between resources) - Synced into
$cwa.forms.fieldValuessouseCwaForm.submit()can read all fields at once - Cleared from the store on component unmount
Example
<script setup lang="ts">
const props = defineProps<{ iri: string; fullName: string }>()
const iriRef = toRef(props, 'iri')
const { value, vars, errors, displayErrors, onBlur, onInput } = useCwaFormInput(iriRef, props.fullName)
</script>
<template>
<div>
<label :for="props.fullName">{{ vars?.label }}</label>
<input
:id="props.fullName"
v-model="value"
v-bind="vars?.attr"
@blur="onBlur"
@input="onInput"
/>
<ul v-if="displayErrors && errors.length">
<li v-for="error in errors" :key="error" class="text-red-500">
{{ error }}
</li>
</ul>
</div>
</template>
Notes
- Each field component should call
useCwaFormInputwith its ownfullName— the composable registers itself in the shared store and cleans up on unmount onInputis a 300 ms debounce so rapid typing doesn't flood the API with validation requests; callvalidate()directly if you need immediate validation- Extra data can be passed to
validate(extraData)— it is merged into the PATCH body, useful when validation of one field depends on another's current value