Skip to main content

Component Development Guide

Quick Start​

This article mainly introduces the development process of custom components.

Environment Setup​

  • Frontend development requires Node environment, recommended Node 14 or higher. After installation, execute node -v command in the command line interface to check the current installed version.

  • Package manager recommends using yarn, use yarn -v to check the version.

  • Download the component development template mycoms.zip file and extract it.

  • Execute yarn to install project dependencies.

Project Directory​

The project directory is as follows:

image-20210825210331456

The current template is a simplified Angular project, currently does not support other technology stacks for development, but you can install your preferred libraries as long as you follow the component development specifications.

Creating New Components​

The template includes two example components by default. You can create new components through automatic generation or manual execution.

  • Automatic Generation

    Use Angular's CLI tool to quickly create a new component, steps as follows.

    1. Use VSCode's Angular Schematics plugin.

      image-20210825210331456

    2. Right-click and select Angular: Generate a component.

  • Manual Execution

    Manually enter the command to generate the component.

    ng g component demo3 --change-detection OnPush --skip-tests

After the component is created, add the path of the new component in package.json.

"exposes": {
"Demo1": "./src/app/demo1/demo1.component.ts",
"Demo2": "./src/app/demo2/demo2.component.ts",
"Demo3": "./src/app/demo3/demo3.component.ts"
}

Starting the Project​

Run npm run start command to start the current project, default port is 3000, you can modify the port number in package.json.

"start": "ng serve --port=3000",

Previewing Components​

Accessing http://localhost:3000 shows an empty page. The simplest way is to add the component to app.component.html, for example:

<app-demo1></app-demo1>

However, this method cannot test component configuration items and event interactions.

Component Packaging​

Run npm run pack to package the component. The packaged component package (default in dist directory) must be compressed into a zip file before uploading.

Online Debugging​

Since there is no independent component development platform yet, we need to use the large screen editor to debug components. Specific operations are as follows:

  1. On the My Components page, add a new record.

    image-20210825210331456

    • 1: Plugin name (custom).
    • 2: Plugin unique code (custom, generally the lowercase form of the exported component name, e.g., demo1).
    • 3: Main entry for the development environment (local service + filename in package.json).
    • 4: Component container name (name in package.json, e.g., mycoms).
    • 5: Exported component (key in exposes in package.json, e.g., Demo3).
    • 6: Component name (class name of the new component, e.g., Demo3Component).
  2. After successful addition, the newly added component will be displayed in the My menu.

    image-20210825210331456

  3. Change the online address `xxx/screen/design/918` to `xxx/screen/dev/918` (change design to dev) to enter the online debugging environment. At this time, dragging the component to the canvas loads the local component, allowing you to debug component configuration, data, and interactions.

Development Specifications​

This article mainly introduces project files and essential component attributes.

File Overview​

For package.json, only the following attributes need attention.

{
"name": "mycoms",
"version": "1",
"description": "My Component Package",
"filename": "remoteEntry.js",
"exposes": {
"Demo1": "./src/app/demo1/demo1.component.ts",
"Demo2": "./src/app/demo2/demo2.component.ts"
},
…
}

The component template is a single package with multiple components. Each time a new component is created, the path of the component to be exported must be manually defined in exposes.

The component directory is as follows:

image-20210825210331456

Default components are separated into html, css, and ts.

Component Attributes​

AttributeTypeDescription
BaseattrComponentAttrBasic attributes of the component, including width, height, opacity, flip, etc.
StyleconfigGuiConfigsGUI configuration items for component styles.
optionsComponentOptionsDefault values for component styles, GUI form can change options.
DataapisComponentApisAPI configuration for component data, including processing methods and field definitions.
dataComponentDataStatic data of the component, also initialization data.
InteractioneventsComponentEventsEvents that the component can trigger.
actionsComponentActionsActions (functions) that the component can execute.

Component Hooks​

Hook FunctionTypeDescription
init() => voidCalled when the component is initialized.
destroy() => voidCalled when the component is destroyed.
render(data: any, options?: any) => voidComponent's data rendering method, defined in the apis attribute.
updateOptions(options: any) => voidCalled when GUI configuration items are updated.
resize(width?: number, height?: number) => voidCalled when the component size changes.

