The CWA is in heavy development
The CWA is still in alpha and not ready for production - some code and implementations are likely to change. If you would like to try out the CWA, please enjoy what we have provided and feel free to provide feedback, or get involved on GitHub.
Component

useCwaFormRepeated

Bind a Symfony RepeatedType field (e.g. new password + confirmation) with cross-field validation.

useCwaFormRepeated wraps two useCwaFormInput instances for a Symfony RepeatedType field. Each side validates with the sibling's current value so the API can check that both entries match.

const { first, second } = useCwaFormRepeated(iri, 'form_name[fieldName]')

Parameters

ParameterTypeDescription
iriRef<string | undefined>IRI of the Form component resource
fullNamestringfull_name of the RepeatedType field (e.g. registration[plainPassword])

Return value

Returns { first, second } — each has the same shape as a useCwaFormInput return value:

PropertyTypeDescription
valueRef<string>Field value; bind with v-model="field.value.value"
varsComputedRefField vars from the API (label, required, etc.)
errorsComputedRef<string[]>Field-level errors (see pair-mismatch note below)
validComputedRef<null | boolean>null until both sub-fields have a value; then reads the parent RepeatedType node's validity
displayErrorsComputedRef<boolean>Gated on both fields having been blurred — see below
onBlur() => voidCall on the input's blur event
onInput() => voidDebounced; triggers cross-field validation
validate(extraData?) => Promise<void>Explicit validate — PATCHes to the form's /submit endpoint

Error display gate

displayErrors is false until both first and second have been blurred at least once — or the form has been submitted and failed, or the pair was previously valid and has since become invalid. This stops a mismatch error appearing on the second field the moment the user leaves the first, without hiding errors after a failed submit. It is also suppressed while a validation request is in flight.

Cross-field validation

onInput and onBlur are overridden to pass the sibling's current value as extraData when it is non-empty. This lets the API validate that both entries match without requiring a full form submit. If the sibling field is still empty, no extraData is sent.

Pair-mismatch errors

Symfony attaches pair-mismatch errors to [first]. When the mismatch is detected after the user typed in the second field, the composable redirects those errors to second.errors so the message appears next to the field the user most recently edited.

Example

<script setup lang="ts">
const props = defineProps<{ iri: string }>()
const iriRef = toRef(props, 'iri')
const password = useCwaFormRepeated(iriRef, 'registration[plainPassword]')
</script>

<template>
  <UFormField
    :label="password.first.vars.value?.label || 'New Password'"
    :error="password.first.displayErrors.value ? password.first.errors.value[0] : undefined"
  >
    <UInput
      v-model="password.first.value.value"
      type="password"
      autocomplete="new-password"
      @blur="password.first.onBlur"
      @input="password.first.onInput"
    />
  </UFormField>

  <UFormField
    :label="password.second.vars.value?.label || 'Confirm Password'"
    :error="password.second.displayErrors.value ? password.second.errors.value[0] : undefined"
  >
    <UInput
      v-model="password.second.value.value"
      type="password"
      autocomplete="new-password"
      @blur="password.second.onBlur"
      @input="password.second.onInput"
    />
  </UFormField>
</template>

Notes

  • Internally maps to {fullName}[first] and {fullName}[second] — these match Symfony's RepeatedType child names
  • Each side registers its own full_name in the form store via useCwaFormInputuseCwaForm.unregisteredFieldErrors will not include them
  • See the Forms guide for full context