handle promises in Vuetify data tables - vue.js

I have a problem when trying to insert the response of an API call (I am using axios) into a vue data table. If I manually write an html table like this:
<table style="width:100%">
<tr>
<th v-for="(item, n) in headers" v-bind:key="n">{{item.text}}</th>
</tr>
<tr v-for="(item, n) in info" v-bind:key="n">
<td>{{ item.code }}</td>
<td>{{ item.symbol }}</td>
<td>{{ item.rate }}</td>
<td>{{ item.description }}</td>
<td>{{ item.rate_float }}</td>
</tr>
</table>
the code works and displays some data.
But when I try to use the Vuetify data table like this:
<v-data-table
app
:headers="headers"
:items="info"
class="elevation-2"
>
<template v-slot:items="props">
<td>{{ props.item.code }}</td>
<td>{{ props.item.symbol }}</td>
<td>{{ props.item.rate }}</td>
<td>{{ props.item.description }}</td>
<td>{{ props.item.rate_float }}</td>
</template>
</v-data-table>
it gives me the following warning:
webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:620 [Vue warn]: Invalid prop: type check failed for prop "items". Expected Array, got Object
found in
---> <VDataTable>
<ApiExample> at src/views/ApiExample.vue
<VContent>
<VApp>
<App> at src/App.vue
<Root>
Leading to this error:
webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1887 TypeError: this.items.map is not a function
at VueComponent.items (webpack-internal:///./node_modules/vuetify/dist/vuetify.js:21511)
at Watcher.run (webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:4556)
at flushSchedulerQueue (webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:4298)
at Array.eval (webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1979)
at flushCallbacks (webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1905)
The application works if instead of trying to diplay the dynamic data I try to display a statically defined object ("staticitems"). In my opinion the problem is that the data table is expecting an array but instead it finds a Promise so it crashes. Below is the full code for the application .
<template>
<v-container fluid>
<table style="width:100%">
<tr>
<th v-for="(item, n) in headers" v-bind:key="n">{{item.text}}</th>
</tr>
<tr v-for="(item, n) in info" v-bind:key="n">
<td>{{ item.code }}</td>
<td>{{ item.symbol }}</td>
<td>{{ item.rate }}</td>
<td>{{ item.description }}</td>
<td>{{ item.rate_float }}</td>
</tr>
</table>
<v-data-table
app
:headers="headers"
:items="info"
class="elevation-2"
>
<template v-slot:items="props">
<td>{{ props.item.code }}</td>
<td>{{ props.item.symbol }}</td>
<td>{{ props.item.rate }}</td>
<td>{{ props.item.description }}</td>
<td>{{ props.item.rate_float }}</td>
</template>
</v-data-table>
</v-container>
</template>
<script>
import axios from 'axios'
export default {
name: "ApiExample",
data() {
return {
info: [],
info2: [],
headers: [
{text: 'code', value: 'code'},
{text: 'symbol', value: 'symbol'},
{text: 'rate', value: 'rate'},
{text: 'description', value: 'description'},
{text: 'rate_float', value: 'rate_float'},
],
staticitems: [
{
code: "USD",
symbol: "$",
rate: "5,247.0417",
description: "United States Dollar",
rate_float: "5247.0417"
}
]
}
},
mounted() {
axios
.get('https://api.coindesk.com/v1/bpi/currentprice.json')
.then(response => (this.info = response.data.bpi))
.catch(error => console.error(error))
}
}
</script>
<style scoped>
</style>

type check failed for prop "items". Expected Array, got Object.
It is what it means. Datatable is fine with using data returned from promises as long as the type of response is an array. Your promise is returning an object.
The application works if instead of trying to diplay the dynamic data I try to display a statically defined object ("staticitems").
staticitems is not an object. It is an array of object. Arrays are displayed like this: [] and objects like {}
You need to make sure that the response coming back from an API is an array. I created a simple example from your API call to show you the right format of your response. As you can see info is now an array and the datatable works fine.

Related

Why Nuxt 3 Not Updating Realtime Upon Data Change on Props?

i have this problem in which the HTML page is not updating upon props variables change. Please help me.
I make a table in my child element based on the array passed by the parent, here is the child code:
<table>
<thead>
<th>Rank</th>
<th>Nama</th>
<th>Blank</th>
<th>Error</th>
<th>Warning</th>
<th>Clean</th>
<th>Total</th>
</thead>
<tbody>
<tr v-for="(datae, index) in props.ranking" :key="index">
<td>{{ index }}</td>
<td>{{ '(' + datae.username + ') ' + datae.realname }}</td>
<td>{{ datae.blank }}</td>
<td>{{ datae.error }}</td>
<td>{{ datae.warning }}</td>
<td>{{ datae.clean }}</td>
<td>{{ datae.total }}</td>
</tr>
</tbody>
</table>
Here is the child script:
<script setup lang="ts">
var props = defineProps({
ranking: {
type: Array,
required: false,
}
})
watch(() => props.ranking, (newValue, oldValue) => console.log('tes'))
</script>
I am expecting the html table update upon props.ranking change
I noticed a couple of issues here. It might help you.
First of all, you used the props keyword in the template, it's not needed.
<!-- props.ranking -> ranking -->
<tr v-for="(datae, index) in ranking" :key="index">
<td>{{ index }}</td>
<td>{{ '(' + datae.username + ') ' + datae.realname }}</td>
<td>{{ datae.blank }}</td>
<td>{{ datae.error }}</td>
<td>{{ datae.warning }}</td>
<td>{{ datae.clean }}</td>
<td>{{ datae.total }}</td>
</tr>
The second point is that you watch an array prop, but it isn't changed, only the properties (items) of this array will be changed. So your watcher callback never fires. To fix this, you can pass the deep option to watch as a third argument, like this:
watch(() => props.ranking,
(newValue, oldValue) => console.log('tes'),
{ deep: true }
)
Source: watch arrays

How to get rid of the Brackets from fetched Nested Array

Good day to you guys.
May I seek your help.
I'm stuck figuring out on how to get rid of the Brackets and Separate each data from my nested array. Here's my code:
<template>
<v-data-table
:headers="headers"
:items="teams"
>
<template v-slot:body="{ items }">
<tbody>
<tr v-for="item in items" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.business_Units }}</td>
<td>
<v-icon small class="mr-2" #click="editRole(item)">
mdi-pencil
</v-icon>
</td>
<td>{{ item.status | boolean }}</td>
</tr>
</tbody>
</template>
</v-data-table>
</template>
<script>
export default {
//..... chunk of my code on how to request from the api
created() {
SparrowService.getTeams()
.then((response) => {
this.loading = false;
this.teams = response.data;
})
.catch((error) => {
console.log(error.response);
});
},
};
</script>
item.business_Units is the array, so you need to take some action to join to the string.
Refer to Array.join()
...
<td>{{ item.business_Units.join(", ") }}</td>
...

