How to display a datatable through an accordion - javascript

I encountered some troubles creating an accordion table. I created an Accordion component and a Table component. While independant from each other it works perfectly fine but I can't get any table appear into accordion.
//js part of accordion component
import Table from "../Table/index.vue"
export default {
name: 'accordion',
components: { Table },
mounted () {
},
data(){
return {
items: [
{ id: "mc", title: "Medical Checkup", text: this.components.Table },
{ id: "ac", title: "Application connected", text:
this.components.Table },
{ id: "p", title: "Programs", text: this.components.Table },
{ id: "pl", title: "Pain list", text: this.components.Table }
]
}
}
}
//html part of accordion component
<div>
<div ref="list" class="list">
<transition-group class="flip-list" name="flip-list" ref="tg1"
tag="div">
<div v-for="(item,index) in items" :key="item.id" class="item">
<slot name="item" :class="{active:item.isOpen}" :item="item"
:index="index">
<v-expansion-panel id="exp1">
<v-expansion-panel-content>
<div slot="header" class="drop-target handle2">
<span>{{item.title}}</span>
</div>
<v-card>
<v-card-text>
<div slot="item">{{Table}}</div>
</v-card-text>
</v-card>
</v-expansion-panel-content>
</v-expansion-panel>
</slot>
</div>
</transition-group>
</div>
</div>
So, the point is : how can I make it so that a datatable appears into accordion ? Like when you click aon one of the titles and it appears instead of some text ?
Thanks a lot

Resolved!
In case of (if people encounter the same problem as I had), here is the code (html / js).
//html goes here
<div id="app">
<v-app id="inspire">
<v-data-table :headers="mainHeaders"
:items="mainItems"
item-key="title"
hide-actions
expand
class="elevation-1">
<template slot="items" scope="props">
<tr #click="props.expanded = !props.expanded">
<td class="text-xs">{{ props.item.title }}</td>
</tr>
</template>
<template slot="expand" scope="props">
<v-data-table :headers="subHeaders"
:items="subItems"
item-key="pain"
hide-actions
class="elevation-10">
<template slot="items" scope="props">
<td class="text-xs">{{ props.item.pain }}</td>
<td class="text-xs">{{ props.item.type }}</td>
</template>
</v-data-table>
</template>
</v-data-table> </v-app>
</div>
//js goes here
export default {
name: 'accordion-table',
mounted () {
},
data () {
return {
mainHeaders: [
{ text: 'Medical informations', value: 'title' }
],
mainItems: [
{ title: 'Medical Checkup' },
{ title: 'Application connected' },
{ title: 'Programs' },
{ title: 'Pain List' }
],
subHeaders: [
{ text: 'Pain', value: 'pain' },
{ text: 'Type', value: 'type' }
],
subItems: [
{ pain: 'Knee', type: '1' },
{ pain: 'Ankle', type: '2' },
{ pain: 'Back', type: '3' },
{ pain: 'Neck', type: '4' }
]
}
}
}
(I used random values)
Hope it helps some of you

Related

Async Axios data with Vue

