1
votes

How to implement the "dynamic table header column" using an array object with vuetify data table? Is it possible to do this task as like the below example:

    ----------------------------------------------
    | Employee Id | Time in | Time Out | Time In |
    ----------------------------------------------
    | E0001       | 8:00 am | 5:00 pm  | 8:30 am |
    ----------------------------------------------
    | E0002       | 8:00 am | 5:00 pm  |         |
    ----------------------------------------------
    | E0003       |         | 5:00 pm  | 8:00 am |
    ----------------------------------------------

The header column Time in | Time Out | Time In are "type". The logs used as header column on the example table were came from an employee array object with the highest number of it then some of the following time logs from other employee will do just follow on what specific type is display on each columns on the table.

I got the first step on rocks! is to get the employee with highest array number which is the "dtr" array from an object. By comparing their lengths and get the values at the same time, to use it as header for table column.

The main problem is, how it could be possible to display it using Vuetify Data table?

[
  {
     employee_id:E0001,   <--- an employee with the highest array of logs 
     dtr:[
           { 
              type:"timein", time:"8:00 am" 
           },
           { 
              type:"timeout", time:"5:00 pm" 
           },
           { 
              type:"timein", time:"8:30 am" 
           }
      ]
  },
  {
     employee_id:E0002,
     dtr:[
           { 
              type:"timein", time:"8:00 am" 
           },
           { 
              type:"timeout", time:"5:00 pm" 
           },
      ]
   },
   {
     employee_id:E0003,
     dtr:[
           { 
              type:"timeout", time:"5:00 pm" 
           },
           { 
              type:"timein", time:"8:00 am" 
           },
      ]
   } 
]
1

1 Answers

1
votes

Yes, it is possible to implement this in Vue and using Vuetify. You just need to use the computed property to transform the headers and actual rows.

If you look at the vuetify's datatable documentation, it takes headers as an array of text, value pair. So it implies, we need to transform our data such a way that vuetify can understand it.

To begin with, just assign the the record to a variable named logs in the data section.

new Vue({
  el: "#app",
  vuetify: new Vuetify(),
  data() {
    return {
      logs: []
    };
  },

Inside the mounted or created hooks, we can initialize the data by some actual data which may come from an API request.

new Vue({
  el: "#app",
  vuetify: new Vuetify(),
  data() {
    return {
      logs: []
    };
  },
  computed: {
    //will see how to implement this
  },
  methods: {
    getLogs() {
      return [
        {
          employee_id: "E0001",
          dtr: [
            {
              type: "timein",
              time: "8:00 am"
            },
            {
              type: "timeout",
              time: "5:00 pm"
            },
            {
              type: "timein",
              time: "8:30 pm"
            },
            {
              type: "timeout",
              time: "8:45 pm"
            },
            {
              type: "timein",
              time: "10:45 pm"
            }
          ]
        },
        {
          employee_id: "E0002",
          dtr: [
            {
              type: "timein",
              time: "8:00 am"
            },
            {
              type: "timeout",
              time: "5:00 pm"
            }
          ]
        },
        {
          employee_id: "E0003",
          dtr: [
            {
              type: "timeout",
              time: "5:00 pm"
            },
            {
              type: "timein",
              time: "8:00 am"
            }
          ]
        }
      ];
    },
    initLogs() {
      this.logs = this.getLogs();
    }
  },
  created() {
    //init log can be use fetch data API with a little bit of change
    this.initLogs();
  }
});

Now the most important part the computed properties that will construct the actual data required by datatable.We need a computed property for headers and items. Here is the logHeader computed property that returns employee_id and timein and timeout header rows.

logHeaders() {
      // tranforms the logs to find out which is the longest row
      let logRecords = this.logs.map((log) => {
        let dtrTypes = [];
        log.dtr.forEach((d) => dtrTypes.push(d.type));
        return {
          employee_id: log.employee_id,
          dtrLength: log.dtr.length,
          dtrTypes: dtrTypes
        };
      });

      let records = _.sortBy(logRecords, ["dtrLength"]);
      // find the longest record
      let longestRecord = records[records.length - 1];
      let headers = [];

      console.log("records: ", records[records.length - 1]);
      //construct the header row
      headers.push({
        text: "employee_id",
        value: "employee_id"
      });

      // inside object we can only store unique keys!!
      //index plays a crucial role in this as it changes
      // timein to be timein1. timein2 so on

      longestRecord.dtrTypes.forEach((type, index) =>
        headers.push({ text: type, value: type + "" + index })
      );
      console.log("headers", headers);
      return headers;
    },

This would give us an array of object like following

[ 
  { name: 'employee_id', value: 'employee_id' },
  { name: 'timein', value: 'timein' },
  { name: 'timeout', value: 'timeout' }
]

NOTE: As object can't have a duplicate key, If there is another timein entry this would create issue, so we just need to add index to the header values so that we can add more timeinor timeout with a different value like timin1 or timeout1.

So the above function uses index to add same key text but with a different value.

[
  { name: "employee_id", value: "employee_id" },
  { name: "timein", value: "timein0" },
  { name: "timeout", value: "timeout1" },
  { name: "timein", value: "timein2"  } // here
];

Finally,the logRows computed property that transforms the logs array to the kind of data we need for our datatable.

logRows() {
      let logR = this.logs.map((log) => {
        let dtrRecord = {};
        // construct the rows accroding to headers
        log.dtr.forEach(
          // index is also crucial here
          (d, index) => (dtrRecord[d.type + "" + index] = d.time)
        );
        return Object.assign(
          {},
          { employee_id: log.employee_id, ...dtrRecord }
        );
      });
      return logR;
    }

In the view we just need to use this headers and rows as inputs to the datatable.

  <div>
    <v-data-table :headers="logHeaders" :items="logRows"></v-data-table>
  </div>

If you connect all the bits and pieces together, it would look like following.

new Vue({
  el: "#app",
  vuetify: new Vuetify(),
  data() {
    return {
      logs: []
    };
  },
  computed: {
    logHeaders() {
      // tranforms the logs to find out which is the longest
      // row
      let logRecords = this.logs.map((log) => {
        let dtrTypes = [];
        log.dtr.forEach((d) => dtrTypes.push(d.type));
        return {
          employee_id: log.employee_id,
          dtrLength: log.dtr.length,
          dtrTypes: dtrTypes
        };
      });

      let records = _.sortBy(logRecords, ["dtrLength"]);
      // find the longest record
      let longestRecord = records[records.length - 1];
      let headers = [];

      console.log("records: ", records[records.length - 1]);
      //construct the header row
      headers.push({
        text: "employee_id",
        value: "employee_id"
      });

      // inside object we can only store unique keys!!
      //index plays a crucial role in this as it changes
      // timein to be timein1. timein2 so on

      longestRecord.dtrTypes.forEach((type, index) =>
        headers.push({ text: type, value: type + "" + index })
      );
      console.log("headers", headers);
      return headers;
    },
    logRows() {
      let logR = this.logs.map((log) => {
        let dtrRecord = {};
        // construct the row accroding to headers
        // checkour
        log.dtr.forEach(
          (d, index) => (dtrRecord[d.type + "" + index] = d.time)
        );
        return Object.assign(
          {},
          { employee_id: log.employee_id, ...dtrRecord }
        );
      });
      return logR;
    }
  },
  methods: {
    getLogs() {
      return [
        {
          employee_id: "E0001",
          dtr: [
            {
              type: "timein",
              time: "8:00 am"
            },
            {
              type: "timeout",
              time: "5:00 pm"
            },
            {
              type: "timein",
              time: "8:30 pm"
            },
            {
              type: "timeout",
              time: "8:45 pm"
            },
            {
              type: "timein",
              time: "10:45 pm"
            }
          ]
        },
        {
          employee_id: "E0002",
          dtr: [
            {
              type: "timein",
              time: "8:00 am"
            },
            {
              type: "timeout",
              time: "5:00 pm"
            }
          ]
        },
        {
          employee_id: "E0003",
          dtr: [
            {
              type: "timein",
              time: "8:00 am"
            },
            {
              type: "timeout",
              time: "5:00 pm"
            }
          ]
        }
      ];
    },
    initLogs() {
      this.logs = this.getLogs();
    }
  },
  created() {
    this.initLogs();
  }
});

Here is a working codepen to just see this implementation in action.