The problem background
When we first used element-UI, new forms were dead for each form-item, and the contents of the form-item were dead, as in the following example
<el-form label-width="80px" :model="formLabelAlign">
<el-form-item label="Name">
<el-input v-model="formLabelAlign.name"></el-input>
</el-form-item>
<el-form-item label="Active area">
<el-input v-model="formLabelAlign.region"></el-input>
</el-form-item>
<el-form-item label="Form of activity">
<el-input v-model="formLabelAlign.type"></el-input>
</el-form-item>. </el-form>Copy the code
The problem with this is that every time you add a new form-item, you have to manually copy it, and then change the V-model of the label, prop, and el-Input bindings. The more for-items, the larger the vUE, the more bloated the file
First generation solution
In the above example, we found that the el-form-item can be generated through the V-for loop, because the structure and contents of each form-item are the same, so we naturally thought of the following solution:
<template>
<el-form :model="formLabelAlign">
<el-form-item
v-for="item in FORM_ITEMS"
:key="item.prop"
:label="item.label"
:prop="item.prop"
:label-width="item.lableWidth"
>
<el-input v-model="formLabelAlign[prop]"></el-input>
</el-form-item>
</el-form>
</template>
<script>
export default {
name: 'CommonForm'.props: {
formLabelAlign: {
type: Object.require: true.defalut: () = >({})}},data() {
return {
FORM_ITEMS: [{prop: 'name'.label: 'name'.lableWidth: 100 },
{prop: 'region'.label: 'Active area'.lableWidth: 100 },
{prop: 'type'.label: 'Form of activity'.lableWidth: 100},]}},}</script>
Copy the code
Each item defines the desired attributes label, prop, etc. We loop through the FORM_ITEMS declaration, so we don’t need to modify our template, we just need to add a new item to the FORM_ITEMS
More scenes
The above example applies only to a single scene, which limits the content of our form-item to input only. The problem is that our daily business scene form contains many types, such as drop-down box, multi-check box, text field, single check box, multi-check box, upload and so on. Therefore, the above encapsulation is obviously not applicable
Solutions for different types of form-item content
For more of the above scenarios, it’s natural to think of enumerations to implement the contents of different types of form-items, like the following:
Common Forms second edition
<template>
<el-form :model="formLabelAlign">
<el-form-item
v-for="item in FORM_ITEMS"
:key="item.prop"
:label="item.label"
:prop="item.prop"
>
<el-input
v-if="item.el === 'input'"
:placeholder="item.placeholder"
v-model="formLabelAlign[item.prop]"
></el-input>
<el-input
v-if="item.el === 'textarea'"
type="textarea"
:rows="item.row || 5"
:placeholder="item.placeholder"
v-model="formLabelAlign[item.prop]"
>
</el-input>
<el-switch
v-if="item.el === 'switch'"
v-model="formLabelAlign[item.prop]"
></el-switch>
<el-slider
v-if="item.el === 'slider'"
v-model="formLabelAlign[item.prop]"
></el-slider>
<el-select v-if="item.el === 'select'">
<el-option
v-for="option in item.options"
:key="option.label"
:label="option.label"
:value="option.value"
></el-option>
</el-select>
<el-radio-group
v-if="item.el === 'radio'"
v-model="formLabelAlign[item.prop]"
>
<el-radio
v-for="radio in item.radios"
:key="radio.label"
:label="radio.label"
v-mode="radio.value"
>{{ radio.label }}</el-radio
>
</el-radio-group>. Etc etc.</el-form-item>
</el-form>
</template>
<script>
export default {
name: "CommonForm".props: {
formLabelAlign: {
type: Object.require: true.defalut: () = >({})}},data() {
return {
FORM_ITEMS: [{prop: "name".label: "Name".lableWidth: 100.el: "input" },
{ prop: "region".label: "Active area".lableWidth: 100.el: "input" },
{ prop: "type".label: "Form of activity".lableWidth: 100.el: "input" },
{
prop: "info".label: "Information introduction".lableWidth: 100.el: "textarea".row: 3}, {prop: "switch".label: "Enabled or not".lableWidth: 100.el: "switch" },
{ prop: "percent".label: "Scale".lableWidth: 100.el: "slider" },
{
prop: "types".label: "Type selection".lableWidth: 100.el: "radio".radios: [{value: 0.label: "yes" },
{ value: 1.label: "no"},],}, {prop: "city".label: "City Choice".lableWidth: 100.el: "radio".radios: [{value: 0.label: "Beijing" },
{ value: 1.label: "Shenzhen" },
{ value: 2.label: "Zhuhai"},],},],}; }};</script>
Copy the code
The simple idea is that we enumerate the various possible elment- UI form elements, using the el attribute to determine what type of tag element we want to render this time
Remaining issues:
- In real development, it is not necessary that each form-item contains only an input or checkbox. It may be a combination of input and button. It may also contain another form nested directly inside the form-item
For example:
<el-form-item label="Activity Name">
<el-input v-model="form.name"></el-input>
<el-radio-group v-model="radio">
<el-radio :label="3">Alternatives to the</el-radio>
<el-radio :label="6">Alternatives to the</el-radio>
<el-radio :label="9">Alternatives to the</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="Active area">
<el-form :model="formData">.</el-form>
</el-form-item>
Copy the code
Third edition Common forms components
We passed prop to slot as the slot name for a form item that didn’t fit, so WE changed the public form to look like this:
<template>
<el-form :model="formLabelAlign">
<el-form-item
v-for="item in FORM_ITEMS"
:key="item.prop"
:label="item.label"
:prop="item.prop"
>
<el-input
v-if="item.el === 'input'"
:placeholder="item.placeholder"
v-model="formLabelAlign[item.prop]"
></el-input>. The content thumbnail above<slot :slot="item.prop" />
</el-form-item>
</el-form>
</template>
<script>
export default {
name: "CommonForm".props: {
formLabelAlign: {
type: Object.require: true.defalut: () = >({})}},data() {
return {
FORM_ITEMS: [{prop: "name".label: "Name".lableWidth: 100.el: "input" },
{ prop: "alias".label: "Flower".lableWidth: 100.el: "custom"},]}; }};</script>
Copy the code
How do I use our third edition forms component
The FORM_ITEMS above is written in data for demonstration purposes, so we actually need to pass it in through props
<template> <el-form :model="formLabelAlign"> <el-form-item v-for="item in formItems" :key="item.prop" :label="item.label" :prop="item.prop" > <el-input v-if="item.el === 'input'" :placeholder="item.placeholder" v-model="formLabelAlign[item.prop]" ></el-input> ... <slot :slot="item.prop" /> </el-form-item> </el-form> </template> <script> export default {name: "CommonForm", props: { formLabelAlign: { type: Object, require: true, defalut: () => ({}), }, formItems: { type: Array, require: true, }, }, }; </script>Copy the code
The specific use
<template >
<CommonForm :form-label-align="formData" :form-items="FORM_ITEMS">
<! Since we should have registered this slot in the public component, the alias does not fit our format, so we can customize it here.
<template slot="alias">
<h3>{{formData.alias}}</h3>
<el-input v-model="formData.alias" />
</template>
<CommonForm>
</template>
<script>
import CommonForm from "./common-form";
export default {
data() {
return {
formData: {
name: "dogeWin".alias: "Dougwin The Dog wins.",},FORM_ITEMS: [{prop: "name".label: "Name".lableWidth: 100.el: "input" },
{ prop: "alias".label: "Flower".lableWidth: 100.el: "custom" },
{
prop: "info".label: "Information introduction".lableWidth: 100.el: "textarea".row: 3}, {prop: "switch".label: "Enabled or not".lableWidth: 100.el: "switch" },
{ prop: "percent".label: "Scale".lableWidth: 100.el: "slider"},]}; },components: {
CommonForm,
},
};
</script>
Copy the code
Fourth edition Common Forms components
There is another way to customize the incoming content, functional Componet, here directly post the implementation, interested can go to study
<template>
<el-form :model="formLabelAlign">
<el-form-item
v-for="item in FORM_ITEMS"
:key="item.prop"
:label="item.label"
:prop="item.prop"
>
<el-input
v-if="item.el === 'input'"
:placeholder="item.placeholder"
v-model="formLabelAlign[item.prop]"
></el-input>. The content thumbnail above<! If the item we pass has a render method, we'll use our functional component to render it. Bind params to the formData we pass.
<functionalComponent v-if="item.render" :params="formLabelAlign" />
<slot :slot="item.prop" />
</el-form-item>
</el-form>
</template>
<script>
// This function component is very simple, just pass the render as its own render, params is the formData we pass
const functionalComponent = {
functional: true.props: {
render: Function.params: Object,},render(h, ctx) {
constparams = { ... ctx.props.params };returnctx.props.render.call(ctx, h, params); }}export default {
name: "CommonForm".props: {
formLabelAlign: {
type: Object.require: true.defalut: () = >({})},formItems: {
type: Array.require: true,}},components: {
functionalComponent
}
};
</script>
Copy the code
Use of the fourth edition common form component
<template >
<CommonForm :form-data="formData" :form-items="FORM_ITEMS" /></template> <script> import CommonForm from "./common-form"; Export default {data() {return {formData: {name: "dogeWin", alias: "dogeWin",}, FORM_ITEMS: [{prop: "Name ", label: "name", lableWidth: 100, el: "input"}, {prop: "alias", label: "name", lableWidth: 100, el: "input"}, {prop: "alias", label: "name", lableWidth: 100, el: "input"}, {prop: "alias", label:" lableWidth ", lableWidth: 100, // JSX syntax, JSX plugin render(h, params) { return ( <div> <h3>{params.alias}</h3> <el-input value={params.alias} onInput={e => params.alias = e} /> <div> }},],}; }, components: { CommonForm, }, }; </script>Copy the code
conclusion
Through our incremental learning, a useful common form component was created, and I was straight up a good guy.