🌱

b-tableでソートすると選択行がクリアされてしまう

2023/03/15に公開

モチベ

  • テーブルリストからユーザーが選択する場面などで困った。
  • ググると「仕様です」みたいな内容が。解決方法は示されていない。
  • 愚直にではあるが、解決できたので誰かの役に立てばと、記事にしてみる。

結論

問題再現

  • こんな風にソートすると選択が解除されてしまう。

<template>
  <div id="app">
    <b-table
      :items="items"
      :fields="fields"
      selectable
      @row-selected="onSelected"
    >
    </b-table>
  </div>
</template>

<script>
export default {
  name: "App",
  components: {},
  data() {
    return {
      selected: [],
      items: [
        { id: 1, age: 40, first_name: "Dickerson", last_name: "Macdonald" },
        { id: 2, age: 21, first_name: "Larsen", last_name: "Shaw" },
        { id: 3, age: 89, first_name: "Geneva", last_name: "Wilson" },
        { id: 4, age: 38, first_name: "Jami", last_name: "Carney" },
      ],
      fields: [
        { key: "id", sortable: true },
        { key: "age", sortable: true },
        { key: "first_name", sortable: true },
        { key: "last_name", sortable: true },
      ],
    };
  },
  methods: {
    onSelected(rows) {
      this.selected = rows;
    },
  },
};
</script>

問題解決

  • ソート時に選択行を backupSelected に待避しておく
  • ソート直後の行活性は activateRow で行う
<template>
  <div id="app">
    <b-table
      :items="items"
      :fields="fields"
      selectable
      @row-selected="onSelected"
      @sort-changed="onSortChanged"
    >
      <template v-slot:cell()="{ value, item, selectRow }">
        {{ value }}
        {{ activateRow(item, selectRow) }}
      </template>
    </b-table>
  </div>
</template>

<script>
export default {
  name: "App",
  components: {},
  data() {
    return {
      selected: [],
      backupSelected: [],
      items: [
        { id: 1, age: 40, first_name: "Dickerson", last_name: "Macdonald" },
        { id: 2, age: 21, first_name: "Larsen", last_name: "Shaw" },
        { id: 3, age: 89, first_name: "Geneva", last_name: "Wilson" },
        { id: 4, age: 38, first_name: "Jami", last_name: "Carney" },
      ],
      fields: [
        { key: "id", sortable: true },
        { key: "age", sortable: true },
        { key: "first_name", sortable: true },
        { key: "last_name", sortable: true },
      ],
    };
  },
  methods: {
    onSelected(rows) {
      if (rows.length === 0) {
        this.selected = this.backupSelected;
        this.backupSelected = [];
      } else {
        this.selected = rows;
      }
    },
    onSortChanged() {
      this.backupSelected = this.selected;
    },
    activateRow(row, activateRowFunc) {
      if (this.selected.find((item) => row.id === item.id)) {
        activateRowFunc();
      }
    },
  },
};
</script>

プラスα

  • ページ読み込み時に既に選択行がある場合は、あらかじめ行を活性化しておく

<template>
  <div id="app">
    <b-table
      :items="items"
      :fields="fields"
      selectable
      @row-selected="onSelected"
      @sort-changed="onSortChanged"
    >
      <template v-slot:cell()="{ value, item, selectRow }">
        {{ value }}
        {{ activateRow(item, selectRow) }}
      </template>
    </b-table>
  </div>
</template>

<script>
export default {
  name: "App",
  components: {},
  data() {
    return {
      selected: [],
      backupSelected: [],
      alreadySelected: [
        { id: 1, age: 40, first_name: "Dickerson", last_name: "Macdonald" },
        { id: 3, age: 89, first_name: "Geneva", last_name: "Wilson" },
      ],
      items: [
        { id: 1, age: 40, first_name: "Dickerson", last_name: "Macdonald" },
        { id: 2, age: 21, first_name: "Larsen", last_name: "Shaw" },
        { id: 3, age: 89, first_name: "Geneva", last_name: "Wilson" },
        { id: 4, age: 38, first_name: "Jami", last_name: "Carney" },
      ],
      fields: [
        { key: "id", sortable: true },
        { key: "age", sortable: true },
        { key: "first_name", sortable: true },
        { key: "last_name", sortable: true },
      ],
    };
  },
  methods: {
    onSelected(rows) {
      if (rows.length === 0) {
        this.selected = this.backupSelected;
        this.backupSelected = [];
      } else {
        this.selected = rows;
      }
    },
    onSortChanged() {
      this.backupSelected = this.selected;
    },
    activateRow(row, activateRowFunc) {
      if (this.selected.find((item) => row.id === item.id)) {
        activateRowFunc();
      }
    },
  },
  mounted() {
    this.selected = this.alreadySelected;
  },
};
</script>

感想

  • slotについては本家のドキュメントが一番しっかりしてる。今まで何となくでやり過ごしていたけど、今回ちゃんと理解できてすっきりした。
  • slotにしろb-tableにしろ、ドキュメントを読めば何の問題もないが、なかなか読まない人へ。お役に立てましたら。
  • はじめて記事書きました。何かお気づきであればFBいただけると嬉しいです。

Discussion