Skip to main content

CnFormDialog

Schema-driven create/edit form dialog. Auto-generates form fields from a schema, supports multiple widget types, and follows the two-phase confirm/result pattern.

Try it

Loading CnFormDialog playground…

Wraps: NcDialog, NcButton, NcTextField, NcSelect, NcCheckboxRadioSwitch

CnFormDialog showing the New client form with name, email, phone, and other fields

CnFormDialog showing the New client form with name, email, phone, and other fields

Props

PropTypeDefaultDescription
schemaObjectnullSchema for auto-generating fields
itemObjectnullFor edit mode (null = create)
dialogTitleString''Defaults to "Create/Edit {schema.title}"
fieldsArraynullManual field definitions (overrides schema)
excludeFieldsArray[]Fields to hide
includeFieldsArraynullFields to show (whitelist)
fieldOverridesObject{}Per-field overrides (see Field Overrides)
nameFieldString'title'
sizeString'normal'Dialog size
successTextString''
cancelLabelString
closeLabelString
confirmLabelString

Widget Types

WidgetUsed For
textShort strings
emailEmail addresses
urlURLs
numberNumeric input
textareaLong text
selectSingle choice from options (static or async)
multiselectMultiple choices (static or async)
tagsTag input (with optional async suggestions)
checkboxBoolean toggle
dateDate picker
datetimeDate-time picker
jsonJSON editor (CnJsonViewer). formData holds the parsed value; invalid JSON blocks confirm
codeFreeform code editor (CnJsonViewer). formData holds the raw string; syntax highlighting via field.language

Events

EventPayloadDescription
confirmformDataForm confirmed (includes id when editing)
closeDialog closed

Slots

SlotScopeDescription
#formfields, formData, errors, updateFieldFull form override
#field-\{key\}field, value, error, updateFieldPer-field override
#field-\{key\}-optionoption object propertiesCustom dropdown option rendering for a select/multiselect/tags field
#field-\{key\}-selected-optionoption object propertiesCustom selected option display for a select/multiselect/tags field
#before-fieldsContent before fields
#after-fieldsContent after fields

The #field-{key}-option and #field-{key}-selected-option slots receive all properties of the option object as scope. They are forwarded directly to NcSelect's #option and #selected-option slots. When not provided, NcSelect uses its default label-based rendering.

Public Methods

MethodDescription
validate()Client-side validation (returns boolean)
setResult(result)Set operation result (pass success or error key)
setValidationErrors(fieldErrors)Set server validation errors

Field Definition

When using the fields prop (manual field definitions), each field object supports:

PropertyTypeDescription
keyStringRequired. Form data key and slot name suffix
labelStringRequired. Display label
widgetStringRequired. Widget type (see table above)
requiredBooleanMarks field as required
readOnlyBooleanDisables the field
descriptionStringHelper text shown below the field
default*Default value for create mode
enumArray | FunctionOptions for select widget. Static array or async function (see below)
itemsObjectFor multiselect: { enum: [...] } or { enum: asyncFn }
debounceNumberDebounce delay in ms for async enum search (default: 300)
validationObject{ minLength, maxLength, minimum, maximum, pattern }
languageStringFor code: 'json' | 'xml' | 'html' | 'text' | 'auto' (default 'auto')

JSON and code fields

For structured-data editing, use widget: 'json'; for freeform highlighted code, use widget: 'code'. Both render a CnJsonViewer inline.

widget: 'json'

Use when the schema property holds a structured value (object, array, primitive, or null). fieldsFromSchema skips type: 'object' by default — setting an explicit widget opts the property back in, so object-shaped values flow through.

// schema
{
title: 'Consumer',
required: ['name'],
properties: {
name: { type: 'string', title: 'Name', required: true },
authorizationConfiguration: {
type: 'object',
widget: 'json',
title: 'Authorization configuration',
},
},
}

The editor shows pretty-printed JSON. On every keystroke the content is parsed: on success formData.authorizationConfiguration updates to the parsed value; on failure the previous value is preserved, an inline error appears, and the Confirm button is disabled until the JSON is valid. An empty editor resolves to null.

widget: 'code'

Stores the raw string as-is — no parse, no validation.

{
type: 'string',
widget: 'code',
title: 'Template',
language: 'html',
}

language may be 'json', 'xml', 'html', 'text', or 'auto' (default 'auto' — CnJsonViewer sniffs the content).

Async Select

Select, multiselect, and tags fields support async options by setting enum (or items.enum) to an async function instead of a static array:

{
key: 'organisation',
widget: 'select',
label: 'Organisation',
required: true,
description: 'Type to search for organisations',
enum: async (query) => {
const results = await orgStore.search(query, 20, 0)
return results.map(org => ({
label: org.name,
id: org.uuid,
description: org.description,
users: org.users,
}))
},
debounce: 500,
}

Behavior:

  • The function receives the search query string and must return an array of option objects
  • Each option must have a label property (used by NcSelect for default display)
  • Options are loaded on mount (called with '') and on each search input (debounced)
  • Per-field loading state is shown via NcSelect's loading indicator
  • filterable is automatically set to false for async fields (server-side filtering)
  • Async selects store the full option object in formData (not just an ID)

