巧妙使用Vue.extend繼承元件實現el-table雙擊可編輯(不使用v-if和v-else)

語言: CN / TW / HK

問題描述

有一個簡單的表格,產品要求實現雙擊可編輯

看了一下網上的帖子,大多數都是搞兩部分dom,一塊是輸入框,用於編輯狀態填寫另一塊是普通標籤,用於在不編輯顯示狀態下呈現單元格文字內容。再加上一個flag標識搭配v-if和v-else去控制編輯狀態、還是顯示狀態。大致程式碼如下:

```js <el-table-column align="center" label="姓名"

<template slot-scope="scope">
  <!--isClick就是標識狀態,狀態處於編輯時候,顯示輸入框,狀態屬於呈現狀態就顯示文字內容-->
  <el-input v-if="scope.row.isClick" v-model="scope.row.name"  @blur="blurFn(scope.row)"></el-input>
  <span @click="clickCell(scope.row)" v-else>{{scope.row.name}}</span>
</template>

```

這種方式有其適用場景,但是得每個el-table-column列中都加上el-input和span以及v-if和v-else。我們嘗試一下動態新增el-input,就是點選那個單元格,給那個單元格新增el-input讓其處於可編輯狀態,然後適時移除即可。這樣的話,很多列的時候,就不用加很多個v-if和v-else啦。我們先看一下效果圖

效果圖

1.gif

程式碼思路

  • 第1步:給el-table繫結雙擊事件 @cell-dblclick='dblclick',再雙擊事件的回撥函式中,可以得知點選的是哪一行、那一列、那個單元格dom,以及點選事件。dblclick(row, column, cell, event) {...},這個是餓了麼官方提供的,沒啥好說的
  • 第2步:重點來嘍
  • 第2.1步:單元格雙擊事件以後,我們首先建立一個el-input標籤,然後把點選的這個單元格的值,作為引數props讓這個el-input接收,這樣的話el-input就會顯示這個單元格的值了,就可以編輯了。問題一:如何建立一個el-input標籤? ,客官稍等,下方會解答
  • 第2.2步:把建立好的el-input標籤替換掉原來的單元格span標籤,這樣的話,就可以看到單元格變成了可輸入的輸入框了。問題二:如何把新建立的el-input標籤,替換原有的span標籤 ,客官稍等,下方會解答
  • 第2.3步,當用戶編輯完了點選別處時候,即輸入框失去焦點的時候,再把el-input輸入框標籤移除掉,恢復預設的span標籤(當然失去焦點的時候,就要發請求修改資料了)問題三:如何移除el-input標籤,並恢復原有的span標籤,客官稍等,下方會解答
  • 這樣的話,每次雙擊搞一個input標籤用於修改,每次改完了失去焦點,就恢復預設單元格展示狀態了,功能就實現了

程式碼思路中的三個問題解答

問題一:如何建立一個el-input標籤?

我們知道,如果是建立原生的input標籤並指定一個值,比較簡單,直接:

js let input = document.createElement('input') // 建立一個input標籤 input.value = '孫悟空' // 給input標籤賦值 document.body.appendChild(input) // 把input標籤追加到文件body中 不過el-input標籤不能通過上述方式建立,因為document.createElement()方法雖然可以創建出來el-input標籤,但是dom並不認識這個el-input標籤,所以頁面沒有變化。畢竟餓了麼的el-input也是把input標籤做一個二次封裝的

所以,這裡我們可以使用Vue.extend()方法去繼承一個元件並暴露出去,而繼承的這個元件中又有一個input標籤,所以那個需要使用,那裡就可以引入並new出來一個el-input了。關於Vue.extend()的定義啥的,這裡不贅述,詳情看官方文件。筆者之前也寫過一篇Vue.extend文章,傳送門:http://juejin.cn/post/7021724333391216677

首先搞一個.vue檔案,用於繼承

```js // input.vue檔案

props: { cellValue: { type: String | Number, default: "", }, } ```

然後定義一個data.js檔案,繼承input.vue檔案,並暴露

js // data.js import Vue from "vue"; import definedInput from "./input.vue"; // vue繼承這個input元件,就相當於一個構造函數了 const inputC = Vue.extend(definedInput); // 暴露出去,哪裡需要哪裡引入 export default { inputC, }

頁面中引入並使用

```js // page.vue import extendComponents from "./threeC/data"; // 1. 引入

new extendComponents.inputC({ // 2. 例項化 propsData: { // 使用propsData物件傳遞引數,子元件在props中可以接收到 cellValue: cellValue, // 傳遞單元格的值 }, }).$mount(cell.children[0]);// 3. 掛載 ```

propsData物件用於給繼承的元件傳遞引數,也可以傳遞一個函式,從而繼承元件通過這個函式通知外部使用元件,詳情見後續完整程式碼

問題二三:el-input標籤和span標籤的來回替換恢復

使用$mount方法去做來回替換,$mount可以把一個子dom元素追加到父dom元素內部,相當於appendChild

然後這裡需要有一個替換的時機,就是例項化的元件中的el-input失去焦點的時候,去通知外部使用的元件,所以可以在外部使用是,在propsData中傳遞一個函式到繼承的元件,如:

```js // 外部元件傳遞 new extendComponents.inputC({ propsData: { cellValue: cellValue, // 傳遞單元格的值 saveRowData: this.saveRowData, // 傳遞迴調函式用於通知,繼承元件中可以觸發之 }, }).$mount(cell.children[0]);

saveRowData(params){ console.log('收到繼承元件訊息通知啦引數為:',params) } ```

```js // 內部元件失去焦點時候通知 <el-input ref="elInputRef" size="mini" v-model.trim="cellValue" @blur="blurFn"

props: { cellValue: { type: String | Number, default: "", }, saveRowData: Function, // 外部,傳遞進來一個函式,當這個el-input失去焦點的時候,通過此函式通知外部 }

blurFn() { // 失去焦點,再丟擲去,通知外部 this.saveRowData({ cellValue: this.cellValue, // 其他引數 }); }, ```

所以當內層失去焦點的時候,就可以通知外層去做一個替換了,就是把單元格dom重新做一個$mount掛載,就把el-input替換成了span了,為了進一步理解,這裡的span我們也可以使用繼承的方式,是new例項化使用,詳情見下方完整程式碼

完整程式碼

目錄結構

js threeC -- data.js -- input.vue -- span.vue three.vue

用於繼承的el-input元件

input.vue

```js

```

用於繼承的span元件

span.vue ```js

```

統一繼承並暴露data.js檔案

```js import Vue from "vue"; import definedInput from "./input.vue"; import definedSpan from "./span.vue";

const inputC = Vue.extend(definedInput); const spanC = Vue.extend(definedSpan);

export default { inputC, spanC, } ```

使用繼承的three.vue元件

```js

```

總結

使用Vue.extend()方法,可以繼承一些元件,甚至繼承一些複雜的元件,在實際業務場景中會有巧妙的使用。具體業務場景具體分析。

此外,上述程式碼中是el-input的繼承,其實,我們也可以做el-select的繼承,思路和上方類似,這樣就可以在表格中雙擊單元格,選擇並更改對應的下拉框更改el-table的單元值了,比如如果有性別這一列,那是下拉框的形式的。道友們可以按照這個思路發散哦...

好記性不如爛筆頭,記錄一下吧 ^_^