22

I have a table with dynamically changing columns. because of that, the template for the table can't be hardcoded like this -

<template>
  <v-data-table
    :headers="headers"
    :items="items"
    hide-actions
    class="elevation-1"
  >
    <template slot="items" slot-scope="props">
      **<td>{{ props.item.name }}</td>
      <td class="text-xs-right">{{ props.item.calories }}</td>
      <td class="text-xs-right">{{ props.item.fat }}</td>
      <td class="text-xs-right">{{ props.item.carbs }}</td>
      <td class="text-xs-right">{{ props.item.protein }}</td>
      <td class="text-xs-right">{{ props.item.iron }}</td>**
    </template>
  </v-data-table>
</template>

I'm getting the code for this part in the response. can't figure out how to communicate it forward.

Kas Elvirov
  • 7,394
  • 4
  • 40
  • 62
moran tal
  • 221
  • 1
  • 2
  • 4

5 Answers5

31

I was looking at the same question, found a typical way to avoid hardcoding the data structure in the markup; you can use the content of the headers to script the item template using a v-for loop like this:

<div id="app">
  <v-app id="inspire">
    <v-data-table
      :headers="headers"
      :items="desserts"
      hide-actions
      class="elevation-1"
    >
      <template slot="items" slot-scope="myprops">
        <td v-for="header in headers">
        {{ myprops.item[header.value] }}
        </td>
      </template>
    </v-data-table>
  </v-app>
</div>
Bart Adriaanse
  • 411
  • 4
  • 3
6

I know this question is old but I have been having same problem and I stumbled across this page. Thankfully I have solved my issue by editing the code given by Bart to meet the latest syntax in Vue 2.

<v-data-table :headers="headers"
 :items="myDataObject"
 class="elevation-24">
    <template v-slot:body="props">
      <tr v-for="index in props.items">
        <td v-for="header in headers" class="text-left font-weight-black">
          {{ index[header.value]}}
        </td>
      </tr>
    </template>
</v-data-table>
Suleman
  • 644
  • 3
  • 12
  • 23
5

Here is something you can try, i know it works as i have used a similar approach. But to understand how this works, read about dynamic components in vue js.

Warning : You have to configure every single data bound item yourself and it can be overwhelming, but if you have many data table it may be worth it. dont give up :)

The headers can be configured using the headers scoped-slot. Read the documentation for more details here. Look for the scoped slots tab and see what you could configure.

And now for the column you got to configure using dynamic components. That said, return the component based on the type of the data table column, say if text then return <td>text</td> and so on. Am simply laying out an idea for you and you got to configure the way you want to.

<v-data-table
  v-model="tableRowsSelected" 
  :items="tableItems"
  :headers="tableHeaders"
  :pagination.sync="tablePagination" 
  :rows-per-page-items="tablePaginationDropdown"
  item-key="name" 
  class="elevation-1"
> 
   <template v-if="tableHeaders" slot="headers" slot-scope="row">
    <tr>
      <th
        v-for="header in row.headers"
        :key="header.text"
        :class="['column sortable', tablePagination.descending ? 'desc' : 'asc', header.value === tablePagination.sortBy ? 'active' : '']"
        @click="changeSort(header.value)"
      >
        <v-icon small>arrow_upward</v-icon>
        {{ header.text }}
      </th>
    </tr>
  </template>
  <template template slot="items" slot-scope="row">
      <tr>
        <component v-for="header in Object.keys(row.item)" :key="header" :is="getComponentByColumnType(header, row.item)"></component>
      </tr>
  </template>