I'm trying to load data from an axios call into a table on my page, but there's no data in the table with my current code. I'm assuming that the page elements are being rendered before the axios call finishes and therefore nothing is loading.
export default {
components: {
BaseTable
},
data() {
return {
table1: {
title: "Scene Objects",
columns: [...tableColumns],
data: [],
type: "hover"
},
};
},
mounted() {
axios.get('http://localhost:4000/api/card/leveldata').then((data) => {
this.table1.data = Object.values(data.data['Card Level Data']);
});
}
};
I've tried a lot of things to fix it, including using v-if to wait for the call to finish but I can't get anything to work. Any help is appreciated.
EDIT:
template:
<template>
<div>
<div class="row">
<card type="plain" title="Scene Preview">
<!-- <div id="map" class="map">
</div> -->
<iframe id="panoramic" src="https://momento360.com/e/u/9a0d3bcfff454d74950b0f9b8ded7e43?utm_campaign=embed&utm_source=other&heading=80.77&pitch=-0.4&field-of-view=75&size=medium"></iframe>
</card>
</div>
<div class="row">
<div class="col-12">
<card :title="table1.title">
<div class="table-responsive">
<base-table :data="table1.data"
:columns="table1.columns"
thead-classes="text-primary">
</base-table>
</div>
</card>
<base-button type="primary">Preview</base-button>
<base-button type="primary">Save</base-button>
</div>
</div>
</div>
</div>
</template>
BaseTable component:
<template>
<table class="table tablesorter" :class="tableClass">
<thead :class="theadClasses">
<tr>
<slot name="columns">
<th v-for="column in columns" :key="column">{{column}}</th>
</slot>
</tr>
</thead>
<tbody :class="tbodyClasses">
<tr v-for="(item, index) in data" :key="index">
<slot :row="item">
<td v-for="(column, index) in columns"
:key="index"
v-if="hasValue(item, column)">
{{itemValue(item, column)}}
</td>
</slot>
</tr>
</tbody>
</table>
</template>
<script>
export default {
name: 'base-table',
props: {
columns: {
type: Array,
default: () => [],
description: "Table columns"
},
data: {
type: Array,
default: () => [],
description: "Table data"
},
type: {
type: String, // striped | hover
default: "",
description: "Whether table is striped or hover type"
},
theadClasses: {
type: String,
default: '',
description: "<thead> css classes"
},
tbodyClasses: {
type: String,
default: '',
description: "<tbody> css classes"
}
},
computed: {
tableClass() {
return this.type && `table-${this.type}`;
}
},
methods: {
hasValue(item, column) {
return item[column.toLowerCase()] !== "undefined";
},
itemValue(item, column) {
return item[column.toLowerCase()];
}
}
};
</script>
<style>
</style>

(vuejs) Array undefined with axios and quasar

