laso
laso

Reputation: 51

How to toggle multiple rows in a table with the same value?

Here is an example of my table.

+----+--------+---------------+
| ID |  User  | Creation_date |
+----+--------+---------------+
|  1 | Bob    | 2020-05-05    |
|  2 | Bob    | 2020-05-06    |
|  3 | Bob    | 2020-06-06    |
|  4 | John   | 2020-05-04    |
|  5 | John   | 2020-05-07    |
|  6 | John   | 2020-06-10    |
|  7 | Robert | 2020-04-04    |
|  8 | Robert | 2020-05-05    |
|  9 | Robert | 2020-06-07    |
| 10 | Robert | 2020-09-09    |
+----+--------+---------------+

What I want: I want to show only the first value of each "User" and when clicked on the row to toggle the hidden values.

+----+--------+---------------+
| ID |  User  | Creation_date |
+----+--------+---------------+
|  1 | Bob    | 2020-05-05    |
|  4 | John   | 2020-05-04    |
|  7 | Robert | 2020-04-04    |
+----+--------+---------------+

My current code, I have tried multiple different approach but I can't figure out how to show only the first value and hide the rest.

<template>
  <div>
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>User</th>
          <th>Creation date</th>
        </tr>
      </thead>
      <tbody>
        <template v-for="row in local_users">
          <tr @click="row.isVisible = !row.isVisible">
            <div v-if="row.isVisible">
              <td>{{row.user_id}}</td>
              <td>{{row.username}}</td>
              <td>{{row.creation_date}}</td>
            </div>
          </tr>
        </template>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  props: ["users"],

  data() {
    return {
      local_users: [],
    };
  },

  created() {
    this.local_users = this.users.map((e) => {
      return { ...e, isVisible: true };
    });
  },
};
</script>

Upvotes: 0

Views: 654

Answers (1)

Sphinx
Sphinx

Reputation: 10729

I moved the codes (clone this.users to this.local_users) from created() to watch of this.users, otherwise the local_users will not be synced when props=users is updated.

Then uses two data properties to store the visible state (invisibles) and the index for first username (firstMatches). firstMatches will be reset when this.users is changed.

Below is the demo:

Vue.component('v-table',{
    template:`
  <div>
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>User</th>
          <th>Creation date</th>
        </tr>
      </thead>
      <tbody>
        <template v-for="(row, index) in local_users">
          <tr @click="toggle(row)" :key="index" v-if="firstMatches[row.username] === index || !invisibles[row.username]">
              <td>{{row.user_id}}</td>
              <td>{{row.username}}</td>
              <td>{{row.creation_date}}</td>
          </tr>
        </template>
      </tbody>
    </table>
    <pre>{{invisibles}}</pre>
  </div>
    `,
  props: ["users"],
  data() {
    return {
      local_users: [],
      firstMatches: {},
      invisibles: {}
    };
  },
  watch: {
    users: {
      handler: function (newVal) {
        this.firstMatches = {}
        this.local_users = (newVal || []).slice()
        this.local_users.forEach((e, i) => {
          if (!(e.username in this.firstMatches)) {
            this.firstMatches[e.username] = i
          }
        })
      },
      immediate: true
    }
  },
  methods: {
    toggle: function (row) {
      this.$set(this.invisibles, row.username, !this.invisibles[row.username])
    }
  }
})

new Vue ({
  el:'#app',
  data () {
    return {
      users: [
        {user_id: 1, username: 'Bob', creation_date: '2020-01-01'},
        {user_id: 2, username: 'Bob', creation_date: '2020-01-02'},
        {user_id: 3, username: 'Tom', creation_date: '2020-01-03'},
        {user_id: 4, username: 'Bob', creation_date: '2020-01-04'},
        {user_id: 5, username: 'Tom', creation_date: '2020-01-05'},
        {user_id: 6, username: 'Sun', creation_date: '2020-01-05'},
      ]
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
    <div class="container">
        <div>
            <v-table :users="users"></v-table>
        </div>
    </div>
</div>

Upvotes: 1

Related Questions