ボタンクリックで表から行を削除する方法について。
これまで、
・v-forを使ったテーブルの作成
・表で複数選択を可能にする方法
・ボタンクリックで行を追加する方法
・ボタンクリックで列を追加する方法
について確認したので、次のステップとして行を削除する方法を確認する。
##アウトプットイメージ
↓ 選択している行のみを削除
##行削除の考え方
複数選択時は、選択した行をすべて削除する。
考え方は比較的シンプル
- 選択中の行番号を抽出する(重複削除)
- Set, mapメソッド
- 抽出した行番号以外の表を作る
- filter, hasメソッド
##行削除実例
removeRow(){
//選択中のセルがある場合のみ処理を実行
if(this.currentCells.length != 0){
//削除したいセル番号を抽出(重複削除)
const rowIndicesToRemove = new Set(
this.currentCells.map(cell => cell.xxxrowIndex )
)
//削除したい列を除外(該当した行番号はスルー)
this.rows = this.rows.filter((row, rowIndex)=>
!rowIndicesToRemove.has(rowIndex)
)
//選択中のセルを解除
this.currentCells = []
}
}
####・選択中のセルがある場合のみ処理を実行 `if(this.currentCells.length != 0)` 現在選択中のセルがない場合は何もしない。
####・選択中のセルの行番号を抜き出す
Setオブジェクトを使う。
Setオブジェクトとは、重複がない特別なオブジェクト。配列のように並び順もない。袋の中に個々の値が入っているイメージ。
const rowIndicesToRemove = new Set(
this.currentCells.map(cell => cell.xxxrowIndex )
)
new Set()
でSetオブジェクトを生み出す。
this.currentCells.map(cell => cell.xxxrowIndex)
選択中のセルの行番号を一つづつ取得し配列にする。
この処理を実行することで下記のようなイメージになる。
Setオブジェクトの詳しい使い方はこちら
####・指定した要素以外を抜き出す
this.rows = this.rows.filter((row, rowIndex)=>
!rowIndicesToRemove.has(rowIndex)
)
filterメソッドはtrueになった場合のみ要素を残す。第二引数はインデックス番号になる。
rowIndicesToRemove.has(rowIndex)
現在選択中の行番号が入ったSetオブジェクトrowIndeicesToRemoveに対し、hasメソッドで一致する値があった場合にtrueを返す。
has
メソッドはSet専用のメソッド。指定した値があるとtrue,該当しない場合はfalseを返す。
ArrayオブジェクトのindexOf
やinclude
メソッドと似た役割。
!
をつけることで、一致しない場合のみtrueとすることで、残したい列のみを残す。
##フルコード
<template>
<div>
<p>〜TmpRemoveRow.vue〜</p>
<p>currentCells : {{currentCells}}</p>
<p>
<button
@click="removeRow"
>行を削除
</button>
</p>
<table>
<template v-for="(tr, rowIndex) in rows">
<tr :key="rowIndex">
<template v-for="(cell, cellIndex) in tr.table_cells">
<th :key="cellIndex"
v-if="cell.cell_type == 'TH'"
:class="{'is-active': isActive(rowIndex, cellIndex)}"
@click="clickCell($event)">
( {{rowIndex}} , {{cellIndex}} )
</th>
<td :key="cellIndex"
v-else-if="cell.cell_type == 'TD'"
:class="{'is-active': isActive(rowIndex, cellIndex)}"
@click="clickCell($event)">
( {{rowIndex}} , {{cellIndex}} )
</td>
</template>
</tr>
</template>
</table>
</div>
</template>
<script>
export default {
data(){
return{
currentCells:[],
rows: [
{
"table_cells": [
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
]
},
{
"table_cells": [
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
]
},
{
"table_cells": [
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
]
},
{
"table_cells": [
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
]
},
{
"table_cells": [
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
{"cell_type": "TD"},
]
},
]
}
},
methods:{
//isActiveの判定
//currentCellsの中にあればtrueにする
//指定した行列番号の要素がある=数値が-1以外ならtrueにする。
isActive(rowIndex, cellIndex){
return this.currentCells.findIndex((elem) =>
elem.xxxrowIndex == rowIndex && elem.xxxcellIndex == cellIndex
) > -1
},
clickCell(event){
//クリックされたセルの情報
const cell = event.target
const tr = event.target.parentNode
//クリックされたセルが既に選択されている場合は、配列から削除する
if(this.isActive(tr.rowIndex, cell.cellIndex)){
//選択中の配列の何番目の要素かを求める
const rmIndex = this.currentCells.findIndex((elem)=>
elem.xxxrowIndex == tr.rowIndex && elem.xxxcellIndex == cell.cellIndex
)
//選択した要素を選択中の配列から削除する
this.currentCells = [
...this.currentCells.slice(0, rmIndex),
...this.currentCells.slice(rmIndex + 1)
]
} else{
this.currentCells = [
...this.currentCells,
{
xxxrowIndex: tr.rowIndex,
xxxcellIndex: cell.cellIndex
}
]
}
},
removeRow(){
//選択中のセルがある場合のみ処理を実行
if(this.currentCells.length != 0){
//削除したいセル番号を抽出(重複削除)
const rowIndicesToRemove = new Set(
this.currentCells.map(cell => cell.xxxrowIndex )
)
//削除したい列を除外(該当した行番号はスルー)
this.rows = this.rows.filter((row, rowIndex)=>
!rowIndicesToRemove.has(rowIndex)
)
//選択中のセルを解除
this.currentCells = []
}
}
}
}
</script>
<style lang="scss" scoped>
table{
width: 80%;
th,td{
border: thin solid rgba(0, 0, 0, 0.12);
text-align: center;
color: gray;
}
th{
background: #ccc;
}
th, td{
//選択状態
&.is-active{
border: 1px double #0098f7;
}
}
}
button{
background: gray;
padding: 5px 20px;
color: white;
border-radius: 50px;
}
input{
margin: 7px;
box-sizing: border-box;
}
</style>
コード自体はすごく簡単。Set, has, map, filterを使えるかが鍵となる。