Vue table in one loop html - html-table

I have an array, with sizes of shoes and quantity each of them in shop, structure like this:
array = {
36=>1,
37=>0,
38=>5,
39=>2
}
In my table key in this table ( here 36, 37 ... ) are TH, and value is TD.
I can't do this in one loop. I tried like this:
<table class="table">
<tr>
<th v-for="(quantity, key) in productSizes" :key='key'>{{key}}</th>
</tr>
<tr>
<td>Here should be quantity for each size<td>
</tr>
</table>
Is there a possibility to do this at once?
Here is structure how it should look like (there is an input, because someone can change quantity).

I believe the right way to do it would be to use a template. I have not tested the code personally but it would seem like the right way to do it.
Similar example found here
<tbody>
<template v-for="(quantity, key) in productSizes" :key='key'>
<tr>
<th>{{key}}</th>
</tr>
<tr>
<td>{{key}}<td>
</tr>
</template>
</tbody>
Edit
Made it work with a single v-for loop using above example (had time to test it this time).
const productSizes = {
36: 0,
37: 2,
38: 1,
39: 3,
40: 2,
41: 0
}
new Vue({
el: "#app",
data:{
productSizes
}
})
<script src="https://unpkg.com/vue#2.4.2/dist/vue.js"></script>
<div id="app">
<table>
<tbody>
<template v-for="(quantity, key) in productSizes">
<tr>
<th>{{key}}</th>
</tr>
<tr>
<td><input v-model="productSizes[key]" type="text"><td>
</tr>
</template>
</tbody>
</table>
</div>

I cannot conceive of a way you could do this in one loop, nor do I think it's worth the effort. Just loop a second time over the same data structure and you will always get matching columns.
console.clear()
const shoeSizes = {
36: 0,
37: 2,
38: 1,
39: 3,
40: 2,
41: 0
}
new Vue({
el: "#app",
data:{
shoeSizes
}
})
<script src="https://unpkg.com/vue#2.4.2"></script>
<div id="app">
<table>
<tr>
<th v-for="quantity, key in shoeSizes">{{key}}</th>
</tr>
<tr>
<td v-for="quantity, key in shoeSizes">
<input type="text" v-model="shoeSizes[key]">
</td>
</tr>
</table>
</div>
Assuming there were even 100 shoe sizes the performance impact would be negligible.
Edit
Well, I can think of one way you could render it in one loop. Using a render function.
console.clear()
const shoeSizes = {
36: 0,
37: 2,
38: 1,
39: 3,
40: 2,
41: 0
}
new Vue({
el: "#app",
data:{
shoeSizes
},
render(h){
let headers = [], cells = []
// build the headers and cells
for (let key of Object.keys(this.shoeSizes)){
// header is easy
headers.push(h('th', key))
// build props for the input to implement v-model
let vmodel = {
domProps: {
value: this.shoeSizes[key]
},
on: {
input: event => {
this.$set(this.shoeSizes, key, event.target.value)
this.$emit('input', event.target.value)
}
}
}
// add the vell
cells.push(h('td', [h('input', vmodel)]))
}
// render the table with headers and cells in the
// right places
return h('table', [h('tr', headers), h('tr', cells)])
}
})
<script src="https://unpkg.com/vue#2.4.2"></script>
<div id="app"></div>
This render function builds the headers and cells in the same loop and then renders the table around them. But I think you would agree this is needlessly complex.

Related

display a foreign data in a table vuejs

