Populating HTML table rows with objects containing an array using Vue - data-binding

I'm trying to populate rows in an HTML table using the Vue framework - the data is as seen below:
TeamRows: [
{ team: 'One', times: ['1', '2', '3'] },
{ team: 'Two', times: ['4', '5', '6'] },
{ team: 'Three', times: ['7', '8', '9'] }
]
I've tried following this codepen, but with bad result - this is my HTML:
<tbody>
<tr v-for="(row, rindex) in teamRows">
<td>{{rindex}}</td>
<template>
<cell v-for="(value, vindex) in row" :value="value" :vindex="vindex" :rindex="rindex"></cell>
</template>
</tr>
</tbody>
</table>
<template id="template-cell">
<td>{{ value }}</td>
</template>
And this is the Vue component:
Vue.component('cell', {
template: '#template-cell',
name: 'row-value',
props: ['value', 'vindex', 'rindex']
});
I would like the team to go in the first column in a row and the times to follow along in as many columns as there are times. Hope someone with more Vue knowledge is able to help me out here. Cheers.

Turns out the reason is you're using in-DOM template and browser moves unknown cell element above the v-for, and Vue can't access row value anymore: https://v2.vuejs.org/v2/guide/components.html#DOM-Template-Parsing-Caveats
A solution without cell component, just with inline cell elements, works fine. Also, template wrapper is not needed in a table template:
new Vue({
el: '#app',
data: {
teamRows: [
{ team: 'One', times: ['1', '2', '3'] },
{ team: 'Two', times: ['4', '5', '6'] },
{ team: 'Three', times: ['7', '8', '9'] }
]
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<table>
<tbody>
<tr v-for="row in teamRows" :key="row.team">
<td>{{ row.team }}</td>
<td v-for="time in row.times">{{ time }}</td>
</tr>
</tbody>
</table>
</div>
Also, as a result of a discussion, you can still use a component if you wrap your table into another component for example, so that browser don't interfere and Vue has a chance to render everything properly:
new Vue({
el: '#app',
data: {
teamRows: [
{ team: 'One', times: ['1', '2', '3'] },
{ team: 'Two', times: ['4', '5', '6'] },
{ team: 'Three', times: ['7', '8', '9'] }
]
},
components: {
'mytable': {
template: `<table>
<tbody>
<tr v-for="row in rows" :key="row.team">
<td>{{ row.team }}</td>
<cell v-for="time in row.times" :value="time" :key="time"></cell>
</tr>
</tbody>
</table>`,
props: ['rows'],
components: {
'cell': {
template: `<td>{{ value }}</td>`,
props: ['value'],
}
}
}
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<mytable :rows="teamRows"></mytable>
</div>

Related

how do I fill table html to column wise?

I am trying to fill my table HTML using an API, however, I want to fill the given HTML table into a column instead of a row to look like the following image layout, but however, I am getting filled as row as you can see in my second image. is there a way to convert it into a column-based filling?
row fill
const arr = [
{
"demo": [
{
"_id": "T0810",
"title": "Historian",
"tags": [
"demo"
],
"queries": [],
},
{
"_id": "T0817",
"title": "book",
"tags": [
"demo"
],
"queries": [],
},
],
"demo_2": [
{
"_id": "T0875",
"title": "Program",
"tags": [
"demo_2",
"Control"
],
"queries": [],
},
{
"_id": "T0807",
"title": "Interface",
"tags": [
"demo_2"
],
"queries": [],
}
]
}
]
const keys = Object.keys(arr[0]);
export default function Demo() {
return (
<div className="wrapper">
<table className="table">
<thead>
<tr>
{keys.map((i) => (
<th key={i}>
<div className="column">
<div className="header">{i}</div>
</div>
</th>
))}
</tr>
</thead>
<tbody>
{keys.map((key) => (
<tr key={key}>
{arr[0][key].map((item) => (
<td key={item._id}>
<div className="column">
<div className="option">{item._id}</div>
</div>
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
It's only slightly more involved than what you have put together. The first thing you have to consider is that your table will have as many rows as the lengthiest key, so if demo array has 2 element and demo_2 has 6 elements, the table will have 6 rows even though there will be nothing for 3 of those rows to display for demo.
As such your top level loop will have to loop not through keys but through a number representing the length of the lengthiest key, if that makes sense. So apart from getting the keys, you will need something like this:
const max_rows = keys.reduce((acc, cur) => {
if (arr[0][cur].length > acc) return arr[0][cur].length;
else return acc;
}, 0);
Then your nested loop will look like this:
<table className="table">
<thead>
<tr>
{keys.map((i) => (
<th key={i}>
<div className="column">
<div className="header">{i}</div>
</div>
</th>
))}
</tr>
</thead>
<tbody>
{Array(max_rows)
.fill(0)
.map((key, index) => (
<tr key={index}>
{keys.map((item, ind) => (
<td key={ind}>
<div className="column">
{arr[0][item][index] ? (
<div className="option">
{arr[0][item][index]._id}
</div>
) : null}
</div>
</td>
))}
</tr>
))}
</tbody>
</table>
Here is a Sandbox: https://codesandbox.io/s/adoring-wave-5fub7?file=/src/App.js

Vue JS unable to properly show data table (v-for)

i'm trying to show a table of data that is coming from Firebase Firestore, i've already managed to put all data into a array, but when i try to show that content, the entire array shows up instead of the single items, see the image:
And heres my code:
<template>
<v-simple-table>
<template v-slot:default>
<thead>
<tr>
<th class="text-left">
Name
</th>
<th class="text-left">
Calories
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(item, index) in result"
v-bind:key="index"
>
<td>{{ result }}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</template>
<script>
import firebase from 'firebase'
import {db} from '../service/firebase'
export default {
data () {
return {
userName: null,
result: [],
name: null,
email: null,
userMail: null,
telefone: null,
userPhone: null,
userAccept: null,
}
},
async created() {
var userData = []
await db.collection("users").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(`${doc.id} => ${doc.data()}`);
userData.push(doc.data())
});
});
this.result = userData.map(a => a.name)
console.log(result)
}
}
</script>
How do i show a table of single items of the array instead of a table of arrays?
Thank you in advance!
You're printing the whole content using {{result}}, the right syntax is the following :
<tr v-for="(item, index) in result" v-bind:key="index" >
<td v-for="(record,i) in item" :key="i">{{ record }}</td>
</tr>

Add style to first row of v-data-table in vuetify

I have defined the table as below using vuetify data table component. The issue I am facing here is I not able to figure out how can I make the first row of the table bold. The first item record to be bold. Please help find a solution.
I am using vuetify 1.0.5.
<v-data-table>
:headers="headers"
:items="agents"
hide-actions
class="agent-table"
>
<template slot="items" slot-scope="props">
<td>{{ props.item.name }}</td>
<td>{{ props.item.address }}</td>
</template>
</v-data-table>
use v-if to search for first row index or something unique about first row and bind it to style or class. Few more ways listed here reference
<template slot="items" slot-scope="props">
<tr v-if="unique condition" v-bind:style="{ 'font-weight': 'bold'}>
<td>{{ props.item.name }}</td>
<td>{{ props.item.address }}</td>
</tr>
<tr v-else>
<td>{{ props.item.name }}</td>
<td>{{ props.item.address }}</td>
</tr>
</template>
Another approach that can be used is using computed properties to insert the index to each element in the data. This can be useful if you need to update the table later on as computed properties are updated automatically.
For example, say the item data is stored in items.
data() {
return {
items: [{
fruit_name: 'Banana',
calories: 30
}, {
fruit_name: 'Apples',
calories: 40
}]
}
}
Here, every element to be itself plus additional attribute, i.e. index. Element addition is achieved using spread operator. All mapped elements are combined into single array using functional-programming style of map function.
computed: {
itemsWithIndex: () {
return this.items.map(
(items, index) => ({
...items,
index: index + 1
}))
}
}
Note: index: index+1 will make the numbering start from 1.
Then, inside headers data needed for v-data-table, you can make reference to index item data for numbering.
data() {
return {
items: {
...
},
headers: [{
text: 'Num',
value: 'index',
},
{
text: 'Fruit Name',
value: 'fruit_name',
},
{
text: 'Calories',
value: 'calories',
}
]
}
}
Codepen example: https://codepen.io/72ridwan/pen/NWPMxXp
Reference
<template slot="items" slot-scope="props">
<tr v-bind:class="getClass(props.item.name)">
<td>{{ props.item.name }}</td>
<td>{{ props.item.address }}</td>
</tr>
</template>
<script>
export default {
methods: {
getClass: function (name) {
if (name == 'XYZ') return 'header2';
},
}
}
</script>
<style>
.header2 {
// added style here
}
<style>

Vue: [Vue warn]: Error in render: "TypeError: product.data is not a function"

I am trying to retrieve the data from cloud firestore database.
But I got an error,
[Vue warn]: Error in render: "TypeError: product.data is not a
function"
I want to show the each product name and price in my table.
But I have no idea why this issue comes up.
So I hope somebody can help me out.
If I don't use data() in the vue template, I can see all the data as I expected.
<template>
<h3>Product List</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Modify</th>
</tr>
</thead>
<tbody>
<tr v-for="(product, index) in products" :key="index">
<td>{{ product.data().name }}</td>
<td>{{ product.data().price }}</td>
<td>
<button #click="editProduct(product)" class="btn btn-primary">Edit</button>
<button #click="deleteProduct(product.id)" class="btn btn-danger">Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import { fb, db } from '../firebase'
export default {
name: 'Products',
props: {
msg: String
},
data () {
return {
products: [],
product: {//object
name: null,
price: null
}
}
},
methods: {
editProduct(product) {
$('#edit').modal('show')
},
readData() {
db.collection("products").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.products.push(doc.data());
});
});
},
saveData() {
// Add a new document with a generated id.
db.collection("products").add(this.product)
.then((docRef) => {
console.log("Document written with ID: ", docRef.id);
this.product.name = "",
this.product.price = ""
this.readData()
})
.catch(function(error) {
console.error("Error adding document: ", error);
});
}
},
created() {
this.readData();
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>
I will have to agree with #Mostafa, the naming convention is not very readable. Your error is telling you that you are trying to invoke a function that is not a function or does not exist in your data.
Change:
<td>{{ product.data().name }}</td>
<td>{{ product.data().price }}</td>
To:
<td>{{ product.name }}</td>
<td>{{ product.price }}</td>
This should fix it, as you are iterating over the products list (of which isn't clear), so i advise you should change:
<tr v-for="(product, index) in products" :key="index">
<td>{{ product.name }}</td>
<td>{{ product.price }}</td>
<td>
<button #click="editProduct(product)" class="btn btn-primary">Edit</button>
<button #click="deleteProduct(product.id)" class="btn btn-danger">Delete</button>
To:
<tr v-for="(productItem, index) in products" :key="index">
<td>{{ productItem.name }}</td>
<td>{{ productItem.price }}</td>
<td>
<button #click="editProduct(productItem)" class="btn btn-primary">Edit</button>
<button #click="deleteProduct(productItem.id)" class="btn btn-danger">Delete</button>
Your code is very confusing.
I don't understand why you are calling data method on product and why you have product and products in your data when you just need one.
So i'm assuming Vue is mixing product in your for loop and the product object in your data.
So either change the product name in your for loop to something else:
v-for="(item,index) in products"
or change product in your data (just remove it if you can) cause it doesn't have any data method in it.

How to fetch and display data in React js but Line 26: 'cusList' is not defined no-undef error is coming up,pls healp.tnx

How to fetch and display data in ASP.net core using React js Line 26: 'cusList' is not defined no-undef error is coming up.
import React, { Component } from 'react';
import './Map';
export class FetchCustomer extends Component {
static displayName = FetchCustomer.Name;
constructor(props) {
super(props);
this.state = { customers: [], loading: true };
}
render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: this.renderCustomerTable(this.state.cusList);
return <table className='table'>
<thead>
<tr>
<th></th>
<th>Id</th>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
<tbody>
{cusList.map(cus =>
<tr key={cus.Id}>
<td></td>
<td>{cus.Id}</td>
<td>{cus.Name}</td>
<td>{cus.Address}</td>
<td>
<a className="action" onClick={(id) => this.handleEdit(cus.Id)}>Edit</a> |
<a className="action" onClick={(id) => this.handleDelete(cus.Id)}>Delete</a>
</td>
</tr>
)}
</tbody>
</table>;
fetch('api/Customer/Index')
.then(response => response.json())
.then(data => { this.setState({ cusList: data, loading: false }) });
}
}
Following changes are needed in your code...
In your constructor change the last line of code with following:
this.state = { customers: [], loading: true, cusList: [] };
And in the "tbody" section please change the code to:
<tbody>
{this.state.cusList.map(cus =>
<tr key={cus.Id}>
<td></td>
<td>{cus.Id}</td>
<td>{cus.Name}</td>
<td>{cus.Address}</td>
<td>
<a className="action" onClick={(id) => this.handleEdit(cus.Id)}>Edit</a> |
<a className="action" onClick={(id) => this.handleDelete(cus.Id)}>Delete</a>
</td>
</tr>
)}
</tbody>
This should work.
Also it's pretty clear from the error itself about what you are missing.
Hope it helps.

Resources