Resource API
FlowEngine NocoBase menyediakan dua class Resource untuk menangani operasi data frontend—MultiRecordResource untuk list/tabel (banyak record), SingleRecordResource untuk form/detail (satu record). Mereka membungkus pemanggilan REST API, menyediakan manajemen data yang reaktif.
Rantai inheritance: FlowResource → APIResource → BaseRecordResource → MultiRecordResource / SingleRecordResource
MultiRecordResource
Digunakan untuk skenario list, tabel, kanban dengan banyak record. Diimport dari @nocobase/flow-engine.
Operasi Data
Baris Terpilih
Contoh: Penggunaan dalam CollectionBlockModel
Saat extends CollectionBlockModel, perlu membuat resource melalui createResource(), lalu membaca data di 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;
// Mendeklarasikan menggunakan MultiRecordResource untuk mengelola data
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 record
return (
<div>
<h3>Total {count} record (Halaman {this.resource.getPage()})</h3>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
}
ManyRecordBlockModel.define({
label: tExpr('Many records block'),
});
Untuk contoh lengkap lihat FlowEngine → Ekstensi Blok.
Contoh: Memanggil CRUD dalam Tombol Action
Di handler registerFlow dari ActionModel, dapatkan resource blok saat ini melalui ctx.blockModel?.resource, lalu panggil method 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) {
// Mendapatkan resource blok saat ini
const resource = ctx.blockModel?.resource as MultiRecordResource;
if (!resource) return;
ctx.viewer.dialog({
title: ctx.t('New todo'),
content: (view) => (
<MyForm
onSubmit={async (values) => {
// Membuat record, setelah pembuatan resource akan otomatis refresh
await resource.create(values);
ctx.message.success(ctx.t('Created successfully'));
view.close();
}}
onCancel={() => view.close()}
/>
),
});
},
},
},
});
Untuk contoh lengkap lihat Membuat plugin manajemen data full-stack.
Contoh: Cheat Sheet Operasi CRUD
async handler(ctx) {
const resource = ctx.blockModel?.resource as MultiRecordResource;
// --- Membuat ---
await resource.create({ title: 'New item', completed: false });
// Tidak refresh otomatis
await resource.create({ title: 'Draft' }, { refresh: false });
// --- Membaca ---
const items = resource.getData(); // TDataItem[]
const count = resource.getCount(); // Total record
const item = await resource.get(1); // Mengambil satu record berdasarkan primary key
// --- Memperbarui ---
await resource.update(1, { title: 'Updated' });
// --- Menghapus ---
await resource.destroy(1); // Hapus satu
await resource.destroy([1, 2, 3]); // Hapus batch
// --- Pagination ---
resource.setPage(2);
resource.setPageSize(50);
await resource.refresh();
// Atau gunakan method shortcut
await resource.goto(3);
await resource.next();
await resource.previous();
// --- Refresh ---
await resource.refresh();
}
SingleRecordResource
Digunakan untuk skenario form, halaman detail dengan satu record. Diimport dari @nocobase/flow-engine.
Operasi Data
Properti Kunci
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(); // Objek tunggal atau null
if (!data) return <div>Memuat...</div>;
return (
<div>
<h3>{data.title}</h3>
<p>{data.content}</p>
</div>
);
}
}
DetailBlockModel.define({
label: tExpr('Detail block'),
});
Contoh: Membuat dan Mengedit Record
async handler(ctx) {
const resource = ctx.model.context.resource as SingleRecordResource;
// --- Membuat record baru ---
resource.isNewRecord = true;
await resource.save({ name: 'John', age: 30 });
// save secara internal memanggil action create, otomatis refresh setelah selesai
// --- Mengedit record yang ada ---
resource.setFilterByTk(1); // Otomatis mengatur isNewRecord = false
await resource.refresh(); // Muat data saat ini terlebih dahulu
const data = resource.getData();
await resource.save({ ...data, name: 'Jane' });
// save secara internal memanggil action update
// --- Menghapus record saat ini ---
await resource.destroy(); // Menggunakan filterByTk yang sudah diatur
}
Method Umum
Method-method berikut tersedia di kedua MultiRecordResource dan SingleRecordResource:
Filter
Kontrol Field
Konfigurasi Resource
Event
resource.on('saved', (data) => {
console.log('Record telah disimpan:', data);
});
Sintaks Filter
NocoBase menggunakan sintaks filter gaya JSON, operator dimulai dengan $:
// Sama dengan
{ status: { $eq: 'active' } }
// Tidak sama dengan
{ status: { $ne: 'deleted' } }
// Lebih besar dari
{ age: { $gt: 18 } }
// Berisi (fuzzy matching)
{ name: { $includes: 'test' } }
// Kondisi gabungan
{
$and: [
{ status: { $eq: 'active' } },
{ age: { $gt: 18 } },
]
}
// Kondisi or
{
$or: [
{ status: { $eq: 'active' } },
{ role: { $eq: 'admin' } },
]
}
Pada Resource, direkomendasikan menggunakan addFilterGroup untuk mengelola kondisi filter:
// Menambahkan beberapa filter group
resource.addFilterGroup('status', { status: { $eq: 'active' } });
resource.addFilterGroup('age', { age: { $gt: 18 } });
// getFilter() otomatis mengagregasi menjadi: { $and: [...] }
// Menghapus filter group tertentu
resource.removeFilterGroup('status');
// Refresh menerapkan filter
await resource.refresh();
Perbandingan MultiRecordResource dan SingleRecordResource
Tautan Terkait