Skip to main content

Composables

Vue composables that encapsulate common view patterns.

useListView

Provides everything a CnIndexPage-based list view needs: reactive collection data, schema, loading and pagination state, sidebar wiring, and event handlers for search, sort, filter, and pagination — all wired to the object store. A complete list view requires ~40 LOC instead of the ~150 LOC of manual boilerplate.

Signature

useListView(objectType, options?)

Before / After

// Before — 150 LOC of boilerplate per component
export default {
data() {
return { searchTerm: '', sortKey: null, schema: null, visibleColumns: null }
},
computed: {
clients() { return this.objectStore.collections.client },
loading() { return this.objectStore.loading.client },
},
mounted() {
this.objectStore.fetchSchema('client').then(s => { this.schema = s })
this.setupSidebar()
this.fetchClients()
},
beforeDestroy() { this.teardownSidebar() },
methods: {
setupSidebar() { /* 25 LOC */ },
teardownSidebar() { /* 10 LOC */ },
fetchClients() { /* 18 LOC */ },
onSearch(v) { /* debounce + fetchClients */ },
// …
},
}

// After — 10 LOC
setup() {
const list = useListView('client', {
sidebarState: inject('sidebarState', null),
})
return { ...list, openClient, createNew }
}

Options

OptionTypeDefaultDescription
sidebarStateObject|nullnullSidebar state from inject('sidebarState'). When provided, wires/unwires the sidebar automatically.
defaultPageSizeNumber20Default _limit sent to the API
debounceMsNumber300Search debounce in milliseconds

Return Value

KeyTypeDescription
schemaRef<Object|null>Schema loaded on mount via objectStore.fetchSchema
objectsComputedRef<Array>objectStore.collections[objectType]
loadingComputedRef<Boolean>objectStore.loading[objectType]
paginationComputedRef<Object>objectStore.pagination[objectType]
searchTermRef<String>Current search value
sortKeyRef<String|null>Active sort column key
sortOrderRef<String>'asc' or 'desc'
activeFiltersRef<Object>Active facet filter values { key: string[] }
visibleColumnsRef<Array|null>Column visibility (synced from sidebar)
pageSizeRef<Number>Current page size
onSearch(value)FunctionDebounced; updates searchTerm and calls refresh(1)
onSort({ key, order })FunctionUpdates sort state and calls refresh(1)
onFilterChange(key, values)FunctionUpdates activeFilters and calls refresh(1)
onPageChange(page)FunctionCalls refresh(page)
onPageSizeChange(size)FunctionUpdates page size and calls refresh(1)
refresh(page?)FunctionBuilds params and calls objectStore.fetchCollection

Lifecycle behaviour

  • onMounted: calls objectStore.fetchSchema, activates sidebar (if provided), and calls refresh(1)
  • onBeforeUnmount: deactivates sidebar and clears all sidebar callbacks

When sidebarState is provided, the composable sets:

sidebarState.active = true
sidebarState.schema = schema.value
sidebarState.onSearch = onSearch
sidebarState.onColumnsChange = (cols) => { visibleColumns.value = cols }
sidebarState.onFilterChange = ({ key, values }) => onFilterChange(key, values)
// facetData is kept in sync via a watcher on objectStore.facets[objectType]

All fields are cleared (set to null / false / {}) in onBeforeUnmount.

useDetailView

Provides reactive object data, save/delete operations, loading state, and optional post-operation router navigation for detail/edit views.

Signature

useDetailView(objectType, id, options?)

id may be a plain string or a Ref<string> — if reactive, the composable re-fetches whenever it changes (route param changes without full remount).

Pass 'new' as id to create a new object.

Example

import { useDetailView } from '@conduction/nextcloud-vue'

const {
object, isNew, loading, saving, editing,
onSave, confirmDelete, showDeleteDialog,
error, validationErrors,
} = useDetailView('client', props.clientId, {
router: useRouter(),
listRouteName: 'ClientList',
detailRouteName: 'ClientDetail',
})

Options

OptionTypeDefaultDescription
routerObject|nullnullVue Router instance. Enables redirect after create and delete.
listRouteNameString|nullnullRoute name to navigate to after successful delete
detailRouteNameString|nullnullRoute name to navigate to after successful create
nameFieldString'title'Field shown in error messages

Return Value

KeyTypeDescription
objectComputedRef<Object>Object from store, or {} when new
isNewComputedRef<Boolean>true when id is 'new' or falsy
loadingComputedRef<Boolean>objectStore.loading[objectType]
savingRef<Boolean>true while save is in flight
editingRef<Boolean>View/edit mode toggle
showDeleteDialogRef<Boolean>Controls delete-confirmation visibility
errorRef<String|null>General error message
validationErrorsRef<Object|null>Field-level errors from a 422 response
onSave(formData)FunctionSave object; redirect on create or re-fetch on update
confirmDelete()FunctionDelete object; navigate to listRouteName on success

Save behaviour

  • New object (isNew === true): calls objectStore.saveObject → on success, navigates to { name: detailRouteName, params: { id: result.id } } if router + detailRouteName are set
  • Existing object (isNew === false): calls objectStore.saveObject → on success, sets editing.value = false and re-fetches the object
  • 422 validation error: sets validationErrors.value to the error map; no redirect
  • Other errors: sets error.value; no redirect

useContextMenu

Manages right-click context menu positioning and state. Handles cursor-based positioning through CSS custom properties and a data attribute on document.documentElement, so any <NcActions> can be opened at the click position.

See useContextMenu for full documentation and usage examples.

Signature

const { isOpen, targetItem, open, close, isActionDisabled, triggerAction } = useContextMenu()

Return Value

KeyTypeDescription
isOpenRef<boolean>Whether the menu is open (bind to NcActions :open.sync)
targetItemRef<any>The right-clicked item (null when closed)
open({ item, event })FunctionOpen menu at cursor; sets CSS vars + data attribute
close()FunctionClose menu and clean up DOM
isActionDisabled(action)FunctionResolve action.disabled (boolean or function)
triggerAction(action)FunctionCall action.handler(targetItem), return { action, row }

useSubResource

Manages sub-resources on an object (e.g., notes, comments).

import { useSubResource } from '@conduction/nextcloud-vue'

const {
items,
loading,
add,
remove,
refresh,
} = useSubResource({
objectStore,
parentType: 'contact',
parentId: props.contactId,
subResourceType: 'notes',
})

Parameters

ParameterTypeDescription
objectStoreStoreObject store instance (with createSubResourcePlugin)
parentTypeStringParent object type
parentIdString/RefParent object ID (reactive)
subResourceTypeStringSub-resource name (matches plugin name)
autoFetchBooleanFetch on mount (default: true)

Return Value

Property/MethodTypeDescription
itemsRef<Array>Sub-resource items
loadingRef<Boolean>Loading state
add(data)FunctionCreate sub-resource
remove(id)FunctionDelete sub-resource
refresh()FunctionRe-fetch items