【Vue】テーブル操作エキスパートへの道。ボタンクリック列でを削除する方法

ボタンクリックで表から列を削除する方法について。


これまで、

・[v-forを使ったテーブルの作成](https://qiita.com/yuta-38/items/88ae6036e95f70d7d708)
・[表で複数選択を可能にする方法](https://qiita.com/yuta-38/items/93807929f83991045ee5)
・[ボタンクリックで行を追加する方法](https://qiita.com/yuta-38/items/d0f646c876b0cdabe0e7)
・[ボタンクリックで列を追加する方法](https://qiita.com/yuta-38/items/fa74e603927115302e1b)
・[ボタンクリックで列を追加する方法](https://qiita.com/yuta-38/items/a42a19472fdd9c5b7438)

について確認したので、次のステップとして列を削除する方法を確認する。


##実装イメージ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/e348f036-217a-48c7-940b-6efbd8af590c.png)


↓ 選択中のセルを削除する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/959c8ccc-0754-1e7a-429d-9729b2dbfaa9.png)


##考え方

1. 現在選択中のセルの有無を判断
2. 現在選択中のセルの列番号を取得する
3. 表から選択中のセル以外を残す
4. 選択を解除


##実装

```javascript:
    removeColumn(){
      //選択中のセルがある場合のみ処理を実行
      if(this.currentCells.length != 0){

        //削除したいセル番号を抽出(重複削除)
        const cellIndicesToRemove = new Set(
          this.currentCells.map(cell => cell.xxxcellIndex )
        )

        //削除したい列を除外
        this.rows.map((row)=> 
          row.table_cells = (row.table_cells.filter((x, cellIndex)=>
            !cellIndicesToRemove.has(cellIndex)
          ))
        )

        //選択中のセルを解除
        this.currentCells = []
      }
    }
```


<br>
####1. 現在選択中のセルの有無を判断

```js:
if(this.currentCells.length != 0)
```

現在選択中のセルの行列番号を格納する配列の要素の長さで判断する。


<br>
####2. 現在選択中のセルの列番号を取得する

```js:
const cellIndicesToRemove = new Set(
          this.currentCells.map(cell => cell.xxxcellIndex )
        )
```

mapメソッドを使って、現在選択中の列番号を抽出する。
重複削除するため、Setオブジェクトに変換する。


<br>
####3.表から選択中のセル以外を残す

```js:
        //削除したい列を除外
        this.rows.map((row)=> 
          row.table_cells = (row.table_cells.filter((x, cellIndex)=>
            !cellIndicesToRemove.has(cellIndex)
          ))
        )
```

filterとSetオブジェクトのhasメソッドを使って、現在選択中以外のセル番号のみを残す。

mapメソッドで行要素を一つづつ抜き出す。
filterメソッドで各行の列要素を一つづつ抜き出し、!とhasメソッドで番号が一致したセルを除外していく。

filterは非破壊のため、不要なセルを排除した行を元の行に代入する。


<br>
####4. 選択を解除

```js:
      this.currentCells = []
```

現在選択中のセルを空にする。



<br>
##フルコード

```vue:
<template>
  <div>
    <p>〜TmpRemoveColumn.vue〜</p>
    <p>currentCells : {{currentCells}}</p>
    

    <p>
      <button
        @click="removeColumn"
      >列を削除
      </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
          }
        ]
      }
    },
    removeColumn(){
      //選択中のセルがある場合のみ処理を実行
      if(this.currentCells.length != 0){

        //削除したいセル番号を抽出(重複削除)
        const cellIndicesToRemove = new Set(
          this.currentCells.map(cell => cell.xxxcellIndex )
        )

        //削除したい列を除外
        this.rows.map((row)=> 
          row.table_cells = (row.table_cells.filter((x, cellIndex)=>
            !cellIndicesToRemove.has(cellIndex)
          ))
        )

        //選択中のセルを解除
        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>
```