2
votes

I have a v-data-table in a component, and am using the checkboxes created by select-all to filter information in the component's parent. None of the rows start off selected. I would like all of them to be checked by default.

Things that have not worked:

  • in the parent's data: setting selectedItems to Array.from(this.$store.state.tableItems) by default (the item in the store isn't defined at that point)
  • in the mounted or created event in the child: setting selectedItems to Array.from(this.tableItems) (this produces an "avoid mutating a prop directly" error)

I feel like there is probably a way to do this with a computed property?

(There is probably also a more idiomatic way using actions or events or something? I am new to Vue.)

MyComponent.vue

<template>
  <v-data-table
    :value="selectedItems"
    @input="$emit('update:selectedItems', $event)"
    :headers="headers"
    :items="tableItems"
    item-key="id"
    select-all
  >
    <template slot="items" slot-scope="props">
      <td>
        <v-checkbox v-model="props.selected" hide-details></v-checkbox>
      </td>
      <td>{{ props.item.id }}</td>
    </template>
  </v-data-table>
</template>
<script>
export default {
  props: {
    tableItems: { type: Array, },
    selectedItems: { type: Array, },
  },
  data() {
    return {
      headers: [
        { text: 'ID', value: 'id', },
      ],
    };
  },
};
</script>

Parent.vue

<template>
  <MyComponent :tableItems="tableItems" :selectedItems.sync="selectedItems"/>
</template>
<script>
export default {
  components: {
    MyComponent,
  },
  data() {
    return {
      selectedItems: [],
    };
  },
  computed: {
    tableItems() {
      return this.$store.state.tableItems;  // set by an axios request in beforeCreate in App.vue
    },
  }
};
</script>
1

1 Answers

1
votes

Alright, the following did work (I was already using Vuex) but I'm hoping someone else has a more satisfying answer.

store.js

Track the selected items here. The default value of null is so that, when setting the table items after the first axios call finishes, we can set the default.

export default new Vuex.Store({
  state: {
    tableItems: {},
    selectedItems: null,
  },
  mutations: {
    setTableItems(state, payload) {
      state.tableItems= payload;
      if (state.selectedItems == null) state.selectedItems = payload;
    },
    setSelectedItems(state, payload) {
      state.selectedItems = payload;
    },
  }
});

MyComponent.vue

What changed here is that I'm using the selected elements to the Vuex store as the value for the data table, and then committing a mutation on every input.

<template>
  <v-data-table
    :value="selectedItems"
    @input="updateSelectedItems"
    :headers="headers"
    :items="tableItems"
    item-key="id"
    select-all
  >
    <template slot="items" slot-scope="props">
      <td>
        <v-checkbox v-model="props.selected" hide-details></v-checkbox>
      </td>
      <td>{{ props.item.id }}</td>
    </template>
  </v-data-table>
</template>
<script>
export default {
  props: {
    tableItems: { type: Array, },
  },
  data() {
    return {
      headers: [
        { text: 'ID', value: 'id', },
      ],
    };
  },
  methods: {
    updateSelectedItems(event) {
      this.$store.commit("setSelectedItems", event);
    },
  },
  computed: {
    selectedItems() { return this.$store.state.selectedItems; },
  }
};
</script>

Parent.vue

This ends up being a lot simpler; less data binding, just referencing the store.

<template>
  <MyComponent :tableItems="tableItems"/>
</template>
<script>
export default {
  components: {
    MyComponent,
  },
  computed: {
    tableItems() { return this.$store.state.tableItems; },
    selectedItems() { return this.$store.state.selectedItems; },
  }
};
</script>