CWA 2.0 is in alpha: try it, build with it, give feedback. APIs can still change between releases; see Releases.
Form

useCwaFormCollection

Manage a Symfony CollectionType field — dynamically add and remove entries at runtime.

useCwaFormCollection manages a Symfony CollectionType field. It clones the prototype from the API response each time addEntry() is called, assigns a monotonic index, and tracks the list of active entry full_name strings. Child components then call useCwaFormInput for each sub-field; their lifecycle hooks handle registration/unregistration automatically.

const { entries, addEntry, removeEntry, vars } = useCwaFormCollection(iri, 'form_name[fieldName]')

Signature

useCwaFormCollection(iri: Ref<string | undefined>, collectionFullName: string)

Parameters

ParameterTypeDescription
iriRef<string | undefined>IRI of the Form component resource
collectionFullNamestringfull_name of the CollectionType field (e.g. registration[tags])

Return Values

PropertyTypeDescription
entriesComputedRef<string[]>Ordered list of active entry full_name strings. Starts with the entries already in the form view, so an edit form lists its existing items
addEntry() => voidClone the prototype and append a new entry
removeEntry(fullName: string) => voidRemove an entry by its full_name
varsComputedRefCollection field vars from the API (label, allow_add, allow_delete, errors, etc.)

How It Works

  • addEntry() deep-clones the collection field's prototype. The prototype is a sibling of vars on the form view ($cwa.forms.getForm(iri)[collectionFullName].prototype), not one of its vars.
  • It replaces every __name__ placeholder with a monotonic counter, recursively through nested children.
  • Symfony's __name__label__ sentinel label is dropped.
  • It then pushes the resolved full_name onto entries.
  • Entries already in the form view are read when it loads and can be removed like added ones. New entries take the next free index after them
  • Indices are never reused after removal, so Vue v-for keys remain stable
  • The prototype is never mutated
  • Child components must call useCwaFormInput(iriRef, entryFullName) (or useCwaFormInput(iriRef, \${entryFullName}subField`)for compound types) — each child registers its field value with the form store during setup (immediately, not on mount) and clears it inonBeforeUnmount`

Example

Simple Collection (TextType Entries)

Working with Forms → CollectionType has a full parent and entry component with Nuxt UI, including a validation icon on each entry.

Compound Collection (Sub-Fields per Entry)

For a CollectionType where each entry is a compound type (e.g. a ChildType with name and age sub-fields), the child component calls useCwaFormInput for each sub-field:

<!-- ChildEntry.vue -->
<script setup lang="ts">
const props = defineProps<{ iri: string | undefined, entryFullName: string }>()
defineEmits<{ remove: [] }>()
const iriRef = toRef(props, 'iri')
const name = useCwaFormInput(iriRef, `${props.entryFullName}[name]`)
const age  = useCwaFormInput(iriRef, `${props.entryFullName}[age]`)
</script>

Notes

  • Use vars.value?.allow_add / vars.value?.allow_delete (from the API response) to conditionally show add/remove controls
  • Collection-level errors (e.g. min/max count violations) are in vars.value?.errors — render them separately from individual entry errors
  • Entries removed via removeEntry() are immediately dropped from entries; the child component unmounts and useCwaFormInput unregisters the field values, so they are excluded from the next submit() payload automatically
  • See the Forms guide for full field-type examples