NuGrid provides powerful inline cell editing with support for various data types, validation, and custom editors.
Double-click a cell to edit. Press Enter to save, Escape to cancel.
<script setup lang="ts">
import type { NuGridColumn } from '#nu-grid/types'
interface Task {
id: number
name: string
status: string
priority: number
}
const data = ref<Task[]>([
{ id: 1, name: 'Design mockups', status: 'completed', priority: 1 },
{ id: 2, name: 'Implement API', status: 'in-progress', priority: 2 },
{ id: 3, name: 'Write tests', status: 'pending', priority: 3 },
{ id: 4, name: 'Deploy to staging', status: 'pending', priority: 4 },
{ id: 5, name: 'Code review', status: 'in-progress', priority: 2 },
])
const columns: NuGridColumn<Task>[] = [
{ accessorKey: 'id', header: 'ID', size: 60, enableEditing: false },
{ accessorKey: 'name', header: 'Task', size: 180, cellDataType: 'text' },
{ accessorKey: 'status', header: 'Status', size: 120, cellDataType: 'text' },
{ accessorKey: 'priority', header: 'Priority', size: 100, cellDataType: 'number' },
]
const toast = useToast()
function onCellValueChanged(event: { row: any; column: any; oldValue: any; newValue: any }) {
toast.add({
title: 'Cell Updated',
description: `${event.column.id}: "${event.oldValue}" → "${event.newValue}"`,
color: 'success',
})
}
</script>
<template>
<div class="w-full">
<p class="mb-3 text-sm text-muted">
Double-click a cell to edit. Press Enter to save, Escape to cancel.
</p>
<NuGrid
:data="data"
:columns="columns"
:editing="{ enabled: true, startClicks: 'double' }"
:ui="{
base: 'w-full border-separate border-spacing-0',
thead: '[&>tr]:bg-elevated/50',
th: 'py-2 border-y border-default first:border-l last:border-r first:rounded-l-lg last:rounded-r-lg',
td: 'border-b border-default',
}"
@cell-value-changed="onCellValueChanged"
/>
</div>
</template>
Enable editing with the editing prop:
<template>
<!-- Simple enable -->
<NuGrid :data="data" :columns="columns" :editing="{ enabled: true }" />
<!-- With options -->
<NuGrid
:data="data"
:columns="columns"
:editing="{
enabled: true,
startClicks: 'double',
startKeys: ['enter', 'f2'],
}"
/>
</template>
Configure editing behavior:
interface NuGridEditingOptions {
enabled: boolean
startClicks: 'none' | 'single' | 'double'
startKeys: ('enter' | 'f2' | 'bs' | 'alpha' | 'numeric')[]
}
| Option | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable editing |
startClicks | 'none' | 'single' | 'double' | 'double' | Click to start editing |
startKeys | array | ['enter', 'f2', 'bs', 'alpha', 'numeric'] | Keys that start editing |
| Key | Description |
|---|---|
enter | Enter key starts editing |
f2 | F2 key starts editing |
bs | Backspace/Delete clears and starts editing |
alpha | Any letter key replaces and starts editing |
numeric | Any number key replaces and starts editing |
Enable or disable editing per column:
const columns: NuGridColumn<Product>[] = [
{
accessorKey: 'id',
header: 'ID',
enableEditing: false, // ID is not editable
},
{
accessorKey: 'name',
header: 'Name',
enableEditing: true, // Explicitly enable
},
{
accessorKey: 'price',
header: 'Price',
// Editing enabled by default when grid editing is on
},
]
Use cell data types for appropriate editors:
const columns: NuGridColumn<Product>[] = [
{
accessorKey: 'name',
header: 'Name',
cellDataType: 'text', // Text input editor
},
{
accessorKey: 'price',
header: 'Price',
cellDataType: 'number', // Number input editor
},
{
accessorKey: 'active',
header: 'Active',
cellDataType: 'boolean', // Checkbox editor
},
{
accessorKey: 'category',
header: 'Category',
cellDataType: 'selection', // Dropdown editor
cellDataTypeOptions: {
options: ['Electronics', 'Clothing', 'Food'],
},
},
]
Listen to cell value changes:
<script setup lang="ts">
function onCellValueChanged(event) {
console.log('Changed:', {
rowId: event.row.id,
column: event.column.id,
oldValue: event.oldValue,
newValue: event.newValue,
})
// Persist to server
await updateRecord(event.row.original.id, {
[event.column.id]: event.newValue,
})
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:editing="{ enabled: true }"
@cell-value-changed="onCellValueChanged"
/>
</template>
NuGrid emits several editing-related events:
<script setup lang="ts">
function onCellEditingStarted(event) {
console.log('Started editing:', event.row.id, event.column.id)
}
function onCellEditingCancelled(event) {
console.log('Cancelled editing:', event.row.id, event.column.id)
}
function onCellValueChanged(event) {
console.log('Value changed:', event.oldValue, '->', event.newValue)
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:editing="{ enabled: true }"
@cell-editing-started="onCellEditingStarted"
@cell-editing-cancelled="onCellEditingCancelled"
@cell-value-changed="onCellValueChanged"
/>
</template>
Create custom editors for special data types:
<script setup lang="ts">
// Register a custom editor component
const columns: NuGridColumn<Product>[] = [
{
accessorKey: 'rating',
header: 'Rating',
cellEditor: defineComponent({
props: ['value', 'onSave', 'onCancel'],
setup(props) {
const localValue = ref(props.value)
return () => h('div', { class: 'flex gap-1' }, [
...Array.from({ length: 5 }, (_, i) =>
h('button', {
class: i < localValue.value ? 'text-yellow-400' : 'text-gray-300',
onClick: () => {
localValue.value = i + 1
props.onSave(localValue.value)
},
}, '★')
),
])
},
}),
},
]
</script>
| Key | Action |
|---|---|
Enter | Save and move down |
Tab | Save and move right |
Shift + Tab | Save and move left |
Escape | Cancel editing |
Arrow keys | Save and navigate (configurable) |
Cancel editing programmatically by returning false from a handler:
<script setup lang="ts">
function onCellEditingStarted(event) {
// Prevent editing locked rows
if (event.row.original.locked) {
return false
}
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:editing="{ enabled: true }"
@cell-editing-started="onCellEditingStarted"
/>
</template>
<script setup lang="ts">
const toast = useToast()
const saving = ref(false)
async function onCellValueChanged(event) {
saving.value = true
try {
await api.updateItem(event.row.original.id, {
[event.column.id]: event.newValue,
})
toast.add({
title: 'Saved',
description: `Updated ${event.column.id}`,
color: 'success',
})
} catch (error) {
toast.add({
title: 'Error',
description: 'Failed to save changes',
color: 'error',
})
// Revert the change
event.row.original[event.column.id] = event.oldValue
} finally {
saving.value = false
}
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:editing="{ enabled: true, startClicks: 'double' }"
:loading="saving"
@cell-value-changed="onCellValueChanged"
/>
</template>