Search code examples
javascriptvue.jsbootstrap-vue

in BootstrapVue table, can only drag and drop table header, the table items are not changing


I want to implement a drag and drop feature on my BootstrapVue table. By using the code below, I can only drag and drop the table header but table items are not changing. I console logged updatedHeaders which is undefined. table screenshot

<template>
  <div class="container mt-5">
    <div class="row">
      <h3>Managing Existing Accounts</h3>
    </div>
    <div class="row mt-3">
      This section shows all accounts you currently manage.<br>
      Click on a row to open up details about that account.<br>
      Accounts that have expired will have all their personally identifiable information scrubbed in 90 days.
    </div>

    <!--toggle for column customization-->
    <div>
      <b-button v-b-toggle.column-collapse variant="primary">Column</b-button>
      <b-collapse id="column-collapse" class="mt-2">
        <b-card>
          <p class="card-text">Choose the column</p>
          <b-form-checkbox-group v-model="selectedColumns">
            <b-form-checkbox value="Renew Date">Renew Date</b-form-checkbox>
            <b-form-checkbox value="Account Name">Account Name</b-form-checkbox>
            <b-form-checkbox value="Location">Location</b-form-checkbox>
            <b-form-checkbox value="Type">Type</b-form-checkbox>
            <b-form-checkbox value="Account Type">Account Type</b-form-checkbox>
          </b-form-checkbox-group>
        </b-card>
      </b-collapse>

      <div>
        <table class="table table-striped">
          <thead class="thead-dark">
          <draggable v-model="updatedHeaders" tag="tr" @end="dragged">
            <th v-for="header in headers" :key="header" scope="col">
              {{ header }}
            </th>
          </draggable>
          </thead>
          <tbody>
          <tr v-model="updatedHeaders" v-for="item in items" :key="item.gymID">
            <td v-for="header in headers" :key="header">{{ item[header] }}</td>
          </tr>
          </tbody>
        </table>

      </div>
      <rawDisplayer class="col-2" :value="items" title="Items"/>

      <rawDisplayer class="col-2" :value="updatedHeaders" title="Headers"/>
    </div>
  </div>
</template>

<script>
import draggable from 'vuedraggable';

export default {
  components: {
    draggable,
  },
  data() {
    return {
      dataLoaded: false,
      accountList: [],
      items: [],
      selectedColumns: ['Renew Date', 'Account Name', 'Location', 'Type', 'Account Type'],
      _xsrf: "",
    };
  },
  created() {
    // Load the saved column order from local storage
    const headers = localStorage.getItem('tableHeaders');

    if (headers !== "undefined") {
      const selectedFields = JSON.parse(headers);
      this.selectedColumns = selectedFields.map(field => field.label);
    }

    fetch('/distributor/api/managing-existing-accounts')
        .then(response => response.json())
        .then(data => {
          this.accountList = data.accountList;
          this.items = data.accountList;
          this.dataLoaded = true;
          this._xsrf = data._xsrf;
        })
        .catch(error => {
          console.error('Error fetching data:', error);
        });
  },
  computed: {
    filteredFields() {
      if (this.dataLoaded) {
        return Object.keys(this.items[0] ? this.items[0] : {})
            .filter(field => this.selectedColumns.includes(field))
            .map(field => ({
              key: field,
              label: field
            }));
      }
    },
    headers() {
      console.log(this.updatedHeaders);
      return this.updatedHeaders ? this.updatedHeaders : this.filteredFields?.map(field => field.key);
    },
  },

  methods: {
    dragged() {
      // Map the new order of headers to their corresponding labels
      const updatedLabels = this.updatedHeaders?.map(header => this.filteredFields.find(field => field.key === header).label);
      // Save the new order of headers to local storage
      localStorage.setItem('tableHeaders', JSON.stringify(this.headers));
    }
  }
}
</script>

<style>
/* Add your styles here */
</style>

I want to have checkboxes to select the column I want to show and I can drag and drop the table columns.


Solution

  • You can only drag the items that are put inside <draggable> tag

      <draggable v-model="updatedHeaders" tag="tr" @end="dragged">
        <th v-for="header in headers" :key="header" scope="col">
          {{ header }}
        </th>
      </draggable>
    

    Since you have the table header inside the <draggable> tag, you can drag it.

    Check the Project Documentation on how to use the Vue.Draggable component.

    The updatedHeaders data property is not declared. I guess you should declare it in the vue data and fill with headers in the created() hook.

    data() {
        return {
          updatedHeaders: [], // <- here
          dataLoaded: false,
         ...
    

    and

    if (headers !== "undefined") {
      const selectedFields = JSON.parse(headers);
      this.selectedColumns = selectedFields.map(field => field.label);
      this.updatedHeaders =  ???  selectedFields  // not sure about your data.
    }
    

    I don't know your data, so you should know it better, what should be put in updatedHeaders.

    If you want to drag the table columns, then you have to implement it yourself using the Vue.Dragabble, similar to your example with headers.