Custom React components
In this section let's discuss how to create our own React component and connect it to FormBuilder and an app. Component code example is uploaded to GitHub. Let's examine this example step by step.
Step 1. Creating React component
First let's write any React component we need. In our example we are working with Counter component which displays form data values and contains two buttons - to increase value by 1 and to decrease value by 1. This component can be linked to data and looks the following way:
Here's its code:
export class CounterControl extends React.Component {
constructor(props) {
super(props);
}
changeValue(e, newValue) {
const props = this.props;
if (props.handleEvent) {
props.handleEvent({ syntheticEvent: e, key: props.name, eventName: "onChange", name: props.name, value: newValue });
}
}
onInc = (e) => {
this.changeValue(e, this.getValue() + 1);
}
onDec = (e) => {
this.changeValue(e, this.getValue() - 1);
}
getValue() {
const data = this.props.data;
if (!data) return 0;
return data[this.props.name] ? data[this.props.name] : 0;
}
render() {
const label = this.props.label ? <label>{this.props.label}</label> : null;
return (
<div className="field">
{label}
<Button.Group>
<Button content={this.props.incButtonText} onClick={this.onInc} />
<Button.Or text={this.getValue()} />
<Button content={this.props.decButtonText} onClick={this.onDec} />
</Button.Group>
</div>
);
}
}
You can see that it's a very simple component.
-
Component receives all form data in its
data
property. If there is a property which name coincides with componentprops.name
(i.e. component name) indata
, this is the data component will display. The code is as follows:getValue() {
const data = this.props.data;
return data ? data[this.props.name] : 0;
} -
When changing data. component must send
onChange
event, in which it transfers its new value and name (props.name
). When connecting component to FormBuilder, the function which is responsible for sending events to an application is available. It is calledprops.handleEvent
. Here's the code:changeValue(e, newValue) {
const props = this.props;
if (props.handleEvent) {
props.handleEvent({ syntheticEvent: e, key: props.name, eventName: "onChange", name: props.name, value: newValue });
}
}
Thus, this component will work with data binding.
Step 2. Creating form for editing component properties
As we have discussed before, form for editing component properties is drawn for each component in Form Builder. We need to create a component which will draw General tab in this form and return a list of events for the Events tab. Its code is also very simple, you can check it out below or on GitHub.
export class CounterEditControl extends BaseEditControl {
constructor(props) {
super(props);
}
getGeneralDescription() {
const data = this.props.data;
const handleChange = this.props.parent.handleChange.bind(this.props.parent);
return (<Form>
<Form.Group widths="equal">
<Form.Input name="key" label="Name" value={data.key} onChange={handleChange} />
<Form.Input name="label" label="Label" value={data.label} onChange={handleChange} />
</Form.Group>
<Form.Group widths="equal">
<Form.Input name="incButtonText" label="Inc button text" value={data.incButtonText} onChange={handleChange} />
<Form.Input name="decButtonText" label="Dec button text" value={data.decButtonText} onChange={handleChange} />
</Form.Group>
</Form>);
}
getEventsList() {
return ["onChange"];
}
}
Let us draw your attention to the following two aspects:
-
getGeneralDescription
function returns a form which will be displayed in General tab. You can edit the followingCounterControl
component properties in this form: Name, Label, Inc button text and Dec button text. Note thatname
property values for inputs in this form are similar withCounterControl
component props. I.e.:-
set
label
property with the following code:<Form.Input name="label" label="Label" value={data.label} onChange={handleChange} />
and then get its value in the
CounterControl
component:const label = this.props.label;
-
set
Inc button text
property with the following code:<Form.Input name="incButtonText" label="Inc button text" value={data.incButtonText} onChange={handleChange} />
and then get its value in the
CounterControl
component:<Button content={this.props.incButtonText} onClick={this.onInc} />
etc.
-
-
getEventsList
function returns a list of events that can send the component.
General tab appearance is shown on the picture below.
Step 3. Creating component renderer function
Renderer function is necessary for our Form Builder component to render component in form. Here it is on GitHub. This function solves to important problems:
- It adapts a lot of Form Builder parameters to props of our manually written component.
- Renders a component.
it is presumed that there is only one custom component renderer function in the application, and you just change it when adding new custom components.
export default function renderControls(parentComponent, control,
{
model, data, errors,
parentItem,
buildermode, children,
handleEvent, getAdditionalDataForControl,
readOnlyControls, readOnly,
disableRefs,
uploadUrl, downloadUrl, extendedData, controlsToReplace, needCheckReplace,
eventOnEdit, eventOnDelete, eventOnCopy
}) {
const props = {
key: model.key,
name: model.key,
"data-buildertype": model["data-buildertype"]
};
if (control === CounterControl) {
props.incButtonText = model.incButtonText;
props.decButtonText = model.decButtonText;
props.label = model.label;
props.handleEvent = handleEvent;
props.data = data;
return (<CounterControl {...props} />);
}
return null;
}
FormBuilder passes lots of parameters into this function. It is necessary because some of the DWKit components are rather complicated. But we will talk only about several renderer function parameters:
model
- component settings. This is what we set in component property edit form in Form Builder.control
- defines which component will be drawn.handleEvent
- DWKit function using which component sends events.data
- form data object.
Step 4. Creating a Custom React components list for Form builder
Now you need to create a list of your Custom components, so that Form Builder could draw it in the components panel. Here it is on GitHub. There can be only one such list and all further custom components should be added to it.
const customControls = [
{ key: "externalControls", title: 'User Controls', isseparate: true, defaultopen: true },
{
key: "counter",
title: 'Counter',
control: CounterControl,
editControl: CounterEditControl,
defaultValues: { label: "Counter", incButtonText: "Inc", decButtonText: "Dec" }
}
];
The first element in this list describes the section to which the components described below will be added. Second element binds Custom component - control: CounterControl
with form for editing properties - editControl: CounterEditControl
. Here you can also set default property values - defaultValues
.
Next we need to transmit info about our custom components into the FormBuilder and Form components.
Step 5. Connecting component directly to each component
We must transmit renderer function and custom components list into <DWKitFormBuilder/>
React component.
import renderControls from './controls/controlrenderer';
import customControls from './controls/controlslist';
...
render(
<DWKitFormBuilder
...
externalControlList={customControls}
externalControlRender={renderControls}
//externalControlsOnly
/>,
document.getElementById('content')
);
If you want Form Builder to display only external custom controls, set externalControlRender
property for <DWKitFormBuilder/>
component.
You can use the same properties for registering custom controls in DWKitForm and DWKitFormViewer.
Step 6. Connecting component to an application globally (optional)
We must transmit renderer function and Custom components list into each Form <DWKitForm/>
React component. However, we can't do it directly all the time, but via window
global object.
import renderControls from './controls/controlrenderer';
import customControls from './controls/controlslist';
...
window.DWKitFormSettings = {
externalControlList: customControls,
externalControlRender: renderControls
};
...
Now you can use your external components in FormBuilder and your application. See the result of our labor.