Resource API
NocoBase FlowEngine proporciona dos clases Resource para gestionar las operaciones de datos en el frontend: MultiRecordResource para listas/tablas (múltiples registros) y SingleRecordResource para formularios/detalles (un solo registro). Ambas encapsulan llamadas a la API REST y proporcionan una gestión reactiva de datos.
Cadena de herencia: FlowResource → APIResource → BaseRecordResource → MultiRecordResource / SingleRecordResource
MultiRecordResource
Se utiliza para escenarios de múltiples registros como listas, tablas, kanban, etc. Se importa desde @nocobase/flow-engine.
Operaciones de datos
Paginación
Filas seleccionadas
Ejemplo: uso en CollectionBlockModel
Al heredar de CollectionBlockModel, se debe crear el resource mediante createResource() y luego leer los datos en renderComponent():
import React from 'react';
import { BlockSceneEnum, CollectionBlockModel } from '@nocobase/client-v2';
import { MultiRecordResource } from '@nocobase/flow-engine';
import { tExpr } from '../locale';
export class ManyRecordBlockModel extends CollectionBlockModel {
static scene = BlockSceneEnum.many;
// Declarar el uso de MultiRecordResource para gestionar los datos
createResource() {
return this.context.makeResource(MultiRecordResource);
}
get resource() {
return this.context.resource as MultiRecordResource;
}
renderComponent() {
const data = this.resource.getData(); // TDataItem[]
const count = this.resource.getCount(); // total de registros
return (
<div>
<h3>Total {count} registros (página {this.resource.getPage()})</h3>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
}
ManyRecordBlockModel.define({
label: tExpr('Many records block'),
});
Ejemplo completo en FlowEngine → Extensión de Block.
Ejemplo: invocar CRUD desde una Action
En el handler de registerFlow de un ActionModel, se obtiene el resource del Block actual mediante ctx.blockModel?.resource y se llama a los métodos CRUD:
import { ActionModel, ActionSceneEnum } from '@nocobase/client-v2';
import { MultiRecordResource } from '@nocobase/flow-engine';
import { tExpr } from '../locale';
export class NewTodoActionModel extends ActionModel {
static scene = ActionSceneEnum.collection;
defaultProps = {
type: 'primary',
children: tExpr('New todo'),
};
}
NewTodoActionModel.define({
label: tExpr('New todo'),
});
NewTodoActionModel.registerFlow({
key: 'newTodoFlow',
title: tExpr('New todo'),
on: 'click',
steps: {
openForm: {
async handler(ctx) {
// Obtener el resource del Block actual
const resource = ctx.blockModel?.resource as MultiRecordResource;
if (!resource) return;
ctx.viewer.dialog({
title: ctx.t('New todo'),
content: (view) => (
<MyForm
onSubmit={async (values) => {
// Crear registro; el resource se refrescará automáticamente
await resource.create(values);
ctx.message.success(ctx.t('Created successfully'));
view.close();
}}
onCancel={() => view.close()}
/>
),
});
},
},
},
});
Ejemplo completo en Crear un Plugin de gestión de datos con interacción frontend-backend.
Ejemplo: referencia rápida de operaciones CRUD
async handler(ctx) {
const resource = ctx.blockModel?.resource as MultiRecordResource;
// --- Crear ---
await resource.create({ title: 'New item', completed: false });
// Sin refresh automático
await resource.create({ title: 'Draft' }, { refresh: false });
// --- Leer ---
const items = resource.getData(); // TDataItem[]
const count = resource.getCount(); // total de registros
const item = await resource.get(1); // obtener uno por clave primaria
// --- Actualizar ---
await resource.update(1, { title: 'Updated' });
// --- Eliminar ---
await resource.destroy(1); // eliminación individual
await resource.destroy([1, 2, 3]); // eliminación masiva
// --- Paginación ---
resource.setPage(2);
resource.setPageSize(50);
await resource.refresh();
// O usando métodos de atajo
await resource.goto(3);
await resource.next();
await resource.previous();
// --- Refresh ---
await resource.refresh();
}
SingleRecordResource
Se utiliza para escenarios de un solo registro como formularios y páginas de detalles. Se importa desde @nocobase/flow-engine.
Operaciones de datos
Propiedades clave
import React from 'react';
import { BlockSceneEnum, CollectionBlockModel } from '@nocobase/client-v2';
import { SingleRecordResource } from '@nocobase/flow-engine';
import { tExpr } from '../locale';
export class DetailBlockModel extends CollectionBlockModel {
static scene = BlockSceneEnum.one;
createResource() {
return this.context.makeResource(SingleRecordResource);
}
get resource() {
return this.context.resource as SingleRecordResource;
}
renderComponent() {
const data = this.resource.getData(); // un objeto o null
if (!data) return <div>Cargando...</div>;
return (
<div>
<h3>{data.title}</h3>
<p>{data.content}</p>
</div>
);
}
}
DetailBlockModel.define({
label: tExpr('Detail block'),
});
Ejemplo: crear y editar registros
async handler(ctx) {
const resource = ctx.model.context.resource as SingleRecordResource;
// --- Crear nuevo registro ---
resource.isNewRecord = true;
await resource.save({ name: 'John', age: 30 });
// save invoca internamente la Action create; refresh automático al finalizar
// --- Editar registro existente ---
resource.setFilterByTk(1); // Establece automáticamente isNewRecord = false
await resource.refresh(); // Cargar primero los datos actuales
const data = resource.getData();
await resource.save({ ...data, name: 'Jane' });
// save invoca internamente la Action update
// --- Eliminar el registro actual ---
await resource.destroy(); // Utiliza el filterByTk previamente establecido
}
Métodos comunes
Los siguientes métodos están disponibles tanto en MultiRecordResource como en SingleRecordResource:
Filtrado
Control de campos
Configuración del Resource
Eventos
resource.on('saved', (data) => {
console.log('Registro guardado:', data);
});
Sintaxis de Filter
NocoBase utiliza una sintaxis de filtrado al estilo JSON, con operadores que comienzan con $:
// Igual a
{ status: { $eq: 'active' } }
// No igual a
{ status: { $ne: 'deleted' } }
// Mayor que
{ age: { $gt: 18 } }
// Contiene (coincidencia parcial)
{ name: { $includes: 'test' } }
// Combinación de condiciones
{
$and: [
{ status: { $eq: 'active' } },
{ age: { $gt: 18 } },
]
}
// Condición OR
{
$or: [
{ status: { $eq: 'active' } },
{ role: { $eq: 'admin' } },
]
}
En el Resource se recomienda gestionar las condiciones de filtrado con addFilterGroup:
// Añadir varios grupos de filtros
resource.addFilterGroup('status', { status: { $eq: 'active' } });
resource.addFilterGroup('age', { age: { $gt: 18 } });
// getFilter() agrega automáticamente como: { $and: [...] }
// Eliminar un grupo de filtros
resource.removeFilterGroup('status');
// Refrescar para aplicar el filtro
await resource.refresh();
Comparación entre MultiRecordResource y SingleRecordResource
Enlaces relacionados