Problem using Vuetify datatables + single selection prop with template - javascript

Morning!
I'm trying to use the prop 'single-select' in a Vuetify data-table, but I'm facing a problem that I don't know if it's an issue.
Right now, I use a template for the body of the table:
<v-data-table :headers="headers" :items="proveedoresByString"
:single-select="singleSelect" show-select v-model="itemSelected">
<template v-slot:body="{ items }">
<tbody>
<tr v-for="item in items" :key="item.id">
<!--<td><v-checkbox v-model="itemSelected" :value="item"></v-checkbox></td>-->
<td class="text-xs-center">{{ item.code }}</td>
<td class="text-xs-center">{{ item.name }}</td>
<td class="text-xs-center">{{ item.movil }}</td>
<td class="text-xs-center">{{ item.phone }}</td>
<td class="text-xs-center">{{ item.email }}</td>
<td class="text-xs-center" v-if="!item.emailList"
v-on="on">{{ item.accesoApp === '0' ? 'No' : 'Yes('+ item.appAccess + ')' }}</td>
<v-tooltip max-width="320px" bottom v-if="item.emailList">
<template v-slot:activator="{ on }">
<td class="text-xs-center" v-on="on">{{ item.appAccess === '0' ? 'No' : 'Si ('+ item.accesoApp + ')' }}</td>
</template>
<span>{{ item.emailList }}</span>
</v-tooltip>
</tr>
</tbody>
</template>
</v-data-table>
Right here, the prop doesn't work, and the piece of code that contains the v-checkbox element doesn't work as a single selector, it selects every row you click.
So, If someone can help to make the single selection prop work it would be awesome or a clue about how to make the checkbox works as a single selector would be nice too.
Thanks!
EDIT: The script part:
<script>
import { mapActions } from 'vuex'
import rulesMixin from '../../generic/rules'
export default {
mixins: [rulesMixin],
data () {
return {
singleSelect: true,
itemSelected: [],
headers: [
{ text: this.$t('m_codigo'), value: 'code', sortable: true, align: 'left' },
{ text: this.$t('m_nombre'), value: 'name', sortable: true, align: 'left' },
{ text: this.$t('m_movil'), value: 'movil', sortable: true, align: 'left' },
{ text: this.$t('m_telefono'), value: 'phone', sortable: true, align: 'left' },
{ text: this.$t('m_email'), value: 'email', align: 'left' },
{ text: this.$t('m_acceso_app'), value: 'accesoApp', align: 'left' }
]
}
}
</script>

Related

Toggle Hide/Show element VueJS?

I am using a Vuetify data table with header and item slots. Now i have a button which by default should be hidden but i only want to show it when a row is selected or when all of them are selected. I assumed i should be able to use the selected data property but that seems to stay empty if i select one row. So not sure how i can hide/show the button.
This is a working pen.
This is the code:-
new Vue({
el: "#app",
data: () => ({
pagination: {
sortBy: "name"
},
selected: [],
headers: [{
text: "Dessert (100g serving)",
align: "left",
value: "name"
},
{
text: "Calories",
value: "calories"
},
{
text: "Fat (g)",
value: "fat"
}
],
desserts: [{
name: "Frozen Yogurt",
calories: 159,
fat: 6.0
},
{
name: "Ice cream sandwich",
calories: 237,
fat: 9.0
}
]
}),
methods: {
toggleAll() {
if (this.selected.length) this.selected = [];
else this.selected = this.desserts.slice();
},
changeSort(column) {
if (this.pagination.sortBy === column) {
this.pagination.descending = !this.pagination.descending;
} else {
this.pagination.sortBy = column;
this.pagination.descending = false;
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<div id="app">
<v-app id="inspire">
<v-data-table v-model="selected" :headers="headers" :items="desserts" :pagination.sync="pagination" select-all item-key="name" class="elevation-1">
<template v-slot:headers="props">
<tr>
<th>
<v-checkbox :input-value="props.all" :indeterminate="props.indeterminate" primary hide-details #click.stop="toggleAll"></v-checkbox>
</th>
<th v-for="header in props.headers" :key="header.text" #click="changeSort(header.value)">
<v-icon small>arrow_upward</v-icon>
{{ header.text }}
</th>
</tr>
</template>
<template v-slot:items="props">
<tr :active="props.selected" #click="props.selected = !props.selected">
<td>
<v-checkbox :input-value="props.selected" primary hide-details></v-checkbox>
</td>
<td class="text-xs-center">{{ props.item.name }}</td>
<td class="text-xs-center">{{ props.item.calories }}</td>
<td class="text-xs-center">{{ props.item.fat }}</td>
</tr>
</template>
</v-data-table>
<v-container>
<v-layout>
<v-flex xs6 class="mt-5">
<v-btn>Hide by default but show on selected</v-btn>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
Any help will be appreciated. Thank you.
you can use selected.length.
here is a computed you can add
computed: {
showBtn() {
return this.selected.length > 0
}
},
then use showBtn in your template
<v-btn v-if="showBtn">Hide by default but show on selected</v-btn>
You can also just use it inline, but I prefer using computed, because they cache the value, and make the template more readable
new Vue({
el: "#app",
data: () => ({
pagination: {
sortBy: "name"
},
selected: [],
headers: [
{
text: "Dessert (100g serving)",
align: "left",
value: "name"
},
{ text: "Calories", value: "calories" },
{ text: "Fat (g)", value: "fat" }
],
desserts: [
{
name: "Frozen Yogurt",
calories: 159,
fat: 6.0
},
{
name: "Ice cream sandwich",
calories: 237,
fat: 9.0
}
]
}),
computed: {
showBtn() {
return this.selected.length > 0
}
},
methods: {
toggleAll() {
if (this.selected.length) this.selected = [];
else this.selected = this.desserts.slice();
},
changeSort(column) {
if (this.pagination.sortBy === column) {
this.pagination.descending = !this.pagination.descending;
} else {
this.pagination.sortBy = column;
this.pagination.descending = false;
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire">
<v-data-table v-model="selected" :headers="headers" :items="desserts" :pagination.sync="pagination" select-all item-key="name" class="elevation-1">
<template v-slot:headers="props">
<tr>
<th>
<v-checkbox :input-value="props.all" :indeterminate="props.indeterminate" primary hide-details #click.stop="toggleAll"></v-checkbox>
</th>
<th v-for="header in props.headers" :key="header.text" :class="['column sortable', pagination.descending ? 'desc' : 'asc', header.value === pagination.sortBy ? 'active' : '']" #click="changeSort(header.value)">
<v-icon small>arrow_upward</v-icon>
{{ header.text }}
</th>
</tr>
</template>
<template v-slot:items="props">
<tr :active="props.selected" #click="props.selected = !props.selected">
<td>
<v-checkbox :input-value="props.selected" primary hide-details></v-checkbox>
</td>
<td class="text-xs-center">{{ props.item.name }}</td>
<td class="text-xs-center">{{ props.item.calories }}</td>
<td class="text-xs-center">{{ props.item.fat }}</td>
</tr>
</template>
</v-data-table>
<v-container>
<v-container>
<v-layout>
<v-flex xs6 class="mt-5">
<v-btn v-if="showBtn">Hide by default but show on selected</v-btn>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>

Using watchers in VueJS?

I am trying to use Watchers in VueJS but having bit of a hard time wrapping my head around them. For example in this case i have set up a watch which checks for the tab i am on and if the tab changes it should reset the selected value back to an empty array. Now i have set the parameters as oldValue and newValue but i am not quite sure how will i use those.
Check this codepen.
Here is the Complete example:-
new Vue({
el: "#app",
data() {
return {
tabs: ["Tab1", "Tab2"],
activeTab: 0,
headers: [{
text: "Dessert (100g serving)",
align: "left",
value: "name"
},
{
text: "Calories",
value: "calories"
}
],
items: [{
name: "Ice cream sandwich",
calories: 237
},
{
name: "Frozen Yogurt",
calories: 159
}
],
selected: []
};
},
methods: {
toggleAll() {
if (this.selected.length) this.items = [];
else this.selected = this.items.slice();
}
},
watch: {
activeTab: (oldValue, newValue) => {
if (oldValue !== newValue) {
this.selected = [];
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<div id="app">
<v-app id="inspire">
<v-tabs fixed-tabs v-model="activeTab">
<v-tab v-for="tab in tabs" :key="tab">
{{ tab }}
</v-tab>
<v-tab-item v-for="tab in tabs" :key="tab">
<v-data-table v-model="selected" :headers="headers" :items="items" select-all item-key="name" class="elevation-1" hide-actions>
<template v-slot:headers="props">
<tr>
<th>
<v-checkbox :input-value="props.all" :indeterminate="props.indeterminate" primary hide-details #click.stop="toggleAll"></v-checkbox>
</th>
<th v-for="header in props.headers" :key="header.text">
<v-icon small>arrow_upward</v-icon>
{{ header.text }}
</th>
</tr>
</template>
<template v-slot:items="props">
<tr :active="props.selected" #click="props.selected = !props.selected">
<td>
<v-checkbox :input-value="props.selected" primary hide-details></v-checkbox>
</td>
<td>{{ props.item.name }}</td>
<td>{{ props.item.calories }}</td>
</tr>
</template>
</v-data-table>
</v-tab-item>
</v-tabs>
</v-app>
</div>
I am not quite sure how i will use that watch so if someone can help me with that, i sure would appreciate it. Thank you.
You're using a fat arrow function for activeTab, it needs to be a normal function or it doesn't have a proper reference to this
Change to this:
activeTab: function (oldValue, newValue) { // function instead of =>
if (oldValue !== newValue) {
this.selected = [];
}
}
Also, there's an issue with your code when you use the check all box.
As a good rule of thumb, all top level functions should be declared using function. Only use => within the functions themselves when a nested function needs access to this

How to show and hide rows in v-data-table Vue.js + Vuetify

I'm new at Vue developer and I make a V-Data-Table consuming a JSON with vue-resource. That´s done already, but now I want to show/hide child rows based on user selection.
e.g.: click on '+' and the table shows the rows, click on '-'and the table hide the rows.
<template>
<v-app>
<div>
<v-flex xs12 sm6 d-flex>
<v-select
v-if="retornoDoJson"
:items="retornoDoJson.nations"
v-model="paisSelecionado"
label="Selecione Um"
v-on:change="selectionChanged"
single-line
menu-props="bottom"
></v-select>
</v-flex>
</div>
<div v-if="paisSelecionado">
<v-data-table :headers="headers" :items="retornoDoJson.top_10s[paisSelecionado]" hide-actions>
<template slot="items" slot-scope="props">
<v-btn fab dark color="indigo">
<v-icon dark>add</v-icon>
</v-btn>
<td class="parent-row">{{ props.item.name }}</td>
<td class="child-row">{{ props.item.club }}</td>
<td class="child-row">{{ props.item.phy }}</td>
<td class="parent-row">{{ props.item.age }}</td>
<td class="parent-row">{{ props.item.pas }}</td>
<td class="child-row">{{ props.item.nationality }}</td>
</template>
</v-data-table>
</div>
</v-app>
</template>
<script>
export default {
data() {
return {
paisSelecionado:undefined,
headers: [
{
text: ' ',
align: 'left',
sortable: false,
value: 'name'
},
{ text: 'name', value: 'name' },
{ text: 'club', value: 'club' },
{ text: 'phy', value: 'phy' },
{ text: 'age', value: 'age' },
{ text: 'pas', value: 'pas' },
{ text: 'nationality', value: 'nationality' }
],
retornoDoJson: []
};
},
methods: {
loadApi() {
this.$http
.get("http://localhost:8080/data.json")
.then(this.successCallback, this.errorCallback);
},
successCallback: function(resposta) {
this.retornoDoJson = resposta.data;
console.log(this.retornoDoJson);
},
selectionChanged: function() {
console.log("selecionarItem:this.selecionarPais:", this.paisSelecionado);
}
},
mounted() {
this.loadApi();
}
};
</script>
<style>
</style>
There's any way to do that?
If I understood you correctly, I hope this helps, also you can check a working example here: https://codepen.io/anon/pen/zemZoB
<template slot="items" scope="props">
<template v-if="props.item.show">
<td>{{ props.item.name }}</td>
<td class="text-xs-right">
<v-btn #click.native="props.item.show = false">
Hide
</v-btn>
</td>
</template>
<template v-else>
<v-btn #click.native="props.item.show = true">
Show
</v-btn>
</template>
</template>

Create dynamic table component Vue JS

I'm learning Vue JS. I want to create a component to show data with table that I can reuse in other components. I have 2 props items(data) and columns(column name)
items: [
{
'id':'1',
'title': 'hello',
'description': 'ok ok',
'created_date': '2018-09-09'
},
{
'id':'2',
'title': 'hello 2',
'description': 'ok ok 2',
'created_date': '2018-10-09'
}
],
columns: [ 'id', 'title', 'description', 'created_date']
I want to create a table as bellow
<table class="table">
<thead>
<tr>
<th v-for="(column, index) in columns" :key="index"> {{column}}</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in items" :key="index">
<td v-for="(column, indexColumn) in columns" :key="indexColumn">{{item.$column}}</td>
</tr>
</tbody>
I don't know how to get value from item by column name. Is it possible to create a table like this?
Everything is fine just change the value in the template item.$column to item[column]
<table class="table">
<thead>
<tr>
<th v-for="(column, index) in columns" :key="index"> {{column}}</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in items" :key="index">
<td v-for="(column, indexColumn) in columns" :key="indexColumn">{{item[column]}}</td>
</tr>
</tbody>
</table>
TABLE COMPONENT WITH DYNAMIC SLOTS
I've created a a simple component for dynamic table with slot option.
<template>
<div id="appTable">
<table>
<thead>
<tr>
<th v-for="(header, i) in headers" :key="i">
{{ header }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, i) of data" :key="i">
<td v-for="(td, j) of slotsProceced" :key="i + '-' + j">
<slot v-if="td.hasSlot" :name="td.slotName" :item="item" />
<span v-else>{{ item[td.key] }}</span>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'AppTable',
props: {
headers: {
type: Object,
required: true
},
data: {
type: Array,
required: true
}
},
setup(props, { slots }) {
const slotsProceced = [];
for (const key in props.headers) {
if (Object.prototype.hasOwnProperty.call(props.headers, key)) {
const slotName = 'item-' + key;
slotsProceced.push({
key: key,
hasSlot: !!slots[slotName],
slotName: slotName
});
}
}
return { slotsProceced };
}
});
</script>
The props to call:
const headTable = {
date: "Date",
price: "Price",
status: "Status",
action: "Action",
};
const dataTable = [
{
date: "2021-02-03 22:04:16",
price: "$32.000",
status: 1,
},
];
To call it:
<AppTable :headers="headTable" :data="dataTable">
<template v-slot:item-action>
<span>Some Action</span>
</template>
<template v-slot:item-status="{ item }">
<span>{{ item.status === 1 ? 'Active' : 'Inactive' }}</span>
</template>
</AppTable>

Accessing array inside of another array to use v-for

I have an array - 'items' of objects and each of them contains an array of more objects.
I would like to access the second array 'productPrices' to use v-for. But items.productPrices doesn't work for me. Should I create double loop somehow?
HTML:
<table>
<tbody>
<tr v-for="(element, index) in items.productPrices">
<td>{{ element.name }}</td>
<td>
<span>{{ element.amount }}</span>
</td>
<td>
{{ element.price }}
</td>
</tr>
</tbody>
</table>
JS:
new Vue({
el: "#app",
data: {
items: [
{
name: 'menu',
path: 'menu',
productPrices: [
{ amount: 100, price: 80.61, name: 'first' },
{ amount: 250, price: 72.10 },
{ amount: 500, price: 79.62 },
{ amount: 1000, price: 100.20 },
{ amount: 2500, price: 147.60 },
{ amount: 5000, price: 232.56 }
],
quantity: 0
},
{
name: 'Etui',
path: 'etui',
productPrices: [
{ amount: 100, price: 80.61, name: 'first' },
{ amount: 250, price: 72.10 },
{ amount: 500, price: 79.62 },
{ amount: 1000, price: 100.20 },
{ amount: 2500, price: 147.60 },
{ amount: 5000, price: 232.56 }
],
quantity: 0
},
]
}
})
Here is a fiddle.
While you can do this with <template> ... </template>
as the other two people have answered, if you wish to not nest another loop you can flatten all of the data into one array to be used, it won't be as pretty unless you move it to a function or the like.
Here's how:
<div id="app">
<table>
<tbody>
<!-- <tr v-for="(element, index) in items.map(item => item.productPrices).reduce((joinedArrays, currentArray) => joinedArrays.concat(currentArray), [])"> -->
<tr v-for='(element, index) in currentItem().productPrices'>
<td>{{ element.name }}</td>
<td>
<span>{{ element.amount }}</span>
</td>
<td>
{{ element.price }}
</td>
</tr>
</tbody>
</table>
just wrap it inside a <template v-for="item in items">
<div id="app">
<table>
<tbody>
<template v-for="item in items">
<tr v-for="(element, index) in item.productPrices">
<td>{{ element.name }}</td>
<td>
<span>{{ element.amount }}</span>
</td>
<td>
{{ element.price }}
</td>
</tr>
</template>
</tbody>
</table>
</div>
update on your jsfiddle http://jsfiddle.net/khz30amb/7/
You can do this by following way:-
<template>
<v-container fluid px-4 py-0>
<h4>Test</h4>
<table>
<tbody>
<template v-for="(element, index) in items">
<tr v-for="newElement in element.productPrices" :key="index">
<td>{{ newElement.name }}</td>
<td>
<span>{{ newElement.amount }}</span>
</td>
<td>
{{ newElement.price }}
</td>
</tr>
</template>
</tbody>
</table>
</v-container>
</template>
since in javascript you cant directly access the properties of array of objects for example if you have a case as above :-
where you have a array of objects that is list and you need to access the property product prices you can't just access it like list.productPrices you will first need to iterate over the higher order objects and then only you will have access to the property..
so what you will need to do is first use v-for over the list and then you can access the property productPrices and then you can iterate over it using v-for..
Hope this answers your question and doubts.

Categories

Resources