Resource API
NocoBase FlowEngine stellt zwei Resource-Klassen für die Datenoperationen im Frontend bereit – MultiRecordResource für Listen/Tabellen (mehrere Datensätze) und SingleRecordResource für Formulare/Detailansichten (ein einzelner Datensatz). Sie kapseln REST-API-Aufrufe und bieten eine reaktive Datenverwaltung.
Vererbungskette: FlowResource → APIResource → BaseRecordResource → MultiRecordResource / SingleRecordResource
MultiRecordResource
Wird für Listen, Tabellen, Kanban-Ansichten und andere Szenarien mit mehreren Datensätzen verwendet. Wird aus @nocobase/flow-engine importiert.
Datenoperationen
Paginierung
Ausgewählte Zeilen
Beispiel: Verwendung in einem CollectionBlockModel
Beim Ableiten von CollectionBlockModel muss die Resource über createResource() erstellt werden, anschließend werden die Daten in renderComponent() gelesen:
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;
// Deklariert die Verwendung von MultiRecordResource für die Datenverwaltung
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(); // Gesamtanzahl der Datensätze
return (
<div>
<h3>Insgesamt {count} Datensätze (Seite {this.resource.getPage()})</h3>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
}
ManyRecordBlockModel.define({
label: tExpr('Many records block'),
});
Vollständiges Beispiel siehe FlowEngine → Block-Erweiterung.
Beispiel: CRUD-Aufruf in einer Aktionsschaltfläche
Im registerFlow-Handler des ActionModel lässt sich die Resource des aktuellen Blocks über ctx.blockModel?.resource abrufen, um CRUD-Methoden aufzurufen:
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) {
// Die Resource des aktuellen Blocks abrufen
const resource = ctx.blockModel?.resource as MultiRecordResource;
if (!resource) return;
ctx.viewer.dialog({
title: ctx.t('New todo'),
content: (view) => (
<MyForm
onSubmit={async (values) => {
// Datensatz erstellen, danach wird die Resource automatisch aktualisiert
await resource.create(values);
ctx.message.success(ctx.t('Created successfully'));
view.close();
}}
onCancel={() => view.close()}
/>
),
});
},
},
},
});
Vollständiges Beispiel siehe Plugin für Datenverwaltung mit Front- und Backend-Integration entwickeln.
Beispiel: CRUD-Operationen Schnellübersicht
async handler(ctx) {
const resource = ctx.blockModel?.resource as MultiRecordResource;
// --- Erstellen ---
await resource.create({ title: 'New item', completed: false });
// Ohne automatisches Refresh
await resource.create({ title: 'Draft' }, { refresh: false });
// --- Lesen ---
const items = resource.getData(); // TDataItem[]
const count = resource.getCount(); // Gesamtanzahl der Datensätze
const item = await resource.get(1); // Einzelnen Datensatz über Primärschlüssel abrufen
// --- Aktualisieren ---
await resource.update(1, { title: 'Updated' });
// --- Löschen ---
await resource.destroy(1); // Einzelner Datensatz
await resource.destroy([1, 2, 3]); // Stapelweises Löschen
// --- Paginierung ---
resource.setPage(2);
resource.setPageSize(50);
await resource.refresh();
// Oder über Kurzbefehle
await resource.goto(3);
await resource.next();
await resource.previous();
// --- Refresh ---
await resource.refresh();
}
SingleRecordResource
Wird für Formulare, Detailseiten und andere Szenarien mit einem einzelnen Datensatz verwendet. Wird aus @nocobase/flow-engine importiert.
Datenoperationen
Wichtige Eigenschaften
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(); // Einzelnes Objekt oder null
if (!data) return <div>Wird geladen...</div>;
return (
<div>
<h3>{data.title}</h3>
<p>{data.content}</p>
</div>
);
}
}
DetailBlockModel.define({
label: tExpr('Detail block'),
});
Beispiel: Datensatz erstellen und bearbeiten
async handler(ctx) {
const resource = ctx.model.context.resource as SingleRecordResource;
// --- Neuen Datensatz erstellen ---
resource.isNewRecord = true;
await resource.save({ name: 'John', age: 30 });
// save ruft intern die create Action auf und aktualisiert anschließend automatisch
// --- Bestehenden Datensatz bearbeiten ---
resource.setFilterByTk(1); // Setzt automatisch isNewRecord = false
await resource.refresh(); // Lädt zuerst die aktuellen Daten
const data = resource.getData();
await resource.save({ ...data, name: 'Jane' });
// save ruft intern die update Action auf
// --- Aktuellen Datensatz löschen ---
await resource.destroy(); // Verwendet den gesetzten filterByTk
}
Allgemeine Methoden
Die folgenden Methoden sind sowohl in MultiRecordResource als auch in SingleRecordResource verfügbar:
Filter
Feldsteuerung
Resource-Konfiguration
Events
resource.on('saved', (data) => {
console.log('Datensatz wurde gespeichert:', data);
});
Filter-Syntax
NocoBase verwendet eine JSON-ähnliche Filter-Syntax. Operatoren beginnen mit $:
// Gleich
{ status: { $eq: 'active' } }
// Ungleich
{ status: { $ne: 'deleted' } }
// Größer als
{ age: { $gt: 18 } }
// Enthält (unscharfes Matching)
{ name: { $includes: 'test' } }
// Verknüpfte Bedingungen
{
$and: [
{ status: { $eq: 'active' } },
{ age: { $gt: 18 } },
]
}
// Oder-Bedingung
{
$or: [
{ status: { $eq: 'active' } },
{ role: { $eq: 'admin' } },
]
}
Auf der Resource wird empfohlen, Filterbedingungen mit addFilterGroup zu verwalten:
// Mehrere Filtergruppen hinzufügen
resource.addFilterGroup('status', { status: { $eq: 'active' } });
resource.addFilterGroup('age', { age: { $gt: 18 } });
// getFilter() aggregiert automatisch zu: { $and: [...] }
// Eine bestimmte Filtergruppe entfernen
resource.removeFilterGroup('status');
// Refresh, um den Filter anzuwenden
await resource.refresh();
MultiRecordResource und SingleRecordResource im Vergleich
Verwandte Links