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;
          }
        }
      });
    }
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容

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