Static enums are unchanged — arrays work exactly as before, storing just the ID value.

Field Overrides

The fieldOverrides prop accepts an object keyed by field name. Each override is merged onto the auto-generated field definition, so any field property can be changed.

enumLabels

For select fields backed by an enum, the dropdown displays raw enum values by default. Use enumLabels to provide human-readable labels:

fieldOverrides: {
type: {
enumLabels: { internal: 'Internal', mongodb: 'MongoDB' },
},
}

The enumLabels object maps each enum value to its display label. Values without a mapping fall back to the raw value.

Live demo

<template>
<div>
<button @click="open = true" style="padding: 6px 16px; border-radius: 4px; background: var(--color-primary-element); color: white; border: none; cursor: pointer;">New contact</button>
<CnFormDialog
v-if="open"
ref="dlg"
dialog-title="New contact"
:fields="fields"
@confirm="onConfirm"
@close="open = false" />
</div>
</template>
<script>
export default {
data() {
return {
open: false,
fields: [
{ key: 'name', label: 'Name', widget: 'text', required: true },
{ key: 'email', label: 'Email', widget: 'email' },
{ key: 'notes', label: 'Notes', widget: 'textarea' },
],
}
},
methods: {
async onConfirm(formData) {
await new Promise(r => setTimeout(r, 800))
this.$refs.dlg.setResult({ success: true })
},
},
}
</script>

Usage

Basic (schema-driven)

<CnFormDialog
:schema="schema"
:item="editItem"
:exclude-fields="['id', 'created', 'updated']"
@confirm="onFormConfirm"
@close="editItem = null">
<!-- Custom field for 'notes' -->
<template #field-notes="{ field, value, updateField }">
<RichTextEditor :value="value" @input="updateField('notes', $event)" />
</template>
</CnFormDialog>

Async select with custom option rendering

<CnFormDialog
:fields="fields"
dialog-title="Add User to Organisation"
confirm-label="Add User"
@confirm="onConfirm"
@close="onClose">
<!-- Rich dropdown options for organisation -->
<template #field-organisation-option="{ name, description, users, isDefault }">
<div class="org-option">
<div>
<strong>{{ name }}</strong>
<span v-if="isDefault" class="badge">Default</span>
</div>
<p v-if="description">{{ description }}</p>
<span class="meta">{{ users?.length || 0 }} members</span>
</div>
</template>

<!-- Simpler display for the selected value -->
<template #field-organisation-selected-option="{ name }">
<span>{{ name }}</span>
</template>

<!-- Info note below the form -->
<template #after-fields>
<NcNoteCard type="info">
Select an organisation to add the user as a member.
</NcNoteCard>
</template>
</CnFormDialog>
// In setup / data:
const fields = [
{
key: 'organisation',
widget: 'select',
label: 'Organisation',
required: true,
enum: async (query) => {
const results = await orgStore.search(query, 20, 0)
return results.map(org => ({ label: org.name, id: org.uuid, ...org }))
},
debounce: 500,
},
{
key: 'user',
widget: 'select',
label: 'User',
enum: async (query) => {
const users = await userApi.search(query)
return users.map(u => ({ label: u.displayName, id: u.id }))
},
},
]

Reference (auto-generated)

The tables below are generated from the SFC source via vue-docgen-cli. They reflect what's actually in CnFormDialog.vue and update automatically whenever the component changes.

Props

NameTypeRequiredDefaultDescription
schemaobjectnullSchema for auto-generating fields. Either schema or fields must be provided.
itemobjectnullExisting item for edit mode. Pass null for create mode.
dialogTitlestring''Dialog title. Defaults to "Create {schema.title}" or "Edit {schema.title}".
fieldsarraynullManual field definitions. Overrides schema-generated fields when provided.
excludeFieldsarray[]Field keys to exclude from auto-generated form
includeFieldsarraynullField keys to include (whitelist mode)
fieldOverridesobject\{\}Per-field overrides passed to fieldsFromSchema
nameFieldstring'title'Which field is the "name" (used in result messages)
sizestring'normal'NcDialog size
successTextstring''Success message. Defaults to "Item saved successfully."
cancelLabelstring() =&gt; t('nextcloud-vue', 'Cancel')
closeLabelstring() =&gt; t('nextcloud-vue', 'Close')
confirmLabelstring''Confirm button label. Defaults to "Create" or "Save".

Events

NamePayloadDescription
close
confirmEmitted when the user confirms the form. Payload: form data object. Includes id when editing.

Slots

NameBindingsDescription
formfields, form-data, errors, update-field
before-fields
'field-' + field.keyname, field, value, error, update-field
'field-' + field.key + '-option'name
'field-' + field.key + '-selected-option'name
after-fields

Methods

NameDescription
validateRun client-side validation on all form fields. Checks required, minLength, maxLength, pattern, minimum, maximum.
setResultSet the result of the save operation. Call this from the parent after the API call completes.
setValidationErrorsSet per-field validation errors from the server. Call this from the parent when the API returns validation errors.