I am creating a page, in which a section called product, I show data and in this I have a data called id_categoria, where instead of showing the id I want to show the name of that category in question, this data is in the table of categories and in the product table I have the id. I already managed to show the data, but not this, besides that I managed to save and edit it in question, it is only necessary in show.
When working with vuejs I saw that you have to use a v-if but I do not know how to do it, or at least the attempts I have made have been wrong.
this is the code of the table where I show the data
<div class="card-body table-responsive">
<table id="example1" class="table table-bordered table-striped">
<thead>
<tr>
<th>id_producto</th>
<th>imagen</th>
<th>código</th>
<th>producto</th>
<th>categoria</th>
<th>stock</th>
<th>precio_compra</th>
<th>precio_venta</th>
<th>fecha</th>
<th colspan="2" class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<tr v-for="pro in productos" :key="pro.id_productos">
<th>{{pro.id_productos}}</th>
<td>
<img :src="pro.imagen" class="img-circle elevation-2" alt="Product Image" width="60" />
</td>
<td>{{pro.codigo}}</td>
<td>{{pro.producto}}</td>
<td>{{pro.id_categoria}}</td>
<td>{{pro.stock}}</td>
<td>{{pro.precio_compra}}</td>
<td>{{pro.precio_venta}}</td>
<td>{{pro.fecha}}</td>
<td class="text-center">
<button #click="modificar=true; abrirModal(pro)" type="button" class="editar btn btn-primary"><i class="fa fa-pencil-alt"></i></button>
</td>
<td class="text-center">
<button #click="eliminar(pro.id_productos,pro.producto)" type="button" class="eliminar btn btn-danger" data-toggle="modal" data-target="#modalEliminar"><i class="fas fa-dumpster-fire"></i></button>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>id_producto</th>
<th>imagen</th>
<th>código</th>
<th>producto</th>
<th>categoria</th>
<th>stock</th>
<th>precio_compra</th>
<th>precio_venta</th>
<th>fecha</th>
<th colspan="2" class="text-center">Acciones</th>
</tr>
</tfoot>
</table>
</div>
and this is the script to bring the info to me
<script>
import axios from "axios";
export default {
watch: {
$route: {
immediate: true,
handler(to, from) {
document.title = to.meta.title || 'Productos';
}
},
},
data() {
return{
productosdatos:{
id_producto: "",
codigo: "",
producto: "",
stock: "",
precio_compra: "",
precio_venta : "",
id_categoria: "",
},
id: 0,
modificar: true,
modal: 0,
tituloModal: '',
productos:[],
categorias:[],
}
},
methods: {
async listarcategorias(){
const res = await axios.get('http://localhost:8000/categoria/');
this.categorias = res.data;
console.log(this.categorias)
},
async listar(){
const res = await axios.get('http://localhost:8000/productos/');
this.productos = res.data;
console.log(this.productos)
},
cerrarModal(){
this.modal = 0;
}
},
created() {
this.listar();
}
}
</script>
as you can see I have a variable called products, where is the id_category corresponding to the product, and categories where I bring all the category info.
the table looks something like this:
how can I make it not show the id of the category but the name of the category in question ?
pd: the category data is received in json as follows:
{
"id_categoria": 8,
"categoria": "Electrodomesticos",
"fecha": "2021-10-24 13:55:00"
}
thank you if you can help me to show the name of the category thank you
You can implement a function like this:
const findCategory = (id)=>{this.categorias.find(category=>category.id_categoria===id)?.categoria}
And in the template:
<td>{{findCategory(pro.id_categoria)}}</td>
Only one small change. In your code you have to edit. pro.id_categoria to pro.categoria. see comment inline.
<tr v-for="pro in productos" :key="pro.id_productos">
<th>{{pro.id_productos}}</th>
<td>
<img :src="pro.imagen" class="img-circle elevation-2" alt="Product Image" width="60" />
</td>
<td>{{pro.codigo}}</td>
<td>{{pro.producto}}</td>
// this line
<td>{{pro.id_categoria}}</td>
// edit to
<td>{{ pro.categoria }} </td>

Using v-for for fixed numbers and not objects

