Resource API
Le FlowEngine NocoBase fournit deux classes Resource pour gérer les opérations sur les données côté frontend : MultiRecordResource pour les listes/tables (plusieurs enregistrements) et SingleRecordResource pour les formulaires/détails (un seul enregistrement). Elles encapsulent les appels REST API et offrent une gestion réactive des données.
Chaîne d'héritage : FlowResource → APIResource → BaseRecordResource → MultiRecordResource / SingleRecordResource
MultiRecordResource
Utilisée pour les scénarios à plusieurs enregistrements comme les listes, les tables, les kanbans, etc. À importer depuis @nocobase/flow-engine.
Opérations sur les données
Lignes sélectionnées
Exemple : utilisation dans CollectionBlockModel
Lorsque vous héritez de CollectionBlockModel, vous devez créer la resource via createResource(), puis lire les données dans 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;
// Déclare l'utilisation de MultiRecordResource pour gérer les données
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(); // Nombre total d'enregistrements
return (
<div>
<h3>{count} enregistrements au total (page {this.resource.getPage()})</h3>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
}
ManyRecordBlockModel.define({
label: tExpr('Many records block'),
});
L'exemple complet est disponible dans FlowEngine → Extension de blocs.
Exemple : appel CRUD dans un bouton d'action
Dans le handler registerFlow d'un ActionModel, récupérez la resource du bloc courant via ctx.blockModel?.resource et appelez les méthodes 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) {
// Récupère la resource du bloc courant
const resource = ctx.blockModel?.resource as MultiRecordResource;
if (!resource) return;
ctx.viewer.dialog({
title: ctx.t('New todo'),
content: (view) => (
<MyForm
onSubmit={async (values) => {
// Crée un enregistrement, la resource sera refresh automatiquement après la création
await resource.create(values);
ctx.message.success(ctx.t('Created successfully'));
view.close();
}}
onCancel={() => view.close()}
/>
),
});
},
},
},
});
L'exemple complet est disponible dans Réaliser un plugin de gestion de données frontend-backend.
Exemple : aide-mémoire des opérations CRUD
async handler(ctx) {
const resource = ctx.blockModel?.resource as MultiRecordResource;
// --- Créer ---
await resource.create({ title: 'New item', completed: false });
// Sans rafraîchissement automatique
await resource.create({ title: 'Draft' }, { refresh: false });
// --- Lire ---
const items = resource.getData(); // TDataItem[]
const count = resource.getCount(); // Nombre total d'enregistrements
const item = await resource.get(1); // Récupère un seul enregistrement par clé primaire
// --- Mettre à jour ---
await resource.update(1, { title: 'Updated' });
// --- Supprimer ---
await resource.destroy(1); // Suppression unique
await resource.destroy([1, 2, 3]); // Suppression en lot
// --- Pagination ---
resource.setPage(2);
resource.setPageSize(50);
await resource.refresh();
// Ou utiliser les méthodes raccourcies
await resource.goto(3);
await resource.next();
await resource.previous();
// --- Rafraîchir ---
await resource.refresh();
}
SingleRecordResource
Utilisée pour les scénarios à un seul enregistrement comme les formulaires, les pages de détails, etc. À importer depuis @nocobase/flow-engine.
Opérations sur les données
Propriétés clés
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 seul objet ou null
if (!data) return <div>Chargement...</div>;
return (
<div>
<h3>{data.title}</h3>
<p>{data.content}</p>
</div>
);
}
}
DetailBlockModel.define({
label: tExpr('Detail block'),
});
Exemple : créer et modifier un enregistrement
async handler(ctx) {
const resource = ctx.model.context.resource as SingleRecordResource;
// --- Créer un enregistrement ---
resource.isNewRecord = true;
await resource.save({ name: 'John', age: 30 });
// save appelle en interne l'action create, refresh automatique après l'opération
// --- Modifier un enregistrement existant ---
resource.setFilterByTk(1); // Définit automatiquement isNewRecord = false
await resource.refresh(); // Charge d'abord les données courantes
const data = resource.getData();
await resource.save({ ...data, name: 'Jane' });
// save appelle en interne l'action update
// --- Supprimer l'enregistrement courant ---
await resource.destroy(); // Utilise le filterByTk déjà défini
}
Méthodes communes
Les méthodes suivantes sont disponibles à la fois sur MultiRecordResource et SingleRecordResource :
Filtrage
Contrôle des champs
Configuration de la resource
Métadonnées et état
Événements
resource.on('saved', (data) => {
console.log('Enregistrement sauvegardé :', data);
});
Syntaxe Filter
NocoBase utilise une syntaxe de filtrage de style JSON, les opérateurs commencent par $ :
// Égal à
{ status: { $eq: 'active' } }
// Différent de
{ status: { $ne: 'deleted' } }
// Supérieur à
{ age: { $gt: 18 } }
// Contient (correspondance floue)
{ name: { $includes: 'test' } }
// Conditions combinées
{
$and: [
{ status: { $eq: 'active' } },
{ age: { $gt: 18 } },
]
}
// Conditions OU
{
$or: [
{ status: { $eq: 'active' } },
{ role: { $eq: 'admin' } },
]
}
Sur la Resource, il est recommandé d'utiliser addFilterGroup pour gérer les conditions de filtrage :
// Ajoute plusieurs groupes de filtres
resource.addFilterGroup('status', { status: { $eq: 'active' } });
resource.addFilterGroup('age', { age: { $gt: 18 } });
// getFilter() agrège automatiquement en : { $and: [...] }
// Supprime un groupe de filtres
resource.removeFilterGroup('status');
// Rafraîchit pour appliquer le filtre
await resource.refresh();
Comparaison entre MultiRecordResource et SingleRecordResource
Liens connexes