如何巧用Vue.extend繼承組件實(shí)現(xiàn)el-table雙擊可編輯(不使用v-if、v-else)
問題描述
有一個(gè)簡(jiǎn)單的表格,產(chǎn)品要求實(shí)現(xiàn)雙擊可編輯
看了一下網(wǎng)上的帖子,大多數(shù)都是搞兩部分dom,一塊是輸入框,用于編輯狀態(tài)填寫;另一塊是普通標(biāo)簽,用于在不編輯顯示狀態(tài)下呈現(xiàn)單元格文字內(nèi)容。再加上一個(gè)flag標(biāo)識(shí)搭配v-if和v-else去控制編輯狀態(tài)、還是顯示狀態(tài)。大致代碼如下:
<el-table-column
align="center"
label="姓名"
>
<template slot-scope="scope">
<!--isClick就是標(biāo)識(shí)狀態(tài),狀態(tài)處于編輯時(shí)候,顯示輸入框,狀態(tài)屬于呈現(xiàn)狀態(tài)就顯示文本內(nèi)容-->
<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>這種方式有其適用場(chǎng)景,但是得每個(gè)el-table-column列中都加上el-input和span以及v-if和v-else。我們嘗試一下動(dòng)態(tài)添加el-input,就是點(diǎn)擊那個(gè)單元格,給那個(gè)單元格添加el-input讓其處于可編輯狀態(tài),然后適時(shí)移除即可。這樣的話,很多列的時(shí)候,就不用加很多個(gè)v-if和v-else啦。我們先看一下效果圖
效果圖