I'm trying to render a simple table with 20 rows and 5 columns with v-for but I'm having problems. My code:
<tr v-for="row in totalRows" :key="row">
<td v-for="col in totalColumns" :key="col">
{{ getTableNum() }}
</td>
</tr>
In data:
totalColumns: 5,
totalRows: 20,
numberCount: 0,
Method:
getTableNum() {
return ++this.numberCount;
},
It is throwing a warning...
[Vue warn]: You may have an infinite update loop in a component render function.
... and rendering like 20k rows.
I can't find an example on how use v-for for fixed numbers(only using objects) anywhere.
I imagine that those loops above would reproduce a result like:
for (let row = 0; row < totalRows; row++) {
for (let col = 0; col < totalCols; col++) {
getTableNum();
}
}
But I'm wrong for some reason.
UPDATE: Ended up using no variables at all:
<tr v-for="row in 20" :key="row">
<td v-for="col in 5" :key="col">
{{ col + (row - 1) * 5 }}
</td>
</tr>
I wish official docs could have examples like that. If I knew that fixed numbers could be used in the for loop it would spare me some time.
Running methods inside the template leads to some infinite rendering loops since the variable is also used in template, to avoid this create a two-dimensional array as a computed property and then render it :
computed:{
arr(){
let n=0;
return [...Array(20)].map((_,i)=>[...Array(5)].map((_,j)=>{
++n;
return n;
}))
}
}
in template :
<tr v-for="(row,i) in arr" :key="i">
<td v-for="col in row" :key="col">
{{ col }}
</td>
</tr>
The getTableNum function execution changes the numberCount in data and it triggers Vue to re-render the template, which causes the function execution, and so on.
In this case, you should try to avoid altering the numberCount value.
If I didn't get you wrong, you wish to have 1-2-3-4-5 in the first row, 6-7-8-9-10 in the second, and so on.
If so, you can try to rewrite your function as such:
getTableNum(row, col) {
// row and col are 1-based
return 1 + ((row - 1) * this.totalColumns) + (col - 1);
},
You are changing numberCount (which is a reactive data property) directly in the template. That triggers a re-render (and thus an infinite loop). You can simply do this :
var app = new Vue({
el: '#app',
data: {
totalColumns: 5,
totalRows: 20
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table border=1>
<tr v-for="(row, rowIndex) in totalRows" :key="row">
<td v-for="(col, columnIndex) in totalColumns" :key="col">
{{ rowIndex * totalColumns + col}}
</td>
</tr>
</table>
</div>
Another alternative ONLY for static tables (where the table content is not modified after initial rendering) is the use of v-once directive. Each table row will be rendered only once, and every subsequent call to getTableNum function will not trigger the rerendering of previous rows:
var app = new Vue({
el: '#app',
data: {
totalColumns: 5,
totalRows: 20,
numberCount: 0
},
methods: {
getTableNum() {
return ++this.numberCount
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table border=1>
<tr v-once v-for="(row, rowIndex) in totalRows" :key="row">
<td v-for="(col, columnIndex) in totalColumns" :key="col">
{{ getTableNum() }}
</td>
</tr>
</table>
</div>

Should I be using a computed property here?

My apologies if this is a simple question.
I have a table that's being built in vue.js
Column A is for numerical input, column B has a preset value and Column C calculates the difference between them.
Currently I'm using a computed property that loops through the rows, calculates the difference and stores that in my data array, then I'm calling the value {{row.difference}} in the table cells.
I've called my computed property difference, however it only works if I include {{difference}} within the element div.
Is that bad usage? Should I be calling a method on each row instead and returning the calculated value?
Computed values never should mutate data.
I would suggest that your computed value return a new array with the difference in it.
Vue.config.devtools = false;
Vue.config.productionTip = false;
var app = new Vue({
el: '#app',
data: {
items: [{
a: 10,
b: 4
},
{
a: 443,
b: 23
}
]
},
computed: {
items_with_difference() {
return this.items.map((i) => ({
...i,
difference: i.a - i.b
}));
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<thead>
<th>
A
</th>
<th>
B
</th>
<th>
Difference
</th>
</thead>
<tbody>
<tr v-for="(item, idx) in items_with_difference" :key="idx">
<td>
{{item.a}}
</td>
<td>
{{item.b}}
</td>
<td>
{{item.difference}}
</td>
</tr>
</tbody>
</table>
</div>

How can I iterate over array data and create new row for every new data in vue?

I have a stream data that I call through axios.get and the resposne.data is a json with one sensor's data like ,{"Temperature":"56"}, with this I use to create a row in vue template.The resposne.data is assigned to array but its not appending rows ,the data in table are getting changed.
This is the template part
<template>
<div class="container">
<h1>Tabular Data</h1>
<table class="table table-striped table-bordered" >
<thead>
<tr>
<th class="text-center">Time</th>
<th class="center">Device Parameter</th>
<th class="center">Magnitude</th>
<th class="right">Unit</th>
</tr>
</thead>
<tbody>
<tr v-for="data in Tabular" :key="data">
<td> {{new Date().toString()}} </td>
<td class="test-left">Temperature</td>
<td class="text-center"> {{data.toString()}} </td>
<td class="test-left">C</td>
</tr>
<!-- <td>{{data_alias.Humidity}}</td>
<td>{{data_alias.Pressure}}</td> -->
</tbody>
</table>
</div>
</template>
<script>
/* eslint-disable */
import axios from 'axios';
export default {
name: 'tabular',
data() {
return {
Tabular:[ ],
}
},
mounted() {
setInterval(()=>{
axios.get('http://localhost:3000/data')
.then((response)=>{
console.log(response.data);
this.Tabular=response.data;
})
.catch((error)=>{
console.log(error);
});
},2000);//I used set interval to get data from stream constantly
},
}
</script>
are you sure Tabular fill at one even?
I think no, because this.Tabular=response.data; ;
this.Tabular is undefined ;
try this
var that = this;
axios.get('http://localhost:3000/data')
.then((response)=>{
console.log(response.data);
that.Tabular=response.data;
})
.catch((error)=>{
console.log(error);
});
That is because you are replacing the content of this.Tabular. You should loop through your response data and, considering response.data is an array of rows, use
response.data.forEach((row) => { this.Tabular.push(row) })

How to style a table in a vue.js component

I am trying to style the table in this vue component, my understanding is that I need to create the css from the data function and then bind it to the appropriate element in the template.
What I am trying to do is apply the following css to my table
css:
.table-striped>tr:nth-of-type(odd) {
background-color: #f9f9f9
}
vue component:
Vue.component('overview', {
delimiters: [ '[[', ']]' ],
props: ['job_execs'],
template: `
<div overview>
<h3>Overview</h3>
<table class="table table-striped table-bordered">
<tr>
<td>Start Time</td>
<td>[[ job_execs[0].time_start ]]</td>
</tr>
<tr>
<td>End Time</td>
<td>[[ job_execs[0].time_end ]]</td>
</tr>
<tr>
<td>Job</td>
<td>http://placeholder</td>
</tr>
</table>
</div>
`,
data: function() {
return {
divExample: {
color: 'red',
fontSize: '13px'
}
}
},
});
Im not sure how to 1. create this CSS from the data function and 2. Bind it within the template.
I am currently working on a project where I need to take an existing web app and convert the front end to vue.js and it seems like its going to be a real headache extracting the css I need from the already bloated css that exists and then converting it to vue js functions that inject that css per component.
You can bind css properties to a tag in Vue using the following way:
<tr>
<td>Start Time</td>
<td>[[ job_execs[0].time_start ]]</td>
</tr>
<tr v-bind:style="strippedTr">
<td>End Time</td>
<td>[[ job_execs[0].time_end ]]</td>
</tr>
<tr>
<td>Job</td>
<td>http://placeholder</td>
</tr>
...
data: function() {
return {
strippedTr: {
'background-color': '#f9f9f9'
}
}
},