Accessing array inside of another array to use v-for - javascript

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.

Related

Dynamically create html table from an Array

Hey I'm fairly new to Vue and JavaScript and I wanna create a Html table. For my software I wanne have a Table.vue component that can display me different tables like the following .
{
"id": 1,
"text": "Hello"
}
{
"id": 1,
"status": "damaged",
"info": "test",
"text": "content"
}
How do I get the columns of these different tables dynamically and how can I display them?
<template>
<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>
</template>
data() {
return {
items: [],
columns: []
}
}
table example
You need to specify both the columns that should be rendered, as well as the rows with all data.
The Table.vue could look like this:
<template>
<table>
<thead>
<tr>
<th v-for="(column, index) in columns" :key="index">
{{ column }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows" :key="index">
<td v-for="(column, index) in columns" :key="index">
{{ row[column] }}
</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
name: "Table",
props: {
columns: Array,
rows: Array,
},
};
</script>
…and the parent component would then pass the data into Table.vue like this:
<template>
<Table :columns="columns" :rows="rows" />
</template>
<script>
import Table from "./components/Table";
export default {
name: "App",
components: {
Table,
},
data() {
return {
columns: ["id", "title", "description"],
rows: [
{
id: 1,
title: "what is this",
description: "i like to describe",
},
{
id: 2,
title: "wait wat?",
description: "i like to describe, too",
},
],
};
},
};
</script>
See the working codesandbox.
Note that most actual Vue dataTable components use render functions for performance reasons.

Vuetify v-simple-table Highlight Selected Row

Been reading the docs in Vutify for their v-simple-table and v-data-table and I am trying to figure out if there is a way to add a row highlight like you can for a v-data-table.
Such as this?
Vuetify - How to highlight row on click in v-data-table
My table body looks like this:
<v-simple-table dense fixed-header height="90vh" >
<template v-slot:default>
<thead >
<tr >
<th style="font-size: 16px;height: 40px;" width='4%' class="black--text">
Location
</th>
<template v-if="Object.keys(arrAvailableDates).length">
<th style="font-size: 17px; " class="text-center black--text" v-for="(strDate, intIndex) in arrAvailableDates" :key="intIndex">
<span> {{ $moment(strDate).format('D MMM (ddd)') }}</span>
<v-badge tile v-if="total[strDate] > 0" inline color="green" v-bind:content="total[strDate]" > </v-badge>
</th>
</template>
<template v-else>
<th class="text-center" v-for="intHeaderCounter in 6" :key="intHeaderCounter">
<v-skeleton-loader
:key="intHeaderCounter"
type='text'
></v-skeleton-loader>
</th>
</template>
</tr>
</thead>
<tbody v-if="!blnLoading">
<template v-if="Object.keys(arrFiltered).length" >
<tr class="teal lighten-5"
v-for="(arrData, strLocationName) in arrFiltered"
:key="'f-' + strLocationName"
>
<th class="black--text text-darken--3">
{{ strLocationName }} <br/><span class="caption">{{arrData['zip']}}</span>
</th>
<template v-for="(arrAppointmentData, strDate) in arrData['appointment']">
<td :key="strDate" class="text-center ma-0 pa-0" >
<template v-if="typeof(arrAppointmentData) == 'object' ">
<span class="time-slot-x-small" v-for='(intCount, intIndex) in arrAppointmentData' :key="intIndex">
{{ $moment(intIndex, ["HH:mm:ss"]).format('hh:mma')}}
<span class="dot">{{intCount}}</span>
</span>
</template>
<template v-else>
-
</template>
</td>
</template>
</tr>
</template>
I can hover a row and it will show that the row is been hover but I am trying to create a way where if I click on the row, it will change the background color when selected.
An example I am trying to mirror is like this but with a simple table:
https://www.codeply.com/p/hi40H9aug9
Here is a more Vue centric approach...
template:
<tbody>
<tr
v-for="(item,idx) in items"
:key="item.name"
#click="handleClick(idx)"
:class="{ selected: selected.includes(idx) }"
>
<td>{{ item.name }}</td>
<td>{{ item.calories }}</td>
</tr>
</tbody>
script:
data () {
return {
items: [
{
id: 1,
name: "Frozen Yogurt",
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
iron: "1%",
},
],
selected: [],
}
},
methods: {
handleClick(i) {
let pos = this.selected.indexOf(i)
if (pos > -1) {
this.selected.splice(pos, 1);
}
else {
this.selected.push(i)
}
},
}
https://codeply.com/p/PaV3dwWk1j

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>

Problem using Vuetify datatables + single selection prop with template

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>

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>

Categories

Resources