Why Nuxt 3 Not Updating Realtime Upon Data Change on Props? - vue.js

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

Related

Using v-for in a table

I have a table is populated with some info and I would like to format the table like the picture
Unfortunately the excel sheet which I have no control over is formatted so:
I want any row that has only a Equipment type to span whole row. All other rows should appear as normal table row.
I am using following vue template:
<table>
<caption>
SHS Scrap Table
</caption>
<thead>
<tr>
<th>Make</th>
<th>Model #</th>
<th>Bar Code</th>
<th>Serial #</th>
<th>Location</th>
<th>Condition</th>
</tr>
</thead>
<tbody v-for="item in scrapDataEmptyRowsRemoved" :key="item">
<tr v-if="item['Equipment Type']">
<td class="equipt-type" colspan="6">
Equipment Type - {{ item["Equipment Type"] }}
</td>
</tr>
<tr v-else>
<td>{{ item["Make"] }}</td>
<td>{{ item["Model #"] }}</td>
<td>{{ item["Bar Code"] }}</td>
<td>{{ item["Serial #"] }}</td>
<td>{{ item["Location"] }}</td>
<td>{{ item["Condition"] }}</td>
</tr>
</tbody>
</table>
The only problem is that looking in Devtools I see that every row has a Tbody which is not semantically correct. Any idea's on how to correct this. If I use a container around the v-if v-else all formatting breaks down.Thanks...
Update the only problem is Vite is objecting to moving :key attribute to the v-else:
I dont what other unique key they want.
Update II - Ok apparently if I use different object keys Vite is ok with that ie :key="item['Equipment Type'] and on v-else :key="item['Make']. Does that seem correct?
You can move the v-for in a template tag, that won't be rendered in the DOM.
<tbody>
<template v-for="item in scrapDataEmptyRowsRemoved" :key="item">
<tr v-if="item['Equipment Type']">
<td class="equipt-type" colspan="6">
Equipment Type - {{ item["Equipment Type"] }}
</td>
</tr>
<tr v-else>
<td>{{ item["Make"] }}</td>
<td>{{ item["Model #"] }}</td>
<td>{{ item["Bar Code"] }}</td>
<td>{{ item["Serial #"] }}</td>
<td>{{ item["Location"] }}</td>
<td>{{ item["Condition"] }}</td>
</tr>
</template>
</tbody>

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';
}
}
}
}

handle promises in Vuetify data tables

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.

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: {}
}

How to get count on v-if list with variable?

I want to show one list based one variable, but if I show {{ i }} represents entire count, how I get count only the current list?
Thats my code:
<tr v-for="(item, i) in placares" v-if="item.dificuldade == dificuldadeAtual.nome">
<td>{{ i }}</td>
<td>{{ item.nome }}</td>
<td>{{ item.fase }}</td>
<td>{{ item.pontos | formatNumber }}</td>
</tr>
The app is running here: http://digitacao.serraonline.net.br/
Your question is not so clear but i think this will help you:
<tr v-for="(item, index) in placares">
<div v-if="item.dificuldade == dificuldadeAtual.nome">
<td>{{ index }}</td>
<td>{{ item.nome }}</td>
<td>{{ item.fase }}</td>
<td>{{ item.pontos | formatNumber }}</td>
</div>
</tr>
Don't use v-for and v-if in same element, v-for will get priority.
It's not semantic, i solved the problem using one method to filter the list
methods:{
filterPlacar(dificuldade){
return _.take(_.filter(this.placares, item => {
return item.dificuldade.indexOf(dificuldade) >=0;
}),15);
}
}
And the v-for:
<tr v-for="(item, j) in filterPlacar(dificuldadeAtual.nome)">
<td>{{ ++j }}</td>
<td>{{ item.nome }}</td>
<td>{{ item.fase }}</td>
<td>{{ item.pontos | formatNumber }}</td>
</tr>
You can replace v-if with v-show like:
<tr v-for="(item, i) in placares" v-show="item.dificuldade == dificuldadeAtual.nome">
<td>{{ i }}</td>
<td>{{ item.nome }}</td>
<td>{{ item.fase }}</td>
<td>{{ item.pontos | formatNumber }}</td>
</tr>
or use v-if in your tds like:
<tr v-for="(item, i) in placares">
<template v-if="item.dificuldade == dificuldadeAtual.nome">
<td>{{ i }}</td>
<td>{{ item.nome }}</td>
<td>{{ item.fase }}</td>
<td>{{ item.pontos | formatNumber }}</td>
</template>
</tr>