Target a specific cell background color in a v-for loop

With vuejs I fill a table with some data with this code :
<tr v-for="droit in listedroit">
<td>{{ droit.id_u }}</td>
<td>{{ droit.role }}</td>
<td>{{ droit.id_e }}</td>
<td>{{ droit.droits }}</td>
<td v-bind:style="{ 'background-color': statusColor }">STATUS</td>
</tr>
statusColor is computed in my app.js and returned to the template.
Some rows need a red cell, others a green one (I check if the rights are RO(green) or RW(red)).
The problem is I failed to target a specific cell of a row, and if I set statusColor to 'red', the whole column is red.
How can I achieve this ?
Thanks a lot for your help.
You can use a method instead of a computed property
<template>
<tr v-for="droit in listedroit">
<td>{{ droit.id_u }}</td>
<td>{{ droit.role }}</td>
<td>{{ droit.id_e }}</td>
<td>{{ droit.droits }}</td>
<td v-bind:style="{ 'background-color': statusColor(droit.rights) }">STATUS</td>
</tr>
</template>
export default{
methods: {
statusColor(rights){
if(rights === 'RO'){
return 'green';
} else {
return 'red';
}
}
}
}

How can I add a click event to the v-data-table?

I want to call the editItem function when the table row is clicked. Current what happens is I click on the table row and it doesn't display the details page. Yet when I put this click event on a particular table data the editItem function gets called. How can I make this same function to be called on the table row itself?
Here in my code I have tried using the the v-data-table template and slot and included the click event on the row but it is not working either
<template slot="items" slot-scope="props">
<tr #click="editItem(item), selected = item.id">
<td>{{ props.item.member }}</td>
<td>{{ props.item[1] }}</td>
<td>{{ props.item[2] }}</td>
<td>{{ props.item[3] }}</td>
<td>{{ props.item[4] }}</td>
<td>{{ props.item[5] }}</td>
<td>{{ props.item[6] }}</td>
<td>{{ props.item[7] }}</td>
<td>{{ props.item[8] }}</td>
<td>{{ props.item[9] }}</td>
<td>{{ props.item[10] }}</td>
<td>{{ props.item[11] }}</td>
<td>{{ props.item[12] }}</td>
<td>{{ props.item[13] }}</td>
</tr>
</template>
I expect that when the row is clicked, the editItem function is called
I found a way around it using #click:row
<v-data-table :headers="headers" :items="desserts" :items-per-page="5"
class="elevation-1" #click:row="handleClick">
</v-data-table>
The handleClick function returns a value of the clicked item so I call the method I want to act upon the value within the handleClick method. I also manually highlighted the clicked row since I didn't find a Vuetify way of doing so.
An example of the handleClick method is here:
handleClick(value) {
this.highlightClickedRow(value);
this.viewDetails(value);
},
You can also access the clicked row using event.target. Example is here
highlightClickedRow(value) {
const tr = value.target.parentNode;
tr.classList.add('highlight');
},
I got another solution with highlighting.
Because Anjayluh's idea was not working for me.
in Vuetify 2.0.0
template
<v-data-table
:headers="headers"
:items="desserts"
:class="[item.selected && 'selected']"
#click:row="handleClick"
/>
...
</v-data-table>
and the script method
handleClick(row) {
// set active row and deselect others
this.desserts.map((item, index) => {
item.selected = item === row
this.$set(this.desserts, index, item)
})
// or just do something with your current clicked row item data
console.log(row.sugar)
},
and a style
.selected {
background-color: red
}
This solutions is the best for, maybe help you, it's work with vuetify 2.x.
{
// inside Vue Component
methods: {
openLink(link) {
console.log(`opened link ${link}`)
}
}
}
<v-data-table
:options="{ openLink }"
>
<template v-slot:body="{ items, options }">
<tbody>
<tr
v-for="item in items"
:key="item.id"
>
<td>
<button #click="() => options.openLink(item)" />
</td>
</tr>
</tbody>
</template>
</v-data-table>
Look: Data Table Api - slots.body it is for me, the best approachable to solve this problem.