Configuration Controls​

This article mainly introduces the usage specifications of GUI controls.

Basic Usage​

GUI is essentially a dynamic form system that renders component configuration items through JSON data. GUI has many control types, which can be combined to construct any data structure. Each control has a unique type.

The following is a basic configuration example.

config: GuiConfigs = {
width: {
name: 'Width',
type: 'number',
default: 1920,
description: '',
suffix: 'px',
},
height: {
name: 'Height',
type: 'number',
default: 1080,
description: '',
suffix: 'px',
},
};

Rendering effect:

image-20210825210331456

Through the above GUI configuration, the following options can be obtained:

{
width: 1920,
height: 1080,
}

GUI configuration is mainly organized in object data format, which effectively prevents duplicate keys. The key in the configuration corresponds to the key in options. As long as the form control is modified, the value of options will be updated immediately (text input controls only update when the focus is lost).

Control Overview​

CategorytypeDescription
Basic ControlstextText input box, string type.
numberNumber input box, number type.
selectDropdown selection box, single selection only.
comboboxCombination dropdown box, supports single selection, multiple selection, search, etc.
radioRadio button.
checkboxCheckbox, array type.
buttonToggleButton toggle, supports single and multiple selection, can set text, icons, images.
switchSwitch, boolean type.
sliderSlider, number type.
fillColor control, supports solid colors and gradients.
hiddenHidden input box, input type is hidden.
imageImage upload.
videoVideo upload.
codeareaCode editor.
Composite ControlsgroupGroup, object type.
inlineInline group, object type.
tabsTab group, array type.
menuMenu, object type.

Note: Some controls have the same data output format, mainly differing in UI display effects, such as select and radio. Generally, if the number of options is less than 5, radio is preferred; in other scenarios, the two can be used interchangeably.

For specific control usage methods, please refer to Control Details.

Control Details​

Text​

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo
prefixPrefix textstringNo
suffixSuffix textstringNo

Example:

config: GuiConfigs = {
text: {
name: 'Text',
type: 'text',
default: 'I am the default value',
prefix: 'Prefix',
suffix: 'Suffix',
},
};

image-20210825210331456

Number​

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo
prefixPrefix textstringNo
suffixSuffix textstringNo
minMinimum valuenumberNo
maxMaximum valuenumberNo
stepStep valuenumberNo

Example:

config: GuiConfigs = {
number: {
name: 'Number',
type: 'number',
default: '123',
prefix: '$',
suffix: '.00',
},
};

image-20210825210331456

Select​

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo
prefixPrefix textstringNo
suffixSuffix textstringNo
optionsOptions listarrayNo
useFontWhether to render fontbooleanNo

Example:

config: GuiConfigs = {
font: {
name: 'Font',
type: 'select',
default: 'SimSun',
options: [
{ value: 'Microsoft Yahei', label: 'Microsoft Yahei' },
{ value: 'SimHei', label: 'SimHei' },
{ value: 'SimSun', label: 'SimSun' },
{ value: 'fangsong', label: 'Fangsong' },
{ value: 'KaiTi', label: 'KaiTi' },
{ value: 'Arial', label: 'Arial' },
{ value: 'fantasy', label: 'Fantasy' },
],
useFont: true,
},
};

image-20210825210331456

image-20210825210331456

Combobox​

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo
prefixPrefix textstringNo
suffixSuffix textstringNo
optionsOptions listarrayNo
multipleWhether multiple selectionbooleanNo
bindValuevalue mappingstringNo
bindLabellabel mappingstringNo

Single selection example:

config: GuiConfigs = {
search: {
name: 'Search',
type: 'combobox',
options: [
{ value: 1, label: 'Apple' },
{ value: 2, label: 'Lemon' },
{ value: 3, label: 'Lime' },
{ value: 4, label: 'Orange', disabled: true },
{ value: 5, label: 'Strawberry' },
],
},
};

image-20210825210331456

Multiple selection example:

config: GuiConfigs = {
search: {
name: 'Search Multiple',
type: 'combobox',
multiple: true,
options: [
{ value: 1, label: 'Apple' },
{ value: 2, label: 'Lemon' },
{ value: 3, label: 'Lime' },
{ value: 4, label: 'Orange', disabled: true },
{ value: 5, label: 'Strawberry' },
],
},
};