I have a problem with an array that is undefined while in Vuejs devTool I see it full. Here is what I did with quasar:
When i click on my update button i want to display a q-card with input of my row. So i have make a request with axios and put response in array. But i have 'undefined' when i do a console.log() of my array.
<template>
<div>
<div class="q-pa-sm q-gutter-sm">
<q-dialog v-model="show_dialog" v-bind:programmeEdit="programmeEdit">
<q-card>
<q-card-section>
<div class="text-h6">Add new item!</div>
</q-card-section>
<q-card-section>
<div class="row">
<q-input v-model="programmeEdit.prg_libelle" label="prg_libelle"></q-input>
<q-input label="id"></q-input>
<q-input label="nom promotteur"></q-input>
<q-input label="date commercialistion"></q-input>
<q-input label="stock initial"></q-input>
<q-input label="nombre tranche"></q-input>
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="OK" color="primary" v-close-popup #click="" ></q-btn>
</q-card-actions>
</q-card>
</q-dialog>
</div>
<q-table title="Programme" :filter="filter" :data="programme.data" :columns="columns" row-key="name">
<template v-slot:top-right>
<q-input borderless dense debounce="300" v-model="filter" placeholder="Search">
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td key="prg_libelle" :props="props">{{ props.row.prg_libelle }}</q-td>
<q-td key="id" :props="props">{{ props.row.id }}</q-td>
<q-td key="nom_promotteur" :props="props">{{ props.row.act_libelle }}</q-td>
<q-td key="id_promotteur" :props="props">{{ props.row.id_promotteur }}</q-td>
<q-td key="date_commercialisation" :props="props">{{ props.row.tra_date_commercialisation }}</q-td>
<q-td key="stock_initial" :props="props">{{ props.row.tra_stock_initial }}</q-td>
<q-td key="nombre_tranche" :props="props">{{ props.row.nombre_tranche }}</q-td>
<q-td key="actions" :props="props">
<q-btn color="blue" label="Update" #click="getProgramme(props.row.id)" size=sm no-caps></q-btn>
<q-btn color="red" label="Delete" #click="deleteItem(props.row)" size=sm no-caps></q-btn>
</q-td>
</q-tr>
</template>
</q-table>
</div>
</template>
Vue.use(VueAxios, axios)
export default {
methods: {
},
data () {
return {
programmetoEdit:'',
show_dialog:false,
filter: '',
programme:{},
programmeEdit:{},
columns: [
{
name: 'prg_libelle',required: true, label: 'prg_libelle',align: 'left', field: 'prg_libelle' ,format: val => `${val}`, sortable: true },
{ name: 'id', align: 'center', label: 'id', field: 'id', sortable: true },
{ name: 'nom_promotteur', align: 'center',label: 'nom_promotteur', field: 'act_libelle', },
{ name: 'id_promotteur', align: 'center',label: 'id_promotteur', field: 'id_promotteur', },
{ name: 'date_commercialisation', align: 'center',label: 'date_commercialisation', field: 'tra_date_commercialisation', },
{ name: 'stock_initial', align: 'center',label: 'stock_initial', field: 'tra_stock_initial', },
{ name: 'nombre_tranche', align: 'center',label: 'nombre_tranche', field: 'nombre_tranche', },
{ name: "actions", label: "Actions", field: "actions"},
]
}
},
created() {
axios.get("http://localhost:80/api/programme")
.then(response =>this.programme=response)
.catch(error=>console.log(error))
},
methods: {
getProgramme($id){
axios.get("http://localhost:80/api/programme/"+$id)
.then(response => this.programmeEdit=response.data);
this.show_dialog= !this.show_dialog;
this.programmeEdit.id=this.programmetoEdit;
}
},
}
I get an undefined when I want to retrieve the query according to the id. I'm blocking and I don't know why.
thanks a lot :)
If you want to log it from your code (like in the .then promise, for example) you should do:
console.log(this.programmetoEdit);
Moreover, you are doing assignment in parallel of the job Axios is doing. Not after, as I guess you are expecting it to do.
You should drop all your stuff in the .then promise if you want it to happen after the this.programmeEdit=response.data part.
Like so (this will show the dialog WHILE loading the data/waiting for the API to respond):
getProgramme ($id) {
axios.get("http://localhost:80/api/programme/" + $id)
.then(response => {
this.programmeEdit=response.data;
this.programmeEdit.id=this.programmetoEdit;
});
this.show_dialog= !this.show_dialog;
}
Or like so (this will show the dialog AFTER loading the data/waiting for the API to respond):
getProgramme ($id) {
axios.get("http://localhost:80/api/programme/" + $id)
.then(response => {
this.programmeEdit=response.data;
this.programmeEdit.id=this.programmetoEdit;
this.show_dialog= !this.show_dialog;
});
}

Vue.js pass a callback as prop to child component and execute on parent when it is clicked

