Change vuetify simple-table to data-table - vue.js

I have this simple table in Vuetify, without headers and only one column. How can I change it to vuetify v-data-table?
<v-simple-table>
<thead />
<tbody>
<tr
v-for="item in categories"
:key="item"
#click="handleClick"
>
<td>{{ item }}</td>
<v-switch />
</tr>
</tbody>
</v-simple-table>
categories is a simple array of strings. I want to change it to data-table in order to nicely handle clicking and selecting rows.

Check this codesandbox I made: https://codesandbox.io/s/stack-71617004-simple-to-v-data-table-bm2yn1?file=/src/components/Example.vue
Using body slot
You can use the body slot of the data table and use almost the same code you have in your simple table like this. This way you set up the handleClick function in the tr:
<v-data-table
:headers="headers"
:items="items"
hide-default-footer
hide-default-header
:items-per-page="-1"
:footer-props="{
itemsPerPageOptions: [-1],
}"
>
<template v-slot:body="{ items}">
<tbody>
<tr v-for="item in items" :key="item" #click="handleClick(item)">
<td align="left">{{item}}</td>
</tr>
</tbody>
</template>
</v-data-table>
Using item slot
Or you can use the item slot, like this. In this other way, the handleClick function is configured using the #click:row event of the data table.
If you try to use the item slot with your array of strings, it will work but you'll get some warnings in your console. Telling you that your data-table item slot expected an object and received an string. That's because v-data-table component expects to receive an array of objects.
To avoid this warning you can simply convert your array of string into a dummy array of objects using Array.prototype.map, and bind the computed property instead.
computed: {
myItemsTransformed() {
return this.items.map(item => ({ name: item }));
}
},
<v-data-table
:headers="headers"
:items="myItemsTransformed"
hide-default-footer
hide-default-header
:items-per-page="-1"
:footer-props="{
itemsPerPageOptions: [-1],
}"
#click:row="(item) => handleClick(item.name)"
>
<template #item.name="{ item }">
{{ item.name }}
</template>
</v-data-table>
Notice that in both examples I have used the props hide-default-footer, hide-default-header to hide the footer and header of the data table and also set the items-per-page to -1. To show all the items of the table and avoid the pagination.

You can change like this :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
categoryList: ['Category 1', 'Category 2', 'Category 3'],
}),
computed: {
categoriesHeader() {
return [
{ text: "Name", value: "name" }
];
},
},
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.5.0/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app id="a">
<v-data-table :headers="categoriesHeader" :items="categoryList" item-key="id" class="elevation-1">
<template v-slot:[`item.name`]="{ item }">
{{ item }}
</template>
</v-data-table>
</v-app>
</div>

Related

Change v-data-table's header's color

I'd like to change the color of the header in my v-data-table but didn't find any way in the Vuetify documentation. Does anyone know how to do it ?
I can change the rest of the colors on the table but no the header...
<v-card-text>
<v-data-table
dark
:footer-props="{ 'items-per-page-options': [10, 25, -1] }"
dense
calculate-widths
fixed-header
height="498"
:headers="headers"
:items="res"
sort-by="publicationDate"
:sortDesc="sortVal"
>
<template #item.video="{ item }">
<a
target="_blank"
v-if="item.video != ''"
class="links1 video-icon"
:href="item.video"
>
<v-btn dark icon>
<v-icon class="ic1">mdi-movie</v-icon>
</v-btn>
</a>
</template>
<template #item.title2="{ item }">
<!-- NEWS column -->
<a
target="_blank"
v-if="item.file != ''"
class="links1"
:href="item.file"
>
<span style="color:white"> {{ item.title }} </span>
</a>
</template>
</v-data-table>
</v-card-text>
You can achieve this by hide default header by adding hide-default-header attribute in <v-data-table> element and then create custom header by using v-slot.
<template v-slot:header="{ props: { headers } }">
<thead>
<tr>
<th v-for="h in headers" :class="h.class">
<span>{{h.text}}</span>
</th>
</tr>
</thead>
</template>
In headers array, add class property in each object which will contain the class name.
Sample structure of headers array :
headers: [
{ text: 'Title', value: 'title', class: 'my-header-style' }
...
...
]
Finally, In CSS you can just add the style to my-header-style class.
.my-header-style {
background: #666fff;
}
Live Demo :
new Vue({
el: '#app',
data: () => ({
headers: [
{ text: 'ID', value: 'id', class: 'my-header-style' },
{ text: 'Name', value: 'name', class: 'my-header-style' },
{ text: 'Age', value: 'age', class: 'my-header-style' }
],
movies: []
})
})
.my-header-style {
background: #666fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.2.5/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vuetify#1.2.5/dist/vuetify.min.css"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"/>
<div id="app">
<v-app id="inspire">
<v-data-table
:headers="headers"
:items="items"
hide-default-header>
<template v-slot:header="{ props: { headers } }">
<thead>
<tr>
<th v-for="h in headers" :class="h.class">
<span>{{h.text}}</span>
</th>
</tr>
</thead>
</template>
</v-data-table>
</v-app>
</div>
One option to modify the default Vuetify CSS is to target the inner elements/sub-components using deep selectors. Target the root node of the component, in this case you can select the v-data-table classname, then deep select the classname on the header sub-component:
<style scoped>
.v-data-table >>> .v-data-table-header {
background-color: red !important;
}
</style>
or if not using scoped styling, you can just target the desired class
<style>
.v-data-table-header {
background-color: red !important;
}
</style>
codesandbox example
Also note that if you're using Sass you may need to use ::v-deep or /deep/ instead of >>>

Dynamic replacement of URL text with a hyperlink

I have a v-data-table component which cells are filled with text containing URLs: "Text with URL https://stackoverflow.com that I'd like to replace with hyperlink"
How can I dynamically replace all these URLs inside text with a-tags? The rest of the text except the URLs should remain unchanged.
<template>
<v-data-table
:headers="headers"
:items="items"
:single-expand="singleExpand"
:expanded.sync="expanded"
item-key="id"
show-expand
>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">
{{ item.text }}
</td>
</template>
</v-data-table>
</template>
<script>
data: () => ({
singleExpand: false,
expanded: [],
headers: [],
}),
methods: {
urlify(text) {
const urlRegex = /(https?:\/\/[^\s]+)/g;
return text.replace(urlRegex, '$1');
},
},
</script>
In order to output real HTML, you will need to use the v-html directive.
You already have the urlify(text) method in your script section to convert text to URL. A simple use case of this directive with a method is:
<td :colspan="headers.length" v-html="urlify(item.text)">
</td>
For more information, you can read the official documentation.

Rest API to v-data-table using v-select but v-data-table can't show so I use v-for to

Purpose:
1.Rest API >>> by axios
2.Select category >>> by v-select
3.Show table >>> by v-data-table but i can't show table so I use v-for to show but I got this
here what I got
it show many table (according to the number of data sets)
Here is my code
HTML Part
<template>
<v-container>
<h1>JSON Data</h1>
<span>Data Test Table For Current Sensor Table</span>
<!-- Choose -->
<div id="top">
<!-- left-right -->
<v-row align-center>
<!-- title -->
<v-col cols="5">
<v-subheader>Choose Generation: {{ selected }}</v-subheader>
</v-col>
<!-- dropdown -->
<v-col cols="7">
<v-select :items="items" v-model="selected" dense></v-select>
</v-col>
</v-row>
</div>
<!-- Button -->
<v-btn block v-on:click="getData()">Load Data</v-btn>
<div v-for="(item, i) in data" :key="i" >
<v-data-table :items="data" :headers="headers" :items-per-page="5">
<template slot="data" slot-scope="props">
<td>{{ props.data.userId }}</td>
<td>{{ props.data.id }}</td>
<td>{{ props.data.title }}</td>
</template>
</v-data-table>
</div>
<!-- Test -->
<span class="data_id">***{{ data }} </span><br />
</v-container>
</template>
Script Part
<script>
import axios from "axios";
export default {
name: "Current",
data: () => ({
items: ["albums", "todos", "posts"],
selected: "",
headers: [
{ text: "USER_ID", align: "start", sortable: false, value: "userId" },
{ text: "ID", value: "id" },
{ text: "TITLE", value: "title" },
],
data: [],
}),
methods: {
getData() {
axios
.get("https://jsonplaceholder.typicode.com/users/1/" + this.selected)
.then((response) => {
this.data = response.data;
})
.catch((err) => alert(err));
},
},
mounted() {
this.getData();
},
};
</script>
Vue&Vuetify is new to me. If you have any suggestions, please tell me.
I try to remove div <div v-for="(item, i) in data" :key="i" ></div> but when I refresh this page it gone and show only empty table
show only empty table
Please remove v-for.
Because v-data-table displays table for data you passed, it will display same table as much as count of items in the data.
https://prnt.sc/1nsxc7w

vue.js how do I make a v-slot template dynamic?

Hello and thank you for reading my question! I am working with Vue.js, Vuetify and v-data-table and I am working on making my v-slot work with two different strings as the name of the header.
<template>
<v-container fluid>
<v-data-table
:options="data"
:headers="headers"
:items="data
:server-items-length="40"
:loading="loading"
:multi-sort="true"
#update:options="updateThePage"
>
<template v-slot:[`header.overalls`]="{ header }" class="flight-date-header">
<overallDatePicker
:options="data"
/>
{{ header.name }}
</template>
<template v-slot:[`item.name`]="{ item }">
<p class="company-name">
{{ item.companyName }}
</p>
</template>
<template v-slot:[`item.price`]="{ item }">
<p> {{ item.price }} </p>
</template>
<template v-slot:[`item.flightDate`]="{ item }">
<p class="style">
{{ item.startDate }}
</p>
</template>
</v-data-table>
</v-container>
</template>
and I store my headers like below
headers: [
{ text: 'Campaign Name', value: 'overalls' },
],
Ideally I would like the name of this slot
<template v-slot:[`header.overalls`]="{ header }" class="flight-date-header">
<overallDatePicker
:options="data"
/>
</template>
To work with two different data options. right now the name of the header is overalls but I want the name of the header so ['header.overalls'] to be like ['header.('overalls' || 'shoes')] ,
The reason I am doing this is right now when I click on the header the options.sortBy property of the table gets set to 'overalls', but I want the icon on the column to show up if the options.sortBy property of the table is "overalls" and also show up if it is "shoes"
Please help and thank you so much!
To reuse the slot content for multiple headers/columns, use Vue's dynamic slots names feature + v-for
data() {
return {
specialHeaders: ['overalls', 'shoes'],
}
}
<template v-for="column in specialHeaders" v-slot:[`header.${column}`]="{ header }">
Special:{{header.text}}
</template>

Condition on template with v-if using v-slot prop

I'm trying to make a condition to enable a named slot like this:
<template v-slot:item="{ item }" v-if="item.loading">
<v-progress-circular indeterminate color="primary"></v-progress-circular>
</template>
My use case is a Vuetify datatable: each item has a "loading" property, and I'd like to activate "item" slot only if the row is loading ("item" slot is Slot to replace the default rendering of a row)
The error is that item is undefined in the v-if, which seems logic : item is only defined for template children tag.
Is there a way to solve this problem?
You can filter the items that you pass to the datatable with a computed property.
Can you just not swap element based on loading ?
Vue.config.devtools = false;
Vue.config.productionTip = false;
var app = new Vue({
el: '#app',
data: {
items: [{data : "", loading: true}, {data : "Some data", loading: false}]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="item in items">
<div>
<div v-if="item.loading">
Loading...
</div>
<div v-else>
{{item.data}}
</div>
</div>
</div>
</div>
I had a similar problem, and I solved it in Vuetify 2 by importing VDataTable/Row as 'v-data-table-row', and using it to render 'regular' table rows, and for custom rows I used my own template.
JavaScript
import Row from 'vuetify/lib/components/VDataTable/Row.js';
export default {
components: { 'v-data-table-row': Row },
data() {
return {
currentItemName: 'Ice cream sandwich'
}
}
// headers, items, etc...
}
HTML
<template v-slot:item="{ item }">
<tr v-if="item.name == currentItemName" class="blue-grey lighten-4">
<td>Custom prefix - {{ item.name }}</td>
<td colspan="2">{{ item.calories }} - Custom suffix</td>
</tr>
<v-data-table-row v-else :headers="headers" :item="item">
<template
v-for="(index, name) in $scopedSlots"
v-slot:[name.substr(5)]="data"
>
<slot
v-if="name.substr(0, 5) === 'item.'"
:name="name"
v-bind="data"
></slot>
</template>
</v-data-table-row> </template
You can check out working example here.
You can just put the v-if on the child element
<template #item="{ item }">
<v-progress-circular
v-if="item.loading"
color="primary"
indeterminate
></v-progress-circular>
</template>