image-20210825210331456

Radio​

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo
optionsOptions listarrayNo

Example:

config: GuiConfigs = {
alignRadio: {
name: 'Alignment',
type: 'radio',
default: 'left',
options: [
{ label: 'Left Align', value: 'left', col: 50 },
{ label: 'Center Align', value: 'center', col: 50 },
{ label: 'Right Align', value: 'right', col: 50 },
],
},
};

image-20210825210331456

Checkbox​

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo
optionsOptions listarrayNo

Example:

config: GuiConfigs = {
checkbox: {
name: 'Checkbox',
type: 'checkbox',
options: [
{ value: 'option1', label: 'Option One' },
{ value: 'option2', label: 'Option Two' },
{ value: 'option3', label: 'Option Three' },
{ value: 'option4', label: 'Option Four' },
],
},
};

image-20210825210331456

ButtonToggle​

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo
optionsOptions listarrayNo
useIconWhether to use iconsbooleanNo

Text Button Example​

config: GuiConfigs = {
align: {
name: 'Alignment',
type: 'buttonToggle',
options: [
{ value: 'left', label: 'Left Align', col: 50 },
{ value: 'center', label: 'Center Align', col: 50 },
{ value: 'right', label: 'Right Align', col: 50 },
{ value: 'top', label: 'Top Align', col: 50 },
{ value: 'bottom', label: 'Bottom Align', col: 50 },
],
},
};

image-20210825210331456

Icon Button Example​

Icon library: https://materialdesignicons.com/

config: GuiConfigs = {
alignIcon: {
name: 'Alignment',
type: 'buttonToggle',
useIcon: true,
options: [
{ value: 'left', label: 'Left', src: 'format_align_left' },
{ value: 'center', label: 'Center', src: 'format_align_center' },
{ value: 'right', label: 'Right', src: 'format_align_right' },
],
},
};

image-20210825210331456

Image Button Example​

config: GuiConfigs = {
direction: {
name: 'Move Direction',
type: 'buttonToggle',
useIcon: true,
options: [
{
value: 'left',
label: 'Left',
src: 'https://img.icons8.com/stickers/2x/chevron-left.png',
},
{
value: 'right',
label: 'Right',
src: 'https://img.icons8.com/stickers/2x/chevron-right.png',
},
{
value: 'up',
label: 'Up',
src: 'https://img.icons8.com/stickers/2x/chevron-up.png',
},
{
value: 'down',
label: 'Down',
src: 'https://img.icons8.com/stickers/2x/chevron-down.png',
},
],
},
};

image-20210825210331456

Switch​

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo

Example:

config: GuiConfigs = {
switch: {
name: 'Switch',
type: 'switch',
},
};

image-20210825210331456

Slider​

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo
prefixPrefix textstringNo
suffixSuffix textstringNo
minMinimum valuenumberNo
maxMaximum valuenumberNo
stepStep valuenumberNo

Example:

config: GuiConfigs = {
slider: {
name: 'Opacity',
type: 'slider',
step: 0.1,
min: 0,
max: 1,
},
};

image-20210825210331456

Fill​

Configuration items:

FieldMeaningTypeRequiredRemarks
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo
modeFill mode'flat''gradient'No

Solid Color Fill​

Example:

config: GuiConfigs = {
flat: {
name: 'Solid Color Fill',
type: 'fill',
default: '#fff,
},
};

image-20210825210331456

Gradient Fill​

The default value of the gradient control is CSS syntax, supporting linear gradients, radial gradients, and conic gradients.

Note: If you want to use gradients in charts, you may need to manually parse the gradient values. A better approach is to use combinations of different controls.

Example:

config: GuiConfigs = {
gradient: {
name: 'Gradient Fill',
type: 'fill',
mode: 'gradient',
default: 'linear-gradient(90deg, white 0%, black 100%)',
},
};

image-20210825210331456

Hidden​

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
defaultDefault valueanyNo

Example:

config: GuiConfigs = {
hiddenInput: {
name: 'Hidden Value',
type: 'hidden',
default: 100,
},
};

Image​

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo

Example:

config: GuiConfigs = {
image: {
name: 'Upload Cover',
type: 'image',
default: 'https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg',
},
};

image-20210825210331456

Video​

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo

Example:

config: GuiConfigs = {
video: {
name: 'Upload Video',
type: 'video',
default: 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4',
},
};

image-20210825210331456

Codearea​

The Codearea control is based on monaco-editor.

Configuration items:

FieldMeaningTypeRequiredRemarks
typeControl typestringYes
nameLabel namestringYes
defaultDefault valuestringNo
heightEditor heightstringNo
editorOptionsEditor configuration optionsobjectNoOfficial Documentation

Example:

config: GuiConfigs = {
codearea: {
name: 'Code Editor',
type: 'codearea',
height: '200px',
editorOptions: {
language: 'javascript',
},
},
};

image-20210825210331456

Group​

Group is used to combine multiple controls together, the control UI is accordion style.

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
childrenChild componentsobjectNo

Example:

config: GuiConfigs = {
group: {
name: 'Group',
type: 'group',
children: {
display: {
name: 'Display',
type: 'switch',
default: true,
},
stepper: {
name: 'Stepper',
type: 'slider',
default: 1,
},
},
},
};

image-20210825210331456

Data:

{
group:{
display: true,
stepper: 1
}
}

Inline​

Inline is used for inline grouping, components can specify col to set width.

Configuration items:

FieldMeaningTypeRequired
typeControl typestringYes
nameLabel namestringYes
childrenChild componentsobjectNo

Example:

config: GuiConfigs = {
inline: {
type: 'inline',
name: 'Inline Group',
children: {
width: {
name: 'Width',
type: 'number',
default: 1920,
suffix: 'px',
col: 50,
},
height: {
name: 'Height',
type: 'number',
default: 1080,
suffix: 'px',
col: 50,
},
angle: {
name: 'Angle',
type: 'select',
default: '0',
options: [
{ value: '0', label: 'Horizontal' },
{ value: '45', label: 'Diagonal' },
{ value: '90', label: 'Vertical' },
],
col: 50,
},
amount: {
name: 'Amount',
type: 'number',
default: 0,
min: 0,
step: 1,
col: 50,
},
},
},
};

image-20210825210331456

Data:

{
inline:{
width: 1920,
height: 1080,
angle: '0',
amount: 0
}
}

Tabs​

Tabs are used to display arrays and implement dynamic addition and deletion of anonymous arrays. They can be nested arbitrarily to achieve very complex data structures.

Configuration items:

FieldMeaningTypeRequiredRemarks
typeControl typestringYesMay need to be used with the tab type.
nameLabel namestringYes
childrenChild componentsobjectarrayNo
templateTemplate for dynamic addition/deletionobjectNo
addableWhether addition/deletion is allowedbooleanNo

Basic Array​

Each item in the array is different.

Example:

config: GuiConfigs = {
tabs: {
name: 'Fixed Array',
type: 'tabs',
default: [
{
fullname: {
firstName: 'Zhang',
lastName: 'San',
},
},
{ switch: true },
],
children: [
{
name: 'tab1',
type: 'tab',
children: {
fullname: {
name: 'Full Name',
type: 'inline',
children: {
firstName: {
type: 'text',
name: 'First Name',
col: 50,
},
lastName: {
type: 'text',
name: 'Last Name',
col: 50,
},
},
},
},
},
{
name: 'tab2',
type: 'tab',
children: {
switch: {
name: 'Switch',
type: 'switch',
},
},
},
],
},
};

image-20210825210331456

Data:

{
tabs: [
{
fullname: {
firstName: 'Zhang',
lastName: 'San'
}
},
{
switch: true
}
]
}

Dynamic Array​

If the array items are all the same, you can use the dynamic addition/deletion configuration. The name in the template supports EJS expressions.

Example:

config: GuiConfigs = {
tabs: {
name: 'Dynamic Array',
type: 'tabs',
default: [
{ seriesName: 'Primary Industry' },
{ seriesName: 'Secondary Industry' }
],
template: {
name: 'Series <%= i + 1 %>',
children: {
seriesName: {
type: 'text',
name: 'Series Name',
},
},
},
},
};

image-20210825210331456

Data:

{
tabs: [
{
seriesName: 'Primary Industry'
},
{
seriesName: 'Secondary Industry'
}
]
}

Basic Array (Basic Data)​

Example:

config: GuiConfigs = {
tabs: {
name: 'Basic Array (Basic Data)',
type: 'tabs',
children: [
{
type: 'text',
name: 'Series Name',
},
{
type: 'switch',
name: 'Switch',
},
],
},
};

image-20210825210331456

Data:

{
tabs: ['123', true]
}

Dynamic Array (Basic Data)​

Example:

config: GuiConfigs = {
tabs: {
name: 'Dynamic Array (Basic Data)',
type: 'tabs',
default: ['123', '456'],
template: {
name: 'Series <%= i + 1 %>',
type: 'text',
},
},
};

image-20210825210331456

Data:

{
tabs: ['123', '456']
}

Nested Basic Array (Basic Data)​

Example:

config: GuiConfigs = {
tabs: {
name: 'Nested Basic Array (Basic Data)',
type: 'tabs',
children: [
{
type: 'tabs',
name: 'level1',
children: [
{
type: 'text',
name: 'level2',
},
{
type: 'number',
name: 'level2',
},
],
},
{
type: 'switch',
name: 'Switch',
},
],
},
};

image-20210825210331456

Data:

{
tabs: [
[1, 2],
true
]
}

Nested Dynamic Array (Basic Data)​

Example:

config: GuiConfigs = {
tabs: {
name: 'Nested Dynamic Array (Basic Data)',
type: 'tabs',
default: [['123']],
template: {
name: 'level1 <%= i + 1%>',
type: 'tabs',
template: {
name: 'level2 <%= i + 1%>',
type: 'text',
},
},
},
};

image-20210825210331456

Data:

{
tabs: [
['123', '456'],
['123', '456'],
]
}

Menu is a higher-level grouping component that supports infinite levels, mainly used to organize data, with fewer usage scenarios.

FieldMeaningTypeRequiredRemarks
typeControl typestringYesNeeds to be used with the menuItem type.
nameLabel namestringYes
childrenChild componentsobjectNo

Example:

config: GuiConfigs = {
menu: {
name: 'Menu',
type: 'menu',
children: {
menuA: {
name: 'Menu A',
type: 'menuItem',
children: {
size: {
type: 'inline',
name: 'Screen Size',
children: {
width: {
name: 'Width',
type: 'number',
default: 1920,
suffix: 'px',
col: 50,
},
height: {
name: 'Height',
type: 'number',
default: 1080,
suffix: 'px',
col: 50,
},
},
},
align: {
name: 'Alignment',
type: 'buttonToggle',
options: [
{ value: 'left', label: 'Left Align', col: 50 },
{ value: 'center', label: 'Center Align', col: 50 },
{ value: 'right', label: 'Right Align', col: 50 },
{ value: 'top', label: 'Top Align', col: 50 },
{ value: 'bottom', label: 'Bottom Align', col: 50 },
],
},
angle: {
name: 'Angle',
type: 'select',
default: '0',
options: [
{ value: '0', label: 'Horizontal', src: 'horizontal' },
{ value: '45', label: 'Diagonal', src: 'incline' },
{ value: '90', label: 'Vertical', src: 'vertical' },
],
},
},
},
menuB: {
name: 'Menu B',
type: 'menu',
children: {
menuB1: {
name: 'Menu B1',
type: 'menuItem',
children: {
text: {
name: 'Text',
type: 'text',
default: 'Hello',
},
slider: {
name: 'Opacity',
type: 'slider',
step: 0.1,
min: 0,
max: 1,
},
},
},
menuB2: {
name: 'Menu B2',
type: 'menuItem',
children: {
display: {
name: 'Display',
type: 'switch',
default: true,
},
stepper: {
name: 'Stepper',
type: 'number',
default: 1,
},
},
},
},
},
},
},
};

image-20210825210331456

image-20210825210331456

Data:

{
menu: {
menuA: {
align: "left",
angle: "0",
size: {
height: 1080
width: 1920,
}
},
menuB: {
menuB1: {
slider: 1,
text: "Hello"
},
menuB2: {
display: true,
stepper: 1
}
}
}
}