I am trying to create a dropdown component, in which it receives various data and the list items are built dynamically, but I am having difficulty detecting the click on any item in the list
parent
<Dropdown
:items="[
{
text: 'Edit',
icon: 'fal fa-edit',
click: editFunction(id)
},
{
text: 'Delete',
icon: 'fal fa-trash-alt',
click: deleteFunction(id)
}
]"
/>
child Dropdown.vue
<template>
<div class="dropdown">
<a class="trigger">
<i class="far fa-ellipsis-h"></i>
</a>
<ul class="items">
<li v-for="(item, index) in items" :key="index" class="item">
<a>
<i :class="item.icon"></i>
{{ item.text }}
</a>
</li>
</ul>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
props: {
items: {
type: Array,
default: () => []
}
}
})
</script>
currently in this way, as soon as the parent component is created, the editFunction(id) and deleteFunction(id) methods are executed.
I know it's possible because vuetifyjs it does that way, but I tried to inspect the source code but I got nowhere.
What you want to achieve can be done by
parent.vue
<child-componet :parent-method="thisIsTheMethod" />
...
methods: {
thisIsTheMethod()
{
//here it does something
}
}
note that the method passed inside the prop does not have the parenthesis () because you are passing a reference to it.
To use it inside the child component add the ()
#click="parentMethod()"
To go back to your example, change this:
<Dropdown
:items="[
{
text: 'Edit',
icon: 'fal fa-edit',
click: editFunction(id)
},
{
text: 'Delete',
icon: 'fal fa-trash-alt',
click: deleteFunction(id)
}
]"
/>
to
<Dropdown
:items="[
{
text: 'Edit',
icon: 'fal fa-edit',
click: () => editFunction(10)
},
{
text: 'Delete',
icon: 'fal fa-trash-alt',
click: () => deleteFunction(20)
}
]"
/>
and leave your editFunction(id) method declaration as is. The argument will be automatically injected.
Despite this solution would work, the best way to achieve this communication between parent and child would be to emit the value in the child and then listen for it
there have betters ways to do it, but about your idea should be as below:
Vue.component('dropdown', {
template: '#dropdown-template',
props: {
items: {
type: Array,
default: () => []
}
}
})
new Vue({
el: '#app',
data() {
return {
users: [
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
]
}
},
methods: {
editFunction(id) {
console.warn('edit item ' + id)
},
deleteFunction(id) {
console.warn('delete item ' + id)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<table border="1">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>actions</tr>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.id">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>
<dropdown
:items="[
{
text: 'Edit',
icon: 'fal fa-edit',
event: 'edit-function'
},
{
text: 'Delete',
icon: 'fal fa-trash-alt',
event: 'delete-function'
}
]"
#edit-function="editFunction(user.id)"
#delete-function="deleteFunction(user.id)"
/>
</td>
</tr>
</tbody>
</table>
</div>
<script type="text/x-template" id="dropdown-template">
<div class="dropdown">
<a class="trigger">
<i class="far fa-ellipsis-h"></i>
</a>
<ul class="items">
<li v-for="(item, index) in items" :key="index" class="item">
<a #click="$emit(item.event)">
<i :class="item.icon"></i>
{{ item.text }}
</a>
</li>
</ul>
</div>
</script>

Data is recognized but not rendered Vuetify

I have an assignment that uses Vuetify and I want to render a table of "customer" data. My code worked fine with Vuetify 1.5, but now I am supposed to update to the newest version, which is 2.0.
The problem I'm running into is that my data is recognized by the program, but none of it actually shows up in the table. The exact number of rows are rendered to match my number of records, and Vue DevTools shows that my data is there, but The rendered html is just empty elements. I attached screenshots below to illustrate my point a little better:
The project can be found at: https://github.com/apalmesano2/assign3_frontend.git
Here is a snippet of what my Vue template for the list of customers:
CustomerList.vue
<template>
<main>
<br />
<v-container fluid grid-list-md>
<v-layout column align-left>
<blockquote>
Welcome {{validUserName}}!
<footer>
<small>
<em>—Eagle Financial Services, your Midwest Financial Services Partner.</em>
</small>
</footer>
</blockquote>
</v-layout>
<v-layout column align-center>
<v-flex xs6 sm8 md7>
<v-alert
v-if="showMsg === 'new'"
dismissible
:value="true"
type="success"
>New customer has been added.</v-alert>
<v-alert
v-if="showMsg === 'update'"
dismissible
:value="true"
type="success"
>Customer information has been updated.</v-alert>
<v-alert
v-if="showMsg === 'deleted'"
dismissible
:value="true"
type="success"
>Selected Customer has been deleted.</v-alert>
</v-flex>
</v-layout>
<br />
<v-container fluid grid-list-md fill-height>
<v-layout column>
<v-flex md6>
<v-data-table
:headers="headers"
:items="customers"
hide-default-footer
class="elevation-1"
fixed
style="max-height: 300px; overflow-y: auto"
>
<template slot="items" slot-scope="props">
<td>{{ props.item.pk }}</td>
<td>{{ props.item.cust_number }}</td>
<td>{{ props.item.name }}</td>
<td nowrap="true">{{ props.item.address }}</td>
<td nowrap="true">{{ props.item.city }}</td>
<td nowrap="true">{{ props.item.state }}</td>
<td nowrap="true">{{ props.item.zipcode }}</td>
<td nowrap="true">{{ props.item.email }}</td>
<td nowrap="true">{{ props.item.cell_phone }}</td>
<td nowrap="true">
<v-icon #click="updateCustomer(props.item)">edit</v-icon>
</td>
<td nowrap="true">
<v-icon #click="deleteCustomer(props.item)">delete</v-icon>
</td>
</template>
</v-data-table>
</v-flex>
</v-layout>
</v-container>
<v-btn class="blue white--text" #click="addNewCustomer">Add Customer</v-btn>
</v-container>
</main>
</template>
And the script, although I don't think there is an issue here because the data is being fetched just fine according to the Vue DevTools:
<script>
import router from "../router";
import { APIService } from "../http/APIService";
const apiService = new APIService();
export default {
name: "CustomerList",
data: () => ({
customers: [],
validUserName: "Guest",
customerSize: 0,
showMsg: "",
headers: [
{ text: "Record Number", sortable: false, align: "left" },
{ text: "Customer Number", align: "left", sortable: false },
{ text: "Name", sortable: false, align: "left" },
{ text: "Address", sortable: false, align: "left" },
{ text: "City", sortable: false, align: "left" },
{ text: "State", sortable: false, align: "left" },
{ text: "ZipCode", sortable: false, align: "left" },
{ text: "Email", sortable: false, align: "left" },
{ text: "Phone", sortable: false, align: "left" },
{ text: "Update", sortable: false, align: "left" },
{ text: "Delete", sortable: false, align: "left" }
]
}),
mounted() {
this.getCustomers();
this.showMessages();
},
methods: {
showMessages() {
console.log(this.$route.params.msg);
if (this.$route.params.msg) {
this.showMsg = this.$route.params.msg;
}
},
getCustomers() {
apiService
.getCustomerList()
.then(response => {
this.customers = response.data.data;
this.customerSize = this.customers.length;
if (
localStorage.getItem("isAuthenticates") &&
JSON.parse(localStorage.getItem("isAuthenticates")) === true
) {
this.validUserName = JSON.parse(localStorage.getItem("log_user"));
}
})
.catch(error => {
if (error.response.status === 401) {
localStorage.removeItem("isAuthenticates");
localStorage.removeItem("log_user");
localStorage.removeItem("token");
router.push("/auth");
}
});
},
addNewCustomer() {
if (
localStorage.getItem("isAuthenticates") &&
JSON.parse(localStorage.getItem("isAuthenticates")) === true
) {
router.push("/customer-create");
} else {
router.push("/auth");
}
},
updateCustomer(customer) {
router.push("/customer-create/" + customer.pk);
},
deleteCustomer(customer) {
apiService
.deleteCustomer(customer.pk)
.then(response => {
if (response.status === 204) {
alert("Customer deleted");
this.showMsg = "deleted";
this.$router.go();
}
})
.catch(error => {
if (error.response.status === 401) {
localStorage.removeItem("isAuthenticates");
localStorage.removeItem("log_user");
localStorage.removeItem("token");
router.push("/auth");
}
});
}
}
};
</script>
I have a hunch I'm doing something wrong with my v-data-table and the template where I'm passing the props in but I can't figure out what's going wrong or how to fix it because even just hardcoded text in the tags won't render in the table.
To access the page where the screenshots are after cloning the project, you would need to log in with the following credentials:
Username: instructor
Password: instructor1a
Thanks to anyone who can help!
I figured out the new syntax after moving to vuetify v2:
<v-data-table
:headers="headers"
:items="stocks"
hide-default-footer
class="elevation-1"
fixed
style="max-height: 300px; overflow-y: auto"
>
<template v-slot:item="props">
<tr>
<td>{{ props.item.customer }}</td>
<td nowrap="true">{{ props.item.symbol }}</td>
<td nowrap="true">{{ props.item.name }}</td>
<td nowrap="true">{{ props.item.shares }}</td>
<td nowrap="true">{{ props.item.purchase_price }}</td>
<td nowrap="true">{{ props.item.purchase_date }}</td>
<td nowrap="true">
<v-icon #click="updateStock(props.item)">edit</v-icon>
</td>
<td nowrap="true">
<v-icon #click="deleteStock(props.item)">delete</v-icon>
</td>
</tr>
</template>
</v-data-table>

Bootstrap Vue Dynamic table templating

I am working with Bootstrap Vue JS table component to create a datatable:
https://bootstrap-vue.js.org/docs/components/table
I am new to VueJS and am uncertain on how to approach this problem which makes searching for a solution even more complicated.
I use an API endpoint to return JSON data:
{
"options":{
"filter":false
},
"fields":[
{
"key":"id",
"label":"Id",
"editLink":false,
"display":true,
"sortable":true,
"class":"shrink"
},
{
"key":"name",
"label":"Name",
"editLink":true,
"display":true,
"sortable":true
}
],
"data":[ ]
}
Here is my table template:
<b-table striped hover bordered foot-clone class="table-sm"
:items="users" :fields="displayedFields" :per-page="perPage" :current-page="currentPage" :filter="filter"
#filtered="onFiltered"
>
<template v-for="(field, index) in fields">
<template slot="{{field.key}}" slot-scope="row" v-if="field.editLink">
<router-link :to="'/user/' + row.item.id" v-bind:key="index"></router-link>
</template>
</template>
<template slot="status" slot-scope="row">
<toggle-button :width="36" :height="18" v-model="row.status" :labels="false" :colors="{checked: '#00FF00', unchecked: '#FF0000', disabled: '#CCCCCC'}"/>
</template>
</b-table>
The first template tag is an attempt from a wild guess. I want to be able to conditionally select a table for a column from the fields config. You can see in my attempt that I want to put a RouterLink when the field's config editLink is true.
How can I get this done?
If you're using version 2.0.0 or newer of bootstrap-vue you need to change the slot names as
they've changed, and the old vue slot has also been deprecated in favor for v-slot.
I changed the accepted answers fiddle to work with the new naming and v-slot
new Vue({
el: "#app",
data: {
fields: [{
key: "id",
label: "Id",
colType: "span"
}, {
key: "name",
label: "Name",
colType: "button"
}, {
key: "uhh",
label: "uhh..",
colType: "idk"
}],
items: [{
id: 0,
name: "Test 0"
}, {
id: 1,
name: "Test 1"
}, {
id: 2,
name: "Test 2"
}]
}
});
<link href="https://unpkg.com/bootstrap#4.3.1/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap-vue#2.0.0/dist/bootstrap-vue.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.0.0/dist/bootstrap-vue.js"></script>
<div id="app">
<b-table :items="items" :fields="fields">
<template v-for="(field, index) in fields" v-slot:[`cell(${field.key})`]="data">
<div v-if="field.colType === 'button'">
<h5>{{data.item.name}}</h5>
<b-button>Am Button</b-button>
</div>
<div v-else-if="field.colType === 'span'">
<h5>{{data.item.name}}</h5>
<span>Am Span</span>
</div>
<div v-else>
<h5>{{data.item.name}}</h5>
Am Confused
</div>
</template>
</b-table>
</div>
Here's a jsfiddle showing dynamic columns with a b-table:
https://jsfiddle.net/nardnob/wvk6fxgt/
new Vue({
el: "#app",
data: {
fields: [{
key: "id",
label: "Id",
colType: "span"
}, {
key: "name",
label: "Name",
colType: "button"
}, {
key: "uhh",
label: "uhh..",
colType: "idk"
}],
items: [{
id: 0,
name: "Test 0"
}, {
id: 1,
name: "Test 1"
}, {
id: 2,
name: "Test 2"
}]
}
});
<div id="app">
<b-table :items="items" :fields="fields">
<template v-for="(field, index) in fields" :slot="field.key" slot-scope="data">
<div v-if="field.colType === 'button'">
<h5>{{data.item.name}}</h5>
<b-button>Am Button</b-button>
</div>
<div v-else-if="field.colType === 'span'">
<h5>{{data.item.name}}</h5>
<span>Am Span</span>
</div>
<div v-else>
<h5>{{data.item.name}}</h5>
Am Confused
</div>
</template>
</b-table>
</div>

Categories