代碼思路
- 第1步:給el-table綁定雙擊事件
@cell-dblclick='dblclick',再雙擊事件的回調(diào)函數(shù)中,可以得知點(diǎn)擊的是哪一行、那一列、那個(gè)單元格dom,以及點(diǎn)擊事件。dblclick(row, column, cell, event) {...},這個(gè)是餓了么官方提供的,沒啥好說的 - 第2步:重點(diǎn)來嘍
- 第2.1步:?jiǎn)卧耠p擊事件以后,我們首先創(chuàng)建一個(gè)el-input標(biāo)簽,然后把點(diǎn)擊的這個(gè)單元格的值,作為參數(shù)props讓這個(gè)el-input接收,這樣的話el-input就會(huì)顯示這個(gè)單元格的值了,就可以編輯了。問題一:如何創(chuàng)建一個(gè)el-input標(biāo)簽? ,客官稍等,下方會(huì)解答
- 第2.2步:把創(chuàng)建好的el-input標(biāo)簽替換掉原來的單元格span標(biāo)簽,這樣的話,就可以看到單元格變成了可輸入的輸入框了。問題二:如何把新創(chuàng)建的el-input標(biāo)簽,替換原有的span標(biāo)簽 ,客官稍等,下方會(huì)解答
- 第2.3步,當(dāng)用戶編輯完了點(diǎn)擊別處時(shí)候,即輸入框失去焦點(diǎn)的時(shí)候,再把el-input輸入框標(biāo)簽移除掉,恢復(fù)默認(rèn)的span標(biāo)簽(當(dāng)然失去焦點(diǎn)的時(shí)候,就要發(fā)請(qǐng)求修改數(shù)據(jù)了)問題三:如何移除el-input標(biāo)簽,并恢復(fù)原有的span標(biāo)簽,客官稍等,下方會(huì)解答
- 這樣的話,每次雙擊搞一個(gè)input標(biāo)簽用于修改,每次改完了失去焦點(diǎn),就恢復(fù)默認(rèn)單元格展示狀態(tài)了,功能就實(shí)現(xiàn)了
代碼思路中的三個(gè)問題解答
問題一:如何創(chuàng)建一個(gè)el-input標(biāo)簽?
我們知道,如果是創(chuàng)建原生的input標(biāo)簽并指定一個(gè)值,比較簡(jiǎn)單,直接:
let input = document.createElement('input') // 創(chuàng)建一個(gè)input標(biāo)簽
input.value = '孫悟空' // 給input標(biāo)簽賦值
document.body.appendChild(input) // 把input標(biāo)簽追加到文檔body中不過el-input標(biāo)簽不能通過上述方式創(chuàng)建,因?yàn)閐ocument.createElement()方法雖然可以創(chuàng)建出來el-input標(biāo)簽,但是dom并不認(rèn)識(shí)這個(gè)el-input標(biāo)簽,所以頁面沒有變化。畢竟餓了么的el-input也是把input標(biāo)簽做一個(gè)二次封裝的
所以,這里我們可以使用Vue.extend()方法去繼承一個(gè)組件并暴露出去,而繼承的這個(gè)組件中又有一個(gè)input標(biāo)簽,所以那個(gè)需要使用,那里就可以引入并new出來一個(gè)el-input了。關(guān)于Vue.extend()的定義啥的,這里不贅述,詳情看官方文檔。筆者之前也寫過一篇Vue.extend文章,傳送門:http://www.dbjr.com.cn/article/251484.htm
首先搞一個(gè).vue文件,用于繼承
// input.vue文件
<template>
<div class="cell">
<el-input
ref="elInputRef"
size="mini"
v-model.trim="cellValue"
></el-input>
</div>
</template>
props: {
cellValue: {
type: String | Number,
default: "",
},
}然后定義一個(gè)data.js文件,繼承input.vue文件,并暴露
// data.js
import Vue from "vue";
import definedInput from "./input.vue";
// vue繼承這個(gè)input組件,就相當(dāng)于一個(gè)構(gòu)造函數(shù)了
const inputC = Vue.extend(definedInput);
// 暴露出去,哪里需要哪里引入
export default {
inputC,
}頁面中引入并使用
// page.vue
import extendComponents from "./threeC/data"; // 1. 引入
new extendComponents.inputC({ // 2. 實(shí)例化
propsData: {
// 使用propsData對(duì)象傳遞參數(shù),子組件在props中可以接收到
cellValue: cellValue, // 傳遞單元格的值
},
}).$mount(cell.children[0]);// 3. 掛載propsData對(duì)象用于給繼承的組件傳遞參數(shù),也可以傳遞一個(gè)函數(shù),從而繼承組件通過這個(gè)函數(shù)通知外部使用組件,詳情見后續(xù)完整代碼
問題二三:el-input標(biāo)簽和span標(biāo)簽的來回替換恢復(fù)
使用$mount方法去做來回替換,$mount可以把一個(gè)子dom元素追加到父dom元素內(nèi)部,相當(dāng)于appendChild
然后這里需要有一個(gè)替換的時(shí)機(jī),就是實(shí)例化的組件中的el-input失去焦點(diǎn)的時(shí)候,去通知外部使用的組件,所以可以在外部使用是,在propsData中傳遞一個(gè)函數(shù)到繼承的組件,如:
// 外部組件傳遞
new extendComponents.inputC({
propsData: {
cellValue: cellValue, // 傳遞單元格的值
saveRowData: this.saveRowData, // 傳遞回調(diào)函數(shù)用于通知,繼承組件中可以觸發(fā)之
},
}).$mount(cell.children[0]);
saveRowData(params){
console.log('收到繼承組件消息通知啦參數(shù)為:',params)
}// 內(nèi)部組件失去焦點(diǎn)時(shí)候通知
<el-input
ref="elInputRef"
size="mini"
v-model.trim="cellValue"
@blur="blurFn"
></el-input>
props: {
cellValue: {
type: String | Number,
default: "",
},
saveRowData: Function, // 外部,傳遞進(jìn)來一個(gè)函數(shù),當(dāng)這個(gè)el-input失去焦點(diǎn)的時(shí)候,通過此函數(shù)通知外部
}
blurFn() {
// 失去焦點(diǎn),再拋出去,通知外部
this.saveRowData({
cellValue: this.cellValue,
// 其他參數(shù)
});
},所以當(dāng)內(nèi)層失去焦點(diǎn)的時(shí)候,就可以通知外層去做一個(gè)替換了,就是把單元格dom重新做一個(gè)$mount掛載,就把el-input替換成了span了,為了進(jìn)一步理解,這里的span我們也可以使用繼承的方式,是new實(shí)例化使用,詳情見下方完整代碼
完整代碼
目錄結(jié)構(gòu)
threeC
-- data.js
-- input.vue
-- span.vue
three.vue
用于繼承的el-input組件
input.vue
<template>
<div class="cell">
<el-input
ref="elInputRef"
size="mini"
v-model.trim="cellValue"
@blur="blurFn"
></el-input>
</div>
</template>
<script>
export default {
props: {
cellValue: {
type: String | Number,
default: "",
},
saveRowData: Function, // 外部,傳遞進(jìn)來一個(gè)函數(shù),當(dāng)這個(gè)el-input失去焦點(diǎn)的時(shí)候,通過此函數(shù)通知外部
cellDom: Node, // 單元格dom
row: Object, // 單元格所在行數(shù)據(jù)
property: String, // 單元格的key
},
mounted() {
// 用戶雙擊后,讓其處于獲取焦點(diǎn)的狀態(tài)
this.$refs.elInputRef.focus();
},
methods: {
blurFn() {
// 失去焦點(diǎn),再拋出去,通知外部
this.saveRowData({
cellValue: this.cellValue,
cellDom: this.cellDom,
row: this.row,
property: this.property,
});
},
},
};
</script>
<style>
.cell {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
padding: 0 8px;
}
</style>用于繼承的span組件
span.vue
<template>
<span class="cell">{{ cellValue }}</span>
</template>
<script>
export default {
props: {
cellValue: {
type: String | Number,
default: "",
},
},
};
</script>統(tǒng)一繼承并暴露data.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組件
<template>
<div id="app">
<el-table
@cell-dblclick="dblclick"
:cell-class-name="cellClassName"
height="480"
:data="tableData"
border
>
<el-table-column align="center" type="index" label="序號(hào)" width="50">
</el-table-column>
<el-table-column align="center" prop="name" label="姓名" width="100">
</el-table-column>
<el-table-column align="center" prop="age" label="年齡" width="100">
</el-table-column>
<el-table-column align="center" prop="home" label="家鄉(xiāng)">
</el-table-column>
</el-table>
</div>
</template>
<script>
// 引入繼承組件對(duì)象,可取其身上的inputC構(gòu)造函數(shù)、或spanC構(gòu)造函數(shù)生成組件dom
import extendComponents from "./threeC/data";
export default {
data() {
return {
tableData: [
{
name: "孫悟空",
age: 500,
home: "花果山水簾洞",
},
{
name: "豬八戒",
age: 88,
home: "高老莊",
},
{
name: "沙和尚",
age: 1000,
home: "通天河",
},
],
/**
* 存一份舊的值,用于校驗(yàn)是否發(fā)生變化,是否修改
* */
oldCellValue: null,
};
},
methods: {
cellClassName({ row, column, rowIndex, columnIndex }) {
row.index = rowIndex; // 自定義指定一個(gè)索引,下方能夠用到
},
dblclick(row, column, cell, event) {
// 1. 序號(hào)列單元格不允許編輯,別的列單元格可以編輯
if (column.label == "序號(hào)") {
this.$message({
type: "warning",
message: "序號(hào)列不允許編輯",
});
return;
}
// 2. 存一份舊的單元格的值
this.oldCellValue = row[column.property];
// 3. 然后把單元格的值,作為參數(shù)傳遞給實(shí)例化的input組件
let cellValue = row[column.property];
// 4. 實(shí)例化組件以后,帶著參數(shù),再掛載到對(duì)應(yīng)位置
new extendComponents.inputC({
propsData: {
// 使用propsData對(duì)象傳遞參數(shù),子組件在props中可以接收到
cellValue: cellValue, // 傳遞單元格的值
saveRowData: this.saveRowData, // 傳遞回調(diào)函數(shù)用于保存行數(shù)據(jù),組件中可以觸發(fā)之
cellDom: cell, // 傳遞這個(gè)dom元素
row: row, // 傳遞雙擊的行的數(shù)據(jù)
property: column.property, // 傳遞雙擊的是哪個(gè)字段
},
}).$mount(cell.children[0]); // 5. $mount方法,用于將某個(gè)dom掛載到某個(gè)dom上
},
/**
* 失去焦點(diǎn)的時(shí)候有以下操作
* 1. 校驗(yàn)新值是否等于原有值,若等于,說明用戶未修改,就不發(fā)請(qǐng)求。若不等于就發(fā)請(qǐng)求,然后更新tableData數(shù)據(jù)
* 2. 然后使用$mount方法,掛載一個(gè)新的span標(biāo)簽dom在頁面上,即恢復(fù)原樣,而span標(biāo)簽也是實(shí)例化的哦
* */
saveRowData(params) {
console.log("繼承的子組件傳遞過來的數(shù)據(jù)", params);
// 1. 看看用戶是否修改了
if (params.cellValue == this.oldCellValue) {
console.log("未修改數(shù)據(jù),不用發(fā)請(qǐng)求");
} else {
params.row[params.property] = params.cellValue;
// 這里模擬一下發(fā)了請(qǐng)求,得到最新表體數(shù)據(jù)以后,更新tableData
setTimeout(() => {
// 給那個(gè)數(shù)組的 第幾項(xiàng) 修改為什么值
this.$set(this.tableData, params.row.index, params.row);
}, 300);
}
// 2. 恢復(fù)dom節(jié)點(diǎn)成為原來的樣子,有下面兩種方式
/**
* 方式一:使用官方推薦的$mount去掛載到某個(gè)節(jié)點(diǎn)上,上方也是
* */
new extendComponents.spanC({
propsData: {
cellValue: params.cellValue,
},
}).$mount(params.cellDom.children[0]);
/**
* 方式二:使用原生js去清空原節(jié)點(diǎn)內(nèi)容,同時(shí)再添加子元素
* */
// let span = document.createElement("span"); // 創(chuàng)建一個(gè)span標(biāo)簽
// span.innerHTML = params.cellValue; // 指定span標(biāo)簽的內(nèi)容的值
// span.classList.add("cell"); // 給span標(biāo)簽添加class為cell
// params.cellDom.innerHTML = ""; // 清空剛操作的input標(biāo)簽的內(nèi)容
// params.cellDom.appendChild(span); // 再把span標(biāo)簽給追加上去,恢復(fù)原樣
},
},
};
</script>
<style lang="less" scoped>
#app {
width: 100%;
height: 100vh;
box-sizing: border-box;
padding: 50px;
}
</style>總結(jié)
使用Vue.extend()方法,可以繼承一些組件,甚至繼承一些復(fù)雜的組件,在實(shí)際業(yè)務(wù)場(chǎng)景中會(huì)有巧妙的使用。具體業(yè)務(wù)場(chǎng)景具體分析。
此外,上述代碼中是el-input的繼承,其實(shí),我們也可以做el-select的繼承,思路和上方類似,這樣就可以在表格中雙擊單元格,選擇并更改對(duì)應(yīng)的下拉框更改el-table的單元值了,比如如果有性別這一列,那是下拉框的形式的。道友們可以按照這個(gè)思路發(fā)散哦...
到此這篇關(guān)于如何巧用Vue.extend繼承組件實(shí)現(xiàn)el-table雙擊可編輯(不使用v-if、v-else)的文章就介紹到這了,更多相關(guān)Vue.extend實(shí)現(xiàn)el-table雙擊可編輯內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue中使用elementui與Sortable.js實(shí)現(xiàn)列表拖動(dòng)排序
這篇文章主要為大家詳細(xì)介紹了Vue中使用elementui與Sortable.js實(shí)現(xiàn)列表拖動(dòng)排序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
TypeScript基本類型 typeof 和keyof案例詳解
這篇文章主要介紹了TypeScript基本類型 typeof 和keyof案例詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-10-10
vue+elementUI實(shí)現(xiàn)動(dòng)態(tài)面包屑
這篇文章主要為大家詳細(xì)介紹了vue+elementUI實(shí)現(xiàn)動(dòng)態(tài)面包屑,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
vue-music關(guān)于Player播放器組件詳解
這篇文章主要為大家詳細(xì)介紹了vue-music關(guān)于Player播放器組件的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11
Vue中Layout內(nèi)部布局el-row、el-col的實(shí)現(xiàn)
layout是一種非常方便的布局方式,本文主要介紹了Vue中Layout內(nèi)部布局el-row、el-col的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07
vue-cli2.0轉(zhuǎn)3.0之項(xiàng)目搭建的詳細(xì)步驟
這篇文章主要介紹了vue-cli2.0轉(zhuǎn)3.0之項(xiàng)目搭建的詳細(xì)步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12
Vue.js中用webpack合并打包多個(gè)組件并實(shí)現(xiàn)按需加載
對(duì)于現(xiàn)在前端插件的頻繁更新,我也是無力吐槽,但是既然入了前端的坑就得認(rèn)嘛,所以多多少少要對(duì)組件化有點(diǎn)了解,下面這篇文章主要給大家介紹了在Vue.js中用webpack合并打包多個(gè)組件并實(shí)現(xiàn)按需加載的相關(guān)資料,需要的朋友可以參考下。2017-02-02

