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.