export default {
data () {
 return {
        tableItems: []
 }
 computed: {
  tableHeaders: function () { }
  tablePagination: functin() {}
  // and other properties here or you could simply configure them as part of 
  data.
  },
  method:{
    getComponentByColumnType(header, data) {
     // return the component per your column type here.
    }
  }
}
tony19
  • 125,647
  • 18
  • 229
  • 307
Jaya
  • 3,721
  • 4
  • 32
  • 48
1

I cannot get your question but i am assuming that you want to create a vuetify table.

So below is the template:

 <template>
  <v-data-table
    :headers="headers"
    :items="items"
    hide-actions
    class="elevation-1"
  >
    <template slot="items" slot-scope="props">
      <td>{{ props.item.name }}</td>
      <td class="text-xs-right">{{ props.item.calories }}</td>
      <td class="text-xs-right">{{ props.item.fat }}</td>
      <td class="text-xs-right">{{ props.item.carbs }}</td>
      <td class="text-xs-right">{{ props.item.protein }}</td>
      <td class="text-xs-right">{{ props.item.iron }}</td>
    </template>
  </v-data-table>
</template>

and below the script:

 <script>
  export default {
    data () {
      return {
        headers: [
          {
            text: 'Dessert (100g serving)',
            align: 'left',
            sortable: false,
            value: 'name'
          },
          { text: 'Calories', value: 'calories' },
          { text: 'Fat (g)', value: 'fat' },
          { text: 'Carbs (g)', value: 'carbs' },
          { text: 'Protein (g)', value: 'protein' },
          { text: 'Iron (%)', value: 'iron' }
        ],
        items: [
          {
            value: false,
            name: 'Frozen Yogurt',
            calories: 159,
            fat: 6.0,
            carbs: 24,
            protein: 4.0,
            iron: '1%'
          },
          {
            value: false,
            name: 'Ice cream sandwich',
            calories: 237,
            fat: 9.0,
            carbs: 37,
            protein: 4.3,
            iron: '1%'
          },
          {
            value: false,
            name: 'Eclair',
            calories: 262,
            fat: 16.0,
            carbs: 23,
            protein: 6.0,
            iron: '7%'
          },
          {
            value: false,
            name: 'Cupcake',
            calories: 305,
            fat: 3.7,
            carbs: 67,
            protein: 4.3,
            iron: '8%'
          },
          {
            value: false,
            name: 'Gingerbread',
            calories: 356,
            fat: 16.0,
            carbs: 49,
            protein: 3.9,
            iron: '16%'
          },
          {
            value: false,
            name: 'Jelly bean',
            calories: 375,
            fat: 0.0,
            carbs: 94,
            protein: 0.0,
            iron: '0%'
          },
          {
            value: false,
            name: 'Lollipop',
            calories: 392,
            fat: 0.2,
            carbs: 98,
            protein: 0,
            iron: '2%'
          },
          {
            value: false,
            name: 'Honeycomb',
            calories: 408,
            fat: 3.2,
            carbs: 87,
            protein: 6.5,
            iron: '45%'
          },
          {
            value: false,
            name: 'Donut',
            calories: 452,
            fat: 25.0,
            carbs: 51,
            protein: 4.9,
            iron: '22%'
          },
          {
            value: false,
            name: 'KitKat',
            calories: 518,
            fat: 26.0,
            carbs: 65,
            protein: 7,
            iron: '6%'
          }
        ]
      }
    }
  }
</script>

This is copy paste from vuetify docs

Now if you want to use dynamic headers just change the headers property.

My recommendation is,to use vuetify multiple select with your table. Populate the multiple select with table columns and let the user to select or deselect.Then in data-table in :headers use the property which correspond to multiple select

For example if mutpiple select is binded to e6(name of property), then the v-data-table will look:

  <v-data-table
    :headers="e6" /*this changed*/
    :items="items"
    hide-actions
    class="elevation-1"
  >
Stephen Docy
  • 4,738
  • 7
  • 18
  • 31
Roland
  • 24,554
  • 4
  • 99
  • 97
  • thanks a lot! let me clarify.. in my code the user chooses an experiment and the table is based on the results. so for different experiments, different things were tested and the columns will be completely different. the problem is creating the different template each time. thanks! – moran tal Apr 02 '18 at 08:23
  • i will make an example and i will send yo you just hold some minutes i am little busy – Roland Apr 02 '18 at 08:41
  • Here is the link where columns changed dinamically regarder the select.It is not perfect,you have to add the logic for the rows of table but i think your question is answered.Please mark my answer as useful if i helped.Here is the link https://codepen.io/Roland1993/pen/LdrPzN?editors=1010 – Roland Apr 02 '18 at 08:49
  • @roliroli your codepen is good but it doesn't remove the td items, so it's not quite there yet. – Simon D May 23 '18 at 05:45
0

For future searchers, I managed to create an example in codepen to resize the table columns with vuetify 2.

https://codepen.io/marcelobbff/pen/BajQwyq?editors=1010

code:

HTML

<div id="app">

  <v-app id="inspire">
    <v-container>
      <v-row><v-btn @click="add">ADD</v-btn><v-btn @click="remove">REMOVE</v-btn></v-row>
    <v-data-table
      :headers="headers"
      :items="desserts"

    >
      <template v-slot:body="props">
       <tbody>
        <tr v-for="item in props.items">
          <td v-for="(header, index) in headers">
            <span v-if="index">{{ item.cols[index-1] }}</span>
            <span v-else>{{ item.name }}</span>
          </td>
        </tr>
       </tbody>
      </template>
    </v-data-table>


      </v-container>
  </v-app>
</div>

JS

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data () {
    return {
      index: 0,
      headers: [
        {
          text: 'Name',
          align: 'start',
          sortable: true,
          value: 'name',
        },
        { text: '1', value: 'calories', align: 'start' },
        { text: '2', value: 'fat', align: 'start' },
        { text: '3', value: 'carbs', align: 'start' },
        { text: '4', value: 'protein', align: 'start' },
      ],
      desserts: [
        {
          name: 'Frozen Yogurt',
          cols:[0,0,0,0]
        },
        {
          name: 'Ice cream sandwich',
          cols:[0,0,0,0]
        },
        {
          name: 'Eclair',
          cols:[0,0,0,0]
        },
        {
          name: 'Cupcake',
          cols:[0,0,0,0]
        },
        {
          name: 'Gingerbread',
          cols:[0,0,0,0]
        },
        {
          name: 'Jelly bean',
          cols:[0,0,0,0]
        },
        {
          name: 'Donut',
          cols:[0,0,0,0]
        },
        {
          name: 'KitKat',
          cols:[0,0,0,0]
        },
      ],
    }
  },
  mounted(){
    this.index = this.headers.length
  },
  methods:{
    add(){
      this.desserts.forEach(item => { item.cols.push(0) })
      this.headers.push({text:this.index, value:this.index})
      this.index++
    },
    remove(){
      let header = this.headers.pop()
      this.desserts.forEach(item => { item.cols.pop() })
      this.index--
    },

  }
})
Marcelo Fonseca
  • 1,705
  • 2
  • 19
  • 38
  • How is this relevant to the question? – trainoasis Jun 17 '21 at 10:01
  • @trainoasis i'ts a working example for dynamic resizing table columns. You can use this example together with a vuetify CRUD table example and change both columns and rows dynamically. In other words. Dynamically building a table using vuetifyJS data table – Marcelo Fonseca Jun 17 '21 at 15:57
  • 1
    By dynamically building a table I mean having various components and items modified for each column and reuising this parent-component with a v-data-table dynamically. – trainoasis Jun 18 '21 at 05:51