Tạo Plugin quản lý dữ liệu phối hợp client-server
Các ví dụ trước hoặc thuần client (block, field, action), hoặc client + API đơn giản (trang cài đặt). Ví dụ này hướng dẫn một kịch bản hoàn chỉnh hơn — phía server định nghĩa collection, phía client kế thừa TableBlockModel để có đầy đủ khả năng table, kết hợp Field component tùy chỉnh và nút Action tùy chỉnh, tạo thành một plugin quản lý dữ liệu có CRUD.
Ví dụ này kết hợp các kiến thức về Block, Field, Action đã học ở các phần trước, thể hiện luồng phát triển đầy đủ của một Plugin.
Nên tìm hiểu các nội dung sau trước khi phát triển để mượt mà hơn:
- Viết Plugin đầu tiên — Tạo plugin và cấu trúc thư mục
- Plugin — Entry point và lifecycle
load()của plugin - FlowEngine → Mở rộng Block — BlockModel, CollectionBlockModel, TableBlockModel
- FlowEngine → Mở rộng Field — ClickableFieldModel, bindModelToInterface
- FlowEngine → Mở rộng Action — ActionModel, ActionSceneEnum
- i18n — Cách viết file dịch và cách dùng
tExpr() - Tổng quan phát triển phía server — Cơ bản về plugin phía server
Kết quả cuối cùng
Chúng ta sẽ tạo một plugin quản lý dữ liệu "Việc cần làm", bao gồm các khả năng:
- Phía server định nghĩa một collection
todoItems, plugin sẽ tự động ghi dữ liệu mẫu khi cài đặt - Phía client kế thừa
TableBlockModel, có ngay block table dùng được luôn (cột field, phân trang, action bar, v.v.) - Field component tùy chỉnh — render field priority bằng Tag màu
- Nút Action tùy chỉnh — nút "Tạo việc mới", click sẽ mở dialog điền form để tạo record
Toàn bộ source code xem tại @nocobase-example/plugin-custom-table-block-resource. Nếu bạn muốn chạy thử trực tiếp ở local:
Sau đây ta sẽ xây dựng plugin này từ đầu, từng bước một.
Bước 1: Tạo khung plugin
Tại thư mục gốc của repo, chạy:
Chi tiết xem tại Viết Plugin đầu tiên.
Bước 2: Định nghĩa collection (phía server)
Tạo file mới src/server/collections/todoItems.ts. NocoBase sẽ tự động load các collection definition trong thư mục này:
Khác với ví dụ trang cài đặt, ở đây không cần đăng ký resource thủ công — NocoBase sẽ tự động sinh các API CRUD chuẩn (list, get, create, update, destroy) cho mỗi collection.
Bước 3: Cấu hình quyền và dữ liệu mẫu (phía server)
Chỉnh sửa src/server/plugin.ts, cấu hình quyền ACL trong load() và chèn dữ liệu mẫu trong install():
Một số điểm chính:
acl.allow()—['list', 'get', 'create', 'update', 'destroy']mở quyền CRUD đầy đủ,'loggedIn'nghĩa là người dùng đã đăng nhập có thể truy cậpinstall()— Chỉ chạy khi plugin được cài lần đầu, phù hợp để ghi dữ liệu khởi tạothis.db.getRepository()— Lấy đối tượng thao tác dữ liệu qua tên collection- Không cần
resourceManager.define()— NocoBase sẽ tự động sinh API CRUD cho collection
Bước 4: Tạo Block Model (phía client)
Tạo file mới src/client-v2/models/TodoBlockModel.tsx. Kế thừa TableBlockModel cho phép có ngay đầy đủ khả năng của block table — cột field, action bar, phân trang, sắp xếp, v.v., không cần tự viết renderComponent.

Trong phát triển plugin thực tế, nếu bạn không cần tùy chỉnh TableBlockModel, bạn thực sự không cần kế thừa và đăng ký block này, chỉ cần để người dùng chọn "Table" khi thêm block là được. Bài viết này viết một TodoBlockModel kế thừa TableBlockModel chỉ để minh họa luồng định nghĩa và đăng ký Block model. TableBlockModel sẽ xử lý tất cả phần còn lại (cột field, action bar, phân trang, v.v.).
Qua filterCollection, ta giới hạn block này chỉ áp dụng cho collection todoItems — khi người dùng thêm "Todo block", trong danh sách chọn collection sẽ chỉ hiện todoItems, không hiện các collection không liên quan khác.

Bước 5: Tạo Field component tùy chỉnh (phía client)
Tạo file mới src/client-v2/models/PriorityFieldModel.tsx. Render field priority bằng Tag màu, trực quan hơn nhiều so với plain text:

Sau khi đăng ký, trong cấu hình cột priority của table, dropdown "Field component" sẽ có thể chuyển sang "Priority tag".
Bước 6: Tạo nút Action tùy chỉnh (phía client)
Tạo file mới src/client-v2/models/NewTodoActionModel.tsx. Sau khi click nút "Tạo việc mới", dùng ctx.viewer.dialog() để mở dialog, điền form xong sẽ tạo record:

Một số điểm chính:
ActionSceneEnum.collection— Nút xuất hiện trong action bar phía trên blockon: 'click'— Lắng nghe sự kiệnclickcủa nút quaregisterFlowctx.viewer.dialog()— Khả năng dialog tích hợp sẵn của NocoBase,contentnhận một function, tham sốviewcó thể gọiview.close()để đóng dialogresource.create(values)— Gọi API create của collection để tạo record, sau khi tạo xong table sẽ tự động refreshobservable+observer— Dùng quản lý trạng thái phản ứng của flow-engine để thay thếuseState, component sẽ tự động phản ứng với thay đổi củaformState.loading
Bước 7: Thêm file đa ngôn ngữ
Chỉnh sửa file dịch trong src/locale/ của plugin:
Khi thêm file ngôn ngữ lần đầu, bạn cần khởi động lại ứng dụng để có hiệu lực.
Về cách viết file dịch và các cách dùng khác của tExpr(), xem chi tiết tại i18n.
Bước 8: Đăng ký trong Plugin (phía client)
Chỉnh sửa src/client-v2/plugin.tsx. Cần làm hai việc: đăng ký các Model, và đăng ký todoItems vào data source phía client.
Đăng ký collection thủ công qua addCollection trong code plugin là cách làm hiếm gặp, ở đây chỉ để minh họa luồng phối hợp client-server đầy đủ. Trong dự án thực tế, collection thường được người dùng tạo và cấu hình trên giao diện NocoBase, hoặc quản lý qua API / MCP, không cần đăng ký rõ ràng trong code phía client của plugin.
Collection được định nghĩa qua defineCollection là collection nội bộ của server, mặc định không xuất hiện trong danh sách chọn collection của block. Sau khi đăng ký thủ công qua addCollection, người dùng sẽ chọn được todoItems khi thêm block.

Một số điểm chính:
registerModelLoaders— Load và đăng ký theo nhu cầu cho ba model: Block, Field, Actionthis.app.eventBus— Event bus cấp ứng dụng, dùng để lắng nghe các sự kiện lifecycle- Sự kiện
dataSource:loaded— Trigger sau khi data source load xong. Bắt buộc phải gọiaddCollectiontrong callback của sự kiện này, vìensureLoaded()sẽ chạy sauload(), và nó sẽ clear toàn bộ rồi set lại các collection — nếu gọiaddCollectiontrực tiếp trongload()sẽ bị ghi đè addCollection()— Đăng ký collection vào data source phía client. Field cần kèm các thuộc tínhinterfacevàuiSchema, để NocoBase biết cách renderfilterTargetKey: 'id'— Bắt buộc phải có, chỉ định field dùng để định danh duy nhất cho record (thường là primary key). Nếu không đặt, collection sẽ không xuất hiện trong danh sách chọn collection của blockdefineCollectionphía server chịu trách nhiệm tạo bảng vật lý và mapping ORM,addCollectionphía client chịu trách nhiệm cho UI biết về sự tồn tại của collection — cả hai phối hợp mới hoàn thành việc liên kết client-server
Bước 9: Bật plugin
Sau khi bật:
- Tạo trang mới, click "Thêm block", chọn "Todo block", bind với collection
todoItems - Table sẽ tự động load dữ liệu, hiển thị cột field, phân trang, v.v.
- Trong "Cấu hình action", thêm nút "New todo", click sẽ mở dialog điền form để tạo record
- Trong "Field component" của cột priority, chuyển sang "Priority tag", priority sẽ được hiển thị bằng Tag màu
Source code đầy đủ
- @nocobase-example/plugin-custom-table-block-resource — Ví dụ đầy đủ về plugin quản lý dữ liệu phối hợp client-server
Tóm tắt
Các khả năng được sử dụng trong ví dụ này:
Liên kết liên quan
- Viết Plugin đầu tiên — Tạo khung plugin từ đầu
- Tổng quan FlowEngine — Cách dùng cơ bản của FlowModel và registerFlow
- FlowEngine → Mở rộng Block — BlockModel, TableBlockModel
- FlowEngine → Mở rộng Field — ClickableFieldModel, bindModelToInterface
- FlowEngine → Mở rộng Action — ActionModel, ActionSceneEnum
- Tạo một Block hiển thị tùy chỉnh — Ví dụ cơ bản về BlockModel
- Tạo một Field component tùy chỉnh — Ví dụ cơ bản về FieldModel
- Tạo một nút Action tùy chỉnh — Ví dụ cơ bản về ActionModel
- Tổng quan phát triển phía server — Cơ bản về plugin phía server
- Server → Collections — defineCollection và addCollection
- Tham khảo nhanh Resource API — Chữ ký method đầy đủ của MultiRecordResource / SingleRecordResource
- Plugin — Entry point và lifecycle load() của plugin
- i18n — Cách viết file dịch và cách dùng tExpr
- Server → ACL — Cấu hình quyền
- Server → Plugin — Lifecycle plugin phía server
- Context → Khả năng thường dùng — ctx.viewer, ctx.message, v.v.
- Phát triển Component — Cách dùng các component như Antd Form
- Tài liệu đầy đủ FlowEngine — Tham chiếu đầy đủ về FlowModel, Flow, Context

