Recently, a large number of H5 form pages have been developed using React Hooks in conjunction with the ZARM component library based on JS object configuration. Everyone knows the h5 form function does not form data collection, validation, submit, echo editor, usually arrangement is also down since last displayed as a line of a column, so the start considering encapsulate a configuration page generation scheme, there are multiple projects based on this pattern configuration development online, share ideas and implementation.
Those who are interested in publishing NPM packages can adopt or adapt their own component library zARM-form-Render
Usage scenarios
Any H5 page containing a form (using the ZARM library, or adapting your own library)
The target
- The code and implementation are simple and concise
- Based on the configuration
- New hands are quick, no learning costs
- The veteran is easy to expand and maintain
Before writing, I referred to some schemes on the market, most of which define a set of formats by defining JSON Schema, such as Ali’s form-render, define forms by JSON Schema, have visual editor configuration, export JSON files, dynamic rendering, and so on. Here is ali form-render as a representative, talk about some of its defects (advantages refer to the official website propaganda)
Form-render or thinking of some defects
- My target is H5. Form-remder is currently just a PC background form configuration solution, which provides ANTD and Fusion themes and does not support H5 by default
- Form-render supports extension components and maintains an internal mapping table mapping the mapping relationship between data types and components. For example, the mapping relationship between antD topic data types and ANTD components is as follows. If I have a h5 component library that is used by my own/third-party companies, You need to configure the extension yourself, which is more troublesome.
// For example, antD is mapped as follows:
export const mapping = {
default: 'input'.string: 'input'.array: 'list'.boolean: 'checkbox'.integer: 'number'.number: 'number'.object: 'map'.html: 'html'.'string:upload': 'upload'.'string:date': 'date'.'string:dateTime': 'date'.'string:time': 'date'.'string:textarea': 'textarea'.'string:color': 'color'.'string:image': 'input'.'string:email': 'input'.'string:url': 'url'.'range:date': 'dateRange'.'range:dateTime': 'dateRange'.'*? enum': 'select'.'array? enum': 'checkboxes'};Copy the code
- If you have used/developed json Schema-based tools such as form-render, there is a requirement that is difficult to handle, such as form field linkage. Form-render provides a limited number of linkage attributes. UI: options, UI: UI: disabled, hidden, and so on, so, in addition to the need to master the form – render defined data type, data type, subject component mapping, and component associated with various properties, but also memorize the linkage of the additional attributes, nothing more than more than learning a new programming complexity, Therefore, visual interface is needed to assist editing.
import React, { useState } from 'react';
import FormRender from 'form-render/lib/antd';
const schema = {
type: 'object'.properties: {
select: {
title: 'radio'.type: 'string'.enum: ['a'.'b'].enumNames: () = > ['Show input field'.'Hide input field'].'ui:disabled': (formData, rootValue) = > rootValue.input1.length > 5.'ui:widget': 'radio',},input1: {
title: 'Input field'.description: 'Try to enter more than 5 characters'.type: 'string'.'ui:hidden': (formData, rootValue) = > formData.select === 'b',}}};const Demo1 = () = > {
const [formData, setFormData] = useState({});
return (
<FormRender schema={schema} formData={formData} onChange={setFormData} />
);
};
export default Demo1;
Copy the code
- This configuration of JSON is good for non-developers to play out forms quickly, but not good for developers to develop extensions, such as if I want to put a text mix between two input fields.
Javascript object scheme
Therefore, aliform-render and other json configuration to achieve dynamic rendering schemes cannot meet the simple, fast, breakthrough, win-win code farming survival criteria, If json is replaced with javascript object, then the ability to configure and extend is not the same. Here we still borrow the general idea of form-render. But the type from string, number, Boolean, array, object, such as HTML data types for the Function, a Function is a React components, For example, antD-Mobile, ZARM Input component, because this article is based on ZARM, the following are based on ZARM expansion (antD-Mobile, VUE and other third-party mobile terminal/PC terminal is also applicable), such as the following configuration
import React, { useState, useEffect } from 'react';
import FormRenderer from 'form-render';
import { Input, Cell, Radio, Select, DateSelect, Button, Toast, Panel } from 'zarm';
export default function App() {
const [data, setData] = useState({});
const layoutData = [
{
type: Input,
label: 'Name of insured'.placeholder: 'Please fill in'.name: 'name'}, {type: Radio.Group,
label: 'gender'.name: 'gender'.elProps: {
type: 'button'.ghost: true,},items: [{label: 'male'.value: 'male' },
{ label: 'woman'.value: 'female'},],}, {render() {
if(! data.gender)return null;
return <Cell title="Are you" description={data.gender= = ='male'? 'the boys':'girls'} ></Cell>; }, {},type: Select,
label: 'Favorite fruit'.name: 'favfood'.elProps: {
dataSource: [{label: 'apple'.value: 'apple' },
{ label: 'banana'.value: 'banana'},],},}, {type: DateSelect,
label: Date of birth.title: 'Date of birth of insured'.placeholder: 'Please select'.name: 'birthday'.min: '1900-01-01'}, {type: Input,
label: 'Mobile phone Number'.placeholder: 'Please fill in'.name: 'mobile'}, {render() {
return <div style={{ margin: '30px 6px',}}></div>; }, {},render() {
return (
<Panel title="What you typed in.">
<div style={{ margin: '10px 6px' }}>{JSON.stringify(data)}</div>
</Panel>); }},];return (
<div>
<FormRenderer layoutData={layoutData} data={data} setData={setData} />
<Button block theme="primary" onClick={()= >Toast. The show (JSON. Stringify (data))} > sure</Button>
</div>
);
}
Copy the code
A form is defined by an array. The object type is the component to render, the name is the key to collect the data, and the other properties define the props for the component. The other properties define the props for the component. And then there’s nothing else, it’s all React that you’re familiar with, and finally we can define our own FormRender that takes a one-dimensional array for listing items from the top down, a data for collecting data, A setData for data updates (from React Hooks useState), as simple as that, the source code is as follows
import React from 'react';
import { Cell, Radio, DateSelect, Select } from 'zarm';
// In cases where you cannot configure a custom component, a component that needs to be displayed based on conditions, etc., use the render method,
// getJSON() returns json dynamically
// render() custom render
export default function FormRenderer({ layoutData, data, setData }) {
const onFiledChange = (name, value) = > {
let v = value;
// for Select ctrl
if (Array.isArray(value)) {
v = value.map((item) = > item.value)[0]; } setData({ ... data, [name]: v }); };const onChangeFactory = (name) = > (value) = > onFiledChange(name, value);
return (
<div className="renderer">{layoutData.map((item, idx) => { if (typeof item.getJSON === 'function') { item = item.getJSON(); } if (typeof item ! == 'object' || ! item) return null; const { name, type, description, items, elProps = {}, cellProps = {}, render, ... props } = item; if (typeof render === 'function') { return render(); } let children = []; if (Array.isArray(items) && type === Radio.Group) { children = items.map((it, idx1) => (<Radio value={it.value} key={idx1}>
{it.label}
</Radio>
));
}
props.value = data[name];
props.onChange = onChangeFactory(name);
if (type === Select) {
props.dataSource = items;
}
if (type === DateSelect || type === Select) {
props.onOk = props.onChange;
delete props.onChange;
props.onChange = elProps.onChange;
}
return (
<Cell key={idx} title={item.label} description={description} {. cellProps} name={name}>{React.createElement(type, { ... props, ... elProps }, ... children)}</Cell>
);
})}
</div>
);
}
Copy the code
Configuration Item Description
export type FormRenderProps = {
layoutData: Item[]; // Form layout configuration
data: Record<string, unknown>; // Data store,Item name as key, component value as value
setData: (p: unknown) = > void; // React-hooks, [data,setData]=useState({})
};
exporttype Item = { type? : React.ComponentType | string;// Component type, such as Input, etc
name: string; // Cell nameitems? :Array<unknown>; // zarm dataSourcedescription? : string;// Cell descriptionlabel? : string;// Cell titlerender? :() = >React.ReactNode; getJSON? :() = > Item | null; // Dynamically returns the Item configurationelProps? : Record<string, unknown>;// The component props configuration, for example, if type is Input, elProps is configured to InputcellProps? : Record<string, unknown>;// Cell props configuration
};
Copy the code
To view the demo, run the yarn start/NPM start command