Bootstrap Vue Dynamic table templating - javascript

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>

Related

How to specify bootstrap accordion for data from API in vue js

I'm trying to create an order feed using accordion elements from bootstrap-vue and I am struggling with making only one element opened when I press it.
I've tried changing ids which are from api, but have no result.
HTML:
<div v-for="el in APIData" :key="el.id" >
<div class="accordion" role="tablist" :id="el.id">
<b-card no-body class="mb-1" :id="el.id">
<b-card-header header-tag="header" class="p-1" role="tab" :id="el.id">
<b-button block v-b-toggle.accordion-1 variant="dark">{{ el.name }}. Deadline: {{ el.deadline }} Price: <strong>{{ el.price }}</strong></b-button>
</b-card-header>
<b-collapse id="accordion-1" accordion="my-accordion" role="tabpanel" >
<b-card-body>
<b-card-text>
<div> <p> <strong>Posted:</strong> {{ el.date_posted }}. <br />{{ el.description }}</p> </div>
<a class= "button btn btn-dark">More</a>
</b-card-text>
</b-card-body>
</b-collapse>
</b-card>
</div>
</div>
Script:
<script>
import axios from 'axios'
export default {
name: 'Orders',
data () {
return {
APIData: [],
}
},
created () {
axios
.get('/api/v1/orders/')
.then(response => {
this.APIData = response.data
console.log(response.data)
})
.catch(err => {
console.log(err)
})
},
}
</script>
Example of data:
[
{
price: "179",
id: "1",
date_posted: "04_04_2022",
description: "some desc bla bla bla",
name: "some name",
deadline: "04_06_2022"
},
{
price: "189",
id: "2",
date_posted: "05_04_2022",
description: "another desc bla bla bla",
name: "some name",
deadline: "05_06_2022"
},
{
price: "199",
id: "3",
date_posted: "06_04_2022",
description: "another desc bla bla bla",
name: "some name",
deadline: "06_06_2022"
},
]
Again, I need to get opened only one accordion but get three instead e.g. because it seems to bootstrap that it is all one element.
You can simply achieve that by concatenating the el.id in the v-b-toggle attribute and the accordion id as well.
Working Demo :
new Vue({
el: '#app',
data: {
APIData: [{
id: 1,
name: 'Accordion 1'
}, {
id: 2,
name: 'Accordion 2'
}]
}
})
#app {
padding: 20px;
height: 350px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.22.0/dist/bootstrap-vue.js"></script>
<link rel="stylesheet" href="https://unpkg.com/bootstrap-vue#2.22.0/dist/bootstrap-vue.css"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.4.1/dist/css/bootstrap.min.css"/>
<div id="app">
<div v-for="el in APIData" :key="el.id" >
<p>
<b-btn v-b-toggle="`collapse-${el.id}`" variant="primary">Toggle {{ el.name }}</b-btn>
</p>
<b-collapse :id="`collapse-${el.id}`">
<b-card>
Collapse {{ el.name }} contents Here
</b-card>
</b-collapse>
</div>
</div>

How to load tab items dynamically in Vuejs?

AppsTab.vue
<script>
export default {
props: {
tabList: {
type: Array,
required: true,
},
variant: {
type: String,
required: false,
default: () => "vertical",
},
},
data() {
return {
activeTab: 1,
};
},
};
</script>
<template>
<div
:class="{
'flex space-x-4': variant === 'horizontal',
}"
>
<ul
class="list-none bg-blue-900 bg-opacity-30 p-1.5 rounded-lg text-center overflow-auto whitespace-nowrap"
:class="{
'flex items-center mb-6': variant === 'vertical',
}"
>
<li
v-for="(tab, index) in tabList"
:key="index"
class="w-full px-4 py-1.5 rounded-lg"
:class="{
'text-blue-600 bg-white shadow-xl': index + 1 === activeTab,
'text-white': index + 1 !== activeTab,
}"
>
<label
:for="`${_uid}${index}`"
v-text="tab"
class="cursor-pointer block"
/>
<input
:id="`${_uid}${index}`"
type="radio"
:name="`${_uid}-tab`"
:value="index + 1"
v-model="activeTab"
class="hidden"
/>
</li>
</ul>
<template v-for="(tab, index) in tabList">
<div
:key="index"
v-if="index + 1 === activeTab"
class="flex-grow bg-white rounded-lg shadow-xl p-4"
>
<!-- <slot :name="`tabPanel-${index + 1}`" /> --> want to get data using loop inside tab
</div>
</template>
</div>
</template>
App.vue
< script >
import AppTabs from "./components/AppTabs";
export default {
components: {
AppTabs,
},
data() {
return {
tabList: ["Tab 1", "Tab 2", "Tab 3", "Tab 4"],
};
},
}; <
/script>
<template>
<div class="bg-gradient-to-r from-blue-300 to-blue-500 min-h-screen">
<app-tabs
class="w-11/12 lg:w-10/12 mx-auto"
:tabList="tabList"
variant="horizontal"
>
{{value}} // want to bind data inside tab
</app-tabs>
</div>
</template>
Expected Output
I am working on vertical tabs. Where the functionality is working fine. Here is the complete working code with static mock data https://codesandbox.io/s/vue-js-tabs-forked-we2cx?file=/src/App.vue
Now i want to create some mockdata inside of my data like 'tabList' and then, i want to display data dynamically when user clicked on tabs(including content -->tabs)
How to remove static data, which is inside slots and then only use data dynamically
To start with that, i am not sure, Where to start the looping to display data inside tabs dynamically with mock data?
You can set dynamic slot name using :slot="slotName" where slotName is a dynamic value
This can be achieved using a v-for aswell like below.
<template v-for="content in contentList" :slot="content.slot">
{{ content.content }}
</template>
Where contentList is your array something like below.
contentList: [
{ id: 1, slot: "tabPanel-1", content: "Content 1" },
{ id: 2, slot: "tabPanel-2", content: "Content 2" },
{ id: 3, slot: "tabPanel-3", content: "Content 3" },
{ id: 4, slot: "tabPanel-4", content: "Content 4" },
]
Working Fiddle

Vue.js use component in another

I have a component that I would like to use in another in order to make a navigation menu. I want separate the menu from links. How can I display data in a child component in another parent component? I use Vue.js CDN.
I am a newbie in using vue.js. Thank you for your help.
index.html
<div id="navbar"><navbar-div></navbar-div></div>
navbar.js:
Vue.component('navbar-content', {
props: ['name', 'link'],
template: '<a class="navbar-item" v-bind:href="link">{{ name }}</a>'
})
Vue.component('navbar-div', {
props: ['links'],
template: `
<nav class="navbar is-dark">
<div class="navbar-brand">[...]</div>
<div class="navbar-menu">
<nav class="navbar-start">
<navbar-content v-for="item in links"
v-bind:key="item.id"
v-bind:name="item.name"
v-bind:link="item.link">
</navbar-content>
</nav>
</div>
</nav>
`})
new Vue({
el: "#navbar",
data: {
links: [
{id: 1, name: "Item 1", link: "link1"},
{id: 2, name: "Item 2", link: "link2"}
],
}
})
In your HTML you have to bind the links to navbar-div; otherwise it's not going to receive the data as props
Vue.component('navbar-content', {
props: ['name', 'link'],
template: `
<a
class="navbar-item"
:href="link"
>
{{ name }}
</a>
`
})
Vue.component('navbar-div', {
props: ['links'],
template: `
<nav class="navbar is-dark">
<div class="navbar-brand">[...]</div>
<div class="navbar-menu">
<nav class="navbar-start">
<navbar-content
v-for="item in links"
:key="item.id"
:name="item.name"
:link="item.link"
/>
</nav>
</div>
</nav>
`
})
new Vue({
el: "#navbar",
data: {
links: [{
id: 1,
name: "Item 1",
link: "link1"
},
{
id: 2,
name: "Item 2",
link: "link2"
}
],
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="navbar">
<navbar-div :links="links"></navbar-div>
</div>

(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;
});
}

How to display a datatable through an accordion

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

Categories