How to add class name using Vue.js to a table column which is part of a Vue.js Template? [duplicate]

This question already has answers here:
Difference between v-bind and {{}}?
(3 answers)
How can I solve "Interpolation inside attributes has been removed. Use v-bind or the colon shorthand"? Vue.js 2
(6 answers)
Closed 5 years ago.
I need to assign class name from my visitor object (visitor.iso_code) to a table column. However Vue.js will not allow me to do it this way. I've tried v-bind:class and :class and neither of them work. Any help would be appreciated. Thank you in advance.
<template lang="html">
<div>
<table id="vue_generated_visitors_table" class="table">
</table>
<tbody>
<tr v-for="visitor in visitors_table">
<th scope="row">{{ visitor.id }}</th>
<td>{{ visitor.created_at }}</td>
<td><img src="images/blank.png" class="{{visitor.iso_code}}" /> {{ visitor.country }}</td>
<td>{{ visitor.city }}</td>
<td>{{ visitor.state_name }}</td>
<td>{{ visitor.postal_code }}</td>
<td>{{ visitor.lat }}</td>
<td>{{ visitor.lon }}</td>
<td>{{ visitor.timezone }}</td>
<td>{{ visitor.currency }}</td>
<td>{{ visitor.userAgent }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script type="text/javascript">
export default {
props: ['visitors_table','method'],
data () {
return {
}
},
methods: {}
}