NocoBase has many Add block buttons for adding blocks to the interface. Some are related to data tables and are called data blocks Data Block, while others that are not related to data tables are called simple blocks Simple Block.
However, the existing block types may not meet our requirements, so we need to develop custom blocks according to our needs. This article focuses on explaining data blocks Data Block.
This example will create an Info block and add it to the Add block in Page, Table, and mobile.
This example is mainly to demonstrate the use of initializer. For more information about block extension, please refer to the Block Extension documentation.
The complete example code for this document can be found in plugin-samples.
Following the Write Your First Plugin documentation, if you don't have a project yet, you can create one first. If you already have one or have cloned the source code, you can skip this step.
This example is about an Info block component with the following specific requirements:
Display the current block's data table name
Display the current block's data list
First, we create packages/plugins/@nocobase-sample/plugin-initializer-block-data/src/client/component/Info.tsx file with the following content:
import React, { FC } from 'react';import { withDynamicSchemaProps } from '@nocobase/client'import { BlockName } from '../constants';export interface InfoProps { collectionName: string; data?: any[]; loading?: boolean;}export const Info: FC<InfoProps> = withDynamicSchemaProps(({ collectionName, data }) => { return <div> <div>collection: {collectionName}</div> <div>data list: <pre>{JSON.stringify(data, null, 2)}</pre></div> </div>}, { displayName: BlockName })
The Info component is essentially a functional component wrapped by withDynamicSchemaProps. withDynamicSchemaProps is a higher-order component used to handle dynamic properties in Schema.
If we ignore withDynamicSchemaProps, Info is just a simple functional component.
Then export it in packages/plugins/@nocobase-sample/plugin-initializer-block-data/src/client/component/index.ts:
We need to register Info to the system through the plugin.
import { Plugin } from '@nocobase/client';import { Info } from './component';export class PluginInitializerBlockDataClient extends Plugin { async load() { this.app.addComponents({ Info }) }}export default PluginInitializerBlockDataClient;
Temporary page verification: We can temporarily create a page and render the Info component to check if it meets the requirements
Documentation example verification: You can start the documentation yarn doc plugins/@nocobase-sample/plugin-initializer-block-data, and verify if it meets the requirements by writing documentation examples (TODO)
We use temporary page verification as an example. We create a new page and add one or more Info components according to property parameters to check if they meet the requirements.
import React from 'react';import { Plugin } from '@nocobase/client';import { Info } from './component';export class PluginInitializerBlockDataClient extends Plugin { async load() { this.app.addComponents({ Info }) this.app.router.add('admin.info-component', { path: '/admin/info-component', Component: () => { return <> <div style={{ marginTop: 20, marginBottom: 20 }}> <Info collectionName='test' data={[{ id: 1 }, { id: 2 }]} /> </div> </> } }) }}export default PluginInitializerBlockDataClient;
Then visit http://localhost:13000/admin/info-component to see the corresponding test page content.
After verification, the test page needs to be deleted.
NocoBase's dynamic pages are all rendered through Schema, so we need to define a Schema, which will be used later to add the Info block to the interface. Before implementing this section, we need to understand some basic knowledge:
UI Schema Protocol: Detailed introduction to the structure of Schema and the role of each property
getInfoSchema(): The reason for defining it as a function is that dataSource and collection are dynamic and determined by the clicked data table
useInfoProps(): Used to handle the dynamic properties of the Info component, and because it needs to be stored in the database, the value type here is a string type.
getInfoSchema(): Returns the Schema of Info
type: 'void': Indicates no data
x-decorator: 'DataBlockProvider': Data block provider, used to provide data. For more information about DataBlockProvider, please refer to DataBlockProvider
x-decorator-props: Properties of DataBlockProvider
dataSource: Data source
collection: Data table
action: 'list': Operation type, here it is list, to get the data list
x-component: 'CardItem': CardItem component, currently all blocks are wrapped in cards, which provide styles, layouts, and drag-and-drop functionality
properties: Child nodes
info: Info block
useInfoProps(): Dynamic properties of the Info component
Same as verifying components, we can verify the Schema by temporary page verification or documentation example verification. Here we use temporary page verification as an example:
"x-toolbar": "BlockSchemaToolbar": BlockSchemaToolbar is used to display the current data table in the upper left corner, usually used with DataBlockProvider
For more information about Schema Initializer definitions, please refer to the Schema Initializer documentation.
A complete Block also needs to have Schema Settings, which are used to configure some properties and operations, but Schema Settings is not the focus of this example, so we only have a remove operation here.
We create packages/plugins/@nocobase-sample/plugin-initializer-block-data/src/client/settings/index.ts file:
We modify the getInfoSchema method in the packages/plugins/@nocobase-sample/plugin-initializer-block-data/src/client/schema/index.ts file to set x-settings to infoSettings.name.
If we need to add it to the page-level Add block, we need to know the corresponding name. We can view the corresponding name through TODO method.
TODO
From the above figure, we can see that the page-level Add block corresponds to the name page:addBlock, and Data Blocks corresponds to the name dataBlocks.
Then we modify packages/plugins/@nocobase-sample/plugin-initializer-block-data/src/client/index.tsx file:
import { Plugin } from '@nocobase/client';import { Info } from './component';import { useInfoProps } from './schema';import { infoSettings } from './settings';import { infoInitializerItem } from './initializer';export class PluginDataBlockInitializerClient extends Plugin { async load() { this.app.addComponents({ Info }); this.app.addScopes({ useInfoProps }); this.app.schemaSettingsManager.add(infoSettings); this.app.schemaInitializerManager.addItem('page:addBlock', `dataBlocks.${infoInitializerItem.name}`, infoInitializerItem) }}export default PluginDataBlockInitializerClient;
We need to add it not only to the page-level Add block, but also to the Add block in the Table block Add new modal.
According to the method of obtaining the page-level name, we get the Add block name of the Table block as popup:addNew:addBlock, and Data Blocks corresponds to the name dataBlocks.
Then modify packages/plugins/@nocobase-sample/plugin-initializer-block-data/src/client/index.tsx file:
import { Plugin } from '@nocobase/client';import { Plugin } from '@nocobase/client';import { Info } from './component';import { useInfoProps } from './schema';import { infoSettings } from './settings';import { infoInitializerItem } from './initializer';export class PluginDataBlockInitializerClient extends Plugin { async load() { this.app.addComponents({ Info }); this.app.addScopes({ useInfoProps }); this.app.schemaSettingsManager.add(infoSettings); this.app.schemaInitializerManager.addItem('page:addBlock', `dataBlocks.${infoInitializerItem.name}`, infoInitializerItem)+ this.app.schemaInitializerManager.addItem('popup:addNew:addBlock', `dataBlocks.${infoInitializerItem.name}`, infoInitializerItem) }}export default PluginDataBlockInitializerClient;