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
}
}
}
}