Visibility and derived props

Show entries under a condition, and compute props from the form's values.

Showing an entry under a condition

visibleWhen takes the whole form's values and returns whether the entry shows. It works on every field entry and on every layout entry:

const  = .({
  : .(['private', 'business']),
  : .(),
  : .(),
})

export const  = createForm({
  ,
  : { : 'private', : '', : '' },
  fields: {
    kind: { : 'select', : { : 'Customer', : ['private', 'business'] } },
    company: { : 'text', : { : 'Company' } },
    vatId: { : 'text', : { : 'VAT ID' } },
  },
  : [
    'kind',
    {
      : 'Business',
      : () => . === 'business',
      : ['company', 'vatId'],
    },
  ],
})

A condition on a nested entry still sees the whole form, so a field inside address can depend on a key at the root.

Hidden means not rendered, not removed from the data. The value stays, the schema keeps validating it and it is submitted. The entry's own validators stop running while it is hidden, because its field is not mounted. To leave a hidden value out of the result, let the schema drop it.

The content of a hidden entry is not built at all, so a condition can guard code that only works while the entry shows.

Derived props

props may be a function of the form's values, on a leaf, a section, a row, a form component and an array entry. The widget re-renders with what it returns:

const : <string, readonly string[]> = {
  : ['Berlin', 'Hamburg'],
  : ['Vienna', 'Graz'],
}

export const  = createForm({
  : .({ : .(), : .() }),
  : { : 'de', : '' },
  fields: {
    country: { : 'select', : { : 'Country', : ['de', 'at'] } },
    city: {
      : 'select',
      : ['country'],
      : () => ({
        : `City in ${.}`,
        : [.] ?? [],
      }),
    },
  },
})

The function's result is checked for the types of the widget's props. Unknown keys slip through, though: TypeScript rejects them on an object you write directly, not on one a function returns.

dependsOn

dependsOn lists the paths a condition or a props function reads, from anywhere in the form: country, address.zip, contacts[0].name. The function then runs again only when one of them changes.

  • Derived props need it. Their result is the values themselves, so without dependsOn the widget re-renders on every change anywhere in the form.
  • A condition rarely does. An entry re-renders only when its condition's answer flips, not each time the condition runs, and running it is one function call.

On an entry without a condition or derived props, dependsOn does nothing.