Schema-Driven Pattern
The core idea: define your data schema once, get a working UI automatically.
How It Works
1. Schema Definition
An OpenRegister schema defines entity properties with types, constraints, and display hints:
{
"title": "Contact",
"properties": {
"name": { "type": "string", "required": true },
"email": { "type": "string", "format": "email" },
"status": { "type": "string", "enum": ["active", "inactive", "lead"] },
"company": { "type": "string" },
"created": { "type": "string", "format": "date-time" }
}
}
2. Auto-Generated Columns
columnsFromSchema() reads properties and creates table column definitions:
import { columnsFromSchema } from '@conduction/nextcloud-vue'
const columns = columnsFromSchema(schema)
// Result:
// [
// { key: 'name', label: 'Name', sortable: true },
// { key: 'email', label: 'Email', sortable: true },
// { key: 'status', label: 'Status', sortable: true },
// { key: 'company', label: 'Company', sortable: true },
// { key: 'created', label: 'Created', sortable: true },
// ]
CnDataTable uses these columns, and CnCellRenderer renders each cell based on the property type — booleans get checkmarks, enums get status badges, dates get formatted, UUIDs get monospace styling.
3. Auto-Generated Filters
filtersFromSchema() identifies filterable properties (enums, booleans, facetable fields):
import { filtersFromSchema } from '@conduction/nextcloud-vue'
const filters = filtersFromSchema(schema)
// Result: filter definitions for 'status' enum, etc.
CnFacetSidebar renders these as interactive filter controls with live counts from the OpenRegister API.
4. Auto-Generated Form Fields
fieldsFromSchema() creates form field definitions for create/edit dialogs:
import { fieldsFromSchema } from '@conduction/nextcloud-vue'
const fields = fieldsFromSchema(schema)
// Result: field definitions with widget types (text, email, select, date...)
CnFormDialog renders these as a complete form with validation. Select fields support both static enum arrays and async functions for remote search — see CnFormDialog — Async Select.
5. All Together in CnIndexPage
CnIndexPage combines all of this into a single component:
<CnIndexPage
:schema="schema"
:objects="objects"
:pagination="pagination" />
No manual column definitions, no filter setup, no form configuration. The schema drives everything.
Customization
While the defaults work well, every auto-generated aspect can be overridden:
Column Overrides
<CnDataTable
:schema="schema"
:excludeColumns="['created', 'updated']"
:includeColumns="['name', 'email', 'status']"
:columnOverrides="{
name: { label: 'Full Name', width: '200px' },
status: { label: 'Current Status' },
}" />
Field Overrides
<CnFormDialog
:schema="schema"
:excludeFields="['id', 'created']"
:fieldOverrides="{
status: { widget: 'select', options: customStatusOptions },
}" />
Custom Cell Renderers
<CnDataTable :schema="schema">
<template #column-status="{ row, value }">
<CnStatusBadge :label="value" :colorMap="{ active: 'success', inactive: 'error' }" />
</template>
</CnDataTable>