vue動態渲染表單配置項

vue動態渲染表單配置項

在項目中,我們經常會用到各種表單,但是數量一般都不多,那當我們編寫大量表單時怎么辦呢,難道我們還要一個標簽一個標簽的寫嗎?如果各個表單之間又出現了各種級聯關系各種判斷呢?所以這時候就出現了另外一種方式,我們可以通過對象的方式編寫表單項來替換element本來的標簽寫法,以使我們能夠方便清晰的最大化的控制每個表單。

當我們編寫一個普通的input表單時,我們一般會直接在template里這樣寫:

<el-input v-model="inputValue" placeholder="請輸入"></el-input>

用了表單配置項后,我們可以這樣在data里寫:

{
  type: 'input',
  key: 'inputValue',
  label: 'input框:',
  placeholder: '請輸入'
}

我們先不討論怎么就可以這樣寫了,我們先來明白為什么要這樣用

  • 可以通過this.XXX來改變某個表單項
  • 不用再在標簽里加各種判斷
  • 標簽寫法太死板
  • render寫法,更貼近vue底層

如何配置

input舉栗,最后會附上其他的表單項

// 準備一個文件---itemRenders.js
export default {
  name: 'ItemRender',
  props: {
    // 類型,input or select or ...
    type: String,
    // 主要配置項
    config: Object
  },
  // 渲染函數
  render: function (h) {
    switch (this.type) {
      case 'input':
        return inputItem(h, this.config);
      default:
        return '';
    }
  }
};  

function inputItem(h, config) {
  let {
    key, // 綁定的值
    props, // 屬性, 如disable等
    listeners, // 事件,如onblur等
    placeholder,
    areaProps // 額外屬性如type等
  } = config;
  let on = listeners || {};
  let pps = props || {};
  let area = areaProps || {};
  return h('el-input', {
    props: {
      ...pps,
      value: key,
      disabled: pps.disabled
    },
    attrs: {
      placeholder: placeholder || pps.placeholder || '請輸入',
      ...area
    },
    on: {
      ...on,
      // 實現雙向綁定
      input: (val) => {
        key = val;
      }
    }
  });
}

如何使用

在.vue文件中使用itemRenders.js

<template>
    <div>
        <item-render
          :type="item.type"
          :config="{
            placeholder: item.placeholder || '',
            ...item.config,
            key: item.key
          }"
        >
        </item-render>
    </div>
</template>

<script>
    import ItemRender from './itemRenders';
    export default {
        components: { 'item-render': ItemRender },
        data() {
            return {
                item: {
                    type: 'input',
                     key: 'inputValue',
                     label: 'input框:',
                     placeholder: '請輸入',
                     config: {
                        props: {
                          disabled: true
                        },
                        listeners: {
                          blur: _this.blur1
                        }
                     }
                }
            }
        }
    }
</script>

這只是渲染一個input的寫法,但既然會用到這種寫法,那就代表要渲染的表單數量可能有點大,像我們項目中有些頁面新增時表單項多達100+,所以,如果頁面只用到個位數的表單的情況,這里還是建議親親直接使用ele提供的標簽寫法。

渲染大量表單

那渲染大量表單時要怎么配置呢?

其實也很簡單,套個form就行了(主要以提交form表單為主)

我們可以先來準備個form表單組件,用的時候只需引入該組件即可

// formRender.vue
<template>
  <el-form
    ref="formRender"
    :model="model"
    :rules="rules"
  >
    <div class="block">
      <el-form-item
        v-for="item in items"
        :key="item.key"
        :label="item.label"
        :prop="item.key"
      >
        <item-render
          :type="item.type"
          :config="{
            placeholder: item.placeholder || '',
            ...item.config,
            model,
            key: item.key
          }"
        >
        </item-render>
      </el-form-item>
    </div>
  </el-form>
</template>
<script>
    import ItemRender from './itemRenders';
    export default {
        components: { 'item-render': ItemRender },
        props: {
            model: {
              type: Object,
              default: () => {}
            },
            // 規則
            rules: {
              type: Object,
              default: () => {}
            },
            items: {
              type: Array,
              default: () => []
            }
        },
    }
</script>

項目中使用

<template>
    <FormRender
        ref="formRender"
        :model="form"
        :rules="basicRules"
        :items="items1"
      ></FormRender>
</template>
<script>
    import FormRender from './formRender';
    export default {
        components: {FormRender},
        data() {
            return {
                form: {
                    formItem1: '',
                    formItem2: [],
                    formItem3: ''
                },
                items1: [
                    // 輸入框
                    {
                        type: 'input',
                        key: 'formItem1',
                        label: 'formItem1:',
                        placeholder: '請輸入',
                    },
                    // 下拉框
                    {
                        type: 'select',
                        key: 'formItem2',
                        label: 'formItem2:',
                        placeholder: '請選擇',
                        config: {
                            props: {
                              filterable: true,
                              clearable: true,
                              disabled: false
                            },
                            options: () => _this.formItem2s,
                            optionConfig: {
                              label: 'name',
                              value: 'code'
                            },
                            listeners: {
                              change: _this.handleChange
                            }
                      }
                    }
                ],
                basicRules: {
                    formItem1: [ { required: true, trigger: 'blur' }]
                },
                formItem2s: []
            }
         }
    }
</script>

其余表單配置項

這里只提供部分常用的給大家做參考,大家也可以從別的角度去考慮,適合我們的并不一定適合所有人

  • 下拉選擇框

    function selectItem(h, config) {
    let {
      model,
      key,
      props,
      listeners,
      options,
      optionConfig,
      placeholder,
      syncConfig
    } = config;
    
    let opl = 'label';
    let opv = 'value';
    if (optionConfig) {
      if (optionConfig.label) {
        opl = optionConfig.label;
      }
      if (optionConfig.value) {
        opv = optionConfig.value;
      }
    }
    
    let opts = options();
    let ops = [];
    if (opts) {
      ops = opts.map((option) => {
        return h('el-option', {
          key: option[opv],
          props: {
            label: option[opl] || option['text'],
            value: option[opv],
            disabled: option.disabled
          }
        });
      });
    }
    
    let on = listeners || {};
    let pps = props || {};
    if (syncConfig) {
      pps = {
        ...pps,
        ...syncConfig()
      };
    }
    
    return h(
      'el-select', {
        props: {
          placeholder,
          ...pps,
          value: model[key]
        },
        on: {
          ...on,
          change: (val) => {
            model[key] = val;
            if (on.change) {
              on.change(val);
            }
          }
        }
      },
      ops
    );
    }
    
  • 日期選擇器

    function dateItem(h, config) {
      let {
        model,
        key,
        props,
        listeners,
        placeholder,
        type
      } = config;
      let pps = props || {};
      let on = listeners || {};
      return h('el-date-picker', {
        props: {
          ...pps,
          value: model[key]
        },
        attrs: {
          placeholder: placeholder || pps.placeholder || '請輸入',
          type: type || 'date'
        },
        on: {
          ...on,
          input: (val) => {
            model[key] = val;
          }
        }
      });
    }
    
  • 計數器

    function inputNumberItem(h, config) {
      let {
        model,
        key,
        props
      } = config;
      let pps = props || {};
      return h('el-input-number', {
        props: {
          ...pps,
          value: model[key]
        },
        on: {
          change: (val) => {
            model[key] = val;
          }
        }
      });
    }
    
  • 上傳

    function uploadItem(h, config) {
      let {
        model,
        key,
        props,
        btnProps,
        listeners,
        iconProps
      } = config;
      let pps = props || {};
      let btnProp = btnProps || {};
      let iconProp = iconProps || {};
      let on = listeners || {};
      return h(
        'el-upload', {
          props: {
            ...pps,
            'file-list': model[key]
          },
          class: {
            upload_demo: true
          },
          on: {
            ...on
          }
        },
        [
          h(
            'el-button', {
              props: {
                ...btnProp,
                size: btnProp.size || 'small'
              }
            },
            [
              h('svg-icon', {
                style: iconProp.styles,
                props: {
                  iconClass: iconProp.iconClass
                }
              }),
              '上傳'
            ]
          )
        ]
      );
    }
    
  • 級聯選擇器

    function cascaderItem(h, config) {
      let {
        model,
        key,
        props,
        listeners,
        options,
        optionProps
      } = config;
      let ops = options();
      let on = listeners || {};
      let pps = props || {};
      return h('el-cascader', {
        props: {
          value: model[key],
          ...pps,
          options: ops,
          props: optionProps
        },
        on: {
          change: (val) => {
            model[key] = val;
            if (on.change) {
              on.change(val);
            }
          }
        }
      });
    }
    
  • 時間選擇器

    function timePicker(h, config) {
      let {
        model,
        key,
        props,
        listeners,
        placeholder,
        type,
        syncConfig
      } = config;
      let pps = props || {};
      if (syncConfig) {
        pps = {
          ...pps,
          ...syncConfig()
        };
      }
      let on = listeners || {};
      return h('el-time-picker', {
        props: {
          ...pps,
          value: model[key]
        },
        attrs: {
          placeholder: placeholder || pps.placeholder || '請輸入',
          type: type || 'date'
        },
        on: {
          ...on,
          input: (val) => {
            model[key] = val;
          }
        }
      });
    }
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom閱讀 2,719評論 0 3
  • Vue 實例 屬性和方法 每個 Vue 實例都會代理其 data 對象里所有的屬性:var data = { a:...
    云之外閱讀 2,241評論 0 6
  • 以下內容是我在學習和研究Vue時,對Vue的特性、重點和注意事項的提取、精練和總結,可以做為Vue特性的字典; 1...
    科研者閱讀 14,137評論 3 24
  • "use strict";function _classCallCheck(e,t){if(!(e instanc...
    久些閱讀 2,046評論 0 2
  • 看過很多相似的場景,一個孩子吃著飯突然從碗底翻出一個荷包蛋或者一個黃燦燦的太陽蛋,母親在一旁慈愛地看著他,“海底撈...
    飛飛的碎碎念閱讀 1,216評論 28 36