Double footer on vue-boostrap table component - vue.js

I would like to implement a double footer for a bootstrap-vue table.
<b-table
striped hover
:items="items"
:fields="visibleFields"
:sort-compare="sortCompare"
:sort-by.sync="sortBy"
foot-clone
selectable
select-mode="single"
#row-selected="onRowSelected"
:tbody-tr-class="rowClass"
>
<!-- Footers total nutritional values -->
<template v-slot:foot(serving)="data">
<span>Total:</span>
</template>
</b-table>
The table looks like this :
Bootstrap vue documentation only provides this to create a footer :
<!-- Footers total nutritional values -->
<template v-slot:foot(serving)="data">
<span>Total:</span>
</template>
The problem is I don't see how can I add a second footer with this. Another way to do this would be to add a div just below the table and to display what I want but I think there is a cleaner way to do this.

You can use the custom-foot slot. This slot will render directly into the tfoot of the table, so you have free control to structure the foot however you want using tr and td
new Vue({
el: '#app',
data() {
return {
fields: [
// A column that needs custom formatting
{ key: 'name', label: 'Full Name' },
{ key: 'age', label: 'Age' },
{ key: 'sex', label: 'Sex' }
],
items: [
{ name: { first: 'John', last: 'Doe' }, sex: 'Male', age: 42 },
{ name: { first: 'Jane', last: 'Doe' }, sex: 'Female', age: 36 },
{ name: { first: 'Rubin', last: 'Kincade' }, sex: 'Male', age: 73 },
{ name: { first: 'Shirley', last: 'Partridge' }, sex: 'Female', age: 62 }
]
}
}
})
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#2.3.0/dist/bootstrap-vue.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.3.0/dist/bootstrap-vue.js"></script>
<div id="app">
<b-table :fields="fields" :items="items">
<template v-slot:cell(name)="data">
{{ data.value.first }} {{ data.value.last }}
</template>
<template v-slot:custom-foot>
<!-- You can customize this however you want, this is just as an example -->
<tr>
<th v-for="field in fields" :key="field.key">
{{ field.label }}
</th>
</tr>
<tr>
<td class="bg-dark text-white" :colspan="fields.length">
This is my second footer
</td>
</tr>
</template>
</b-table>
</div>

Related

How to get index of tr in Bootstrap Vue table?

When I use a v-for in vue I make like this:
v-for="(test, index) in tests"
How can I make the same using Bootstrap vue? I want to get the index of all the TR.
<b-table
:items="items"
:fields="columns"
>
Maybe you may want diffirent thing but You might want to look at the solution.
If primary-key is not provided, will auto-generate keys based on the displayed row's index number (i.e. position in the displayed table rows).
<template>
<div>
<b-table small :fields="fields" :items="items" responsive="sm">
<!-- A virtual column -->
<template #cell(index)="data">
{{ data.index + 1 }}
</template>
<!-- A custom formatted column -->
<template #cell(name)="data">
<b class="text-info">{{ data.value.last.toUpperCase() }}</b>, <b>{{ data.value.first }}</b>
</template>
<!-- A virtual composite column -->
<template #cell(nameage)="data">
{{ data.item.name.first }} is {{ data.item.age }} years old
</template>
<!-- Optional default data cell scoped slot -->
<template #cell()="data">
<i>{{ data.value }}</i>
</template>
</b-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: [
// A virtual column that doesn't exist in items
'index',
// A column that needs custom formatting
{ key: 'name', label: 'Full Name' },
// A regular column
'age',
// A regular column
'sex',
// A virtual column made up from two fields
{ key: 'nameage', label: 'First name and age' }
],
items: [
{ name: { first: 'John', last: 'Doe' }, sex: 'Male', age: 42 },
{ name: { first: 'Jane', last: 'Doe' }, sex: 'Female', age: 36 },
{ name: { first: 'Rubin', last: 'Kincade' }, sex: 'Male', age: 73 },
{ name: { first: 'Shirley', last: 'Partridge' }, sex: 'Female', age: 62 }
]
}
}
}
</script>

How to close previous row details when clicking a new one in bootstrap vue?

Following Bootstrap Vue documentation, I've following code:
<template>
<div>
<b-table :items="items" :fields="fields" striped responsive="sm">
<template #cell(show_details)="row">
<b-button size="sm" #click="row.toggleDetails" class="mr-2">
{{ row.detailsShowing ? 'Hide' : 'Show'}} Details
</b-button>
<!-- As `row.showDetails` is one-way, we call the toggleDetails function on #change -->
<b-form-checkbox v-model="row.detailsShowing" #change="row.toggleDetails">
Details via check
</b-form-checkbox>
</template>
<template #row-details="row">
<b-card>
<b-row class="mb-2">
<b-col sm="3" class="text-sm-right"><b>Age:</b></b-col>
<b-col>{{ row.item.age }}</b-col>
</b-row>
<b-row class="mb-2">
<b-col sm="3" class="text-sm-right"><b>Is Active:</b></b-col>
<b-col>{{ row.item.isActive }}</b-col>
</b-row>
<b-button size="sm" #click="row.toggleDetails">Hide Details</b-button>
</b-card>
</template>
</b-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: ['first_name', 'last_name', 'show_details'],
items: [
{ isActive: true, age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
{ isActive: false, age: 21, first_name: 'Larsen', last_name: 'Shaw' },
{
isActive: false,
age: 89,
first_name: 'Geneva',
last_name: 'Wilson',
_showDetails: true
},
{ isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' }
]
}
}
}
</script>
Full example could be found here.
According to this example, if I click Show Details button on each of the row it will show its respective row detail, but I want it to close all previous rows upon clicking it and only open details of the currently clicked row.
I know that looping through rows and setting detailsShowing to false can solve it like below:
this.rownames.forEach(item => {
this.$set(item, 'detailsShowing', false)
})
but I don't know how to do it. Thank You.
I feel the accepted answer is a bit over engineered, so I want to suggest what I think is a simpler solution.
This works by saving the "currently" open row in a variable, and setting _showDetails on it to false if another row is opened. This way we avoid having to do any sort of loops.
new Vue({
el: "#app",
data() {
return {
detailsRow: null,
items: [
{ age: 40, first_name: "Dickerson", last_name: "Macdonald" },
{ age: 21, first_name: "Larsen", last_name: "Shaw" },
{ age: 89, first_name: "Geneva", last_name: "Wilson" },
{ age: 38, first_name: "Jami", last_name: "Carney" }
]
};
},
methods: {
onRowClicked(item) {
const { detailsRow } = this
if (detailsRow && detailsRow !== item) {
detailsRow._showDetails = false;
}
this.$set(item, "_showDetails", !item._showDetails);
this.detailsRow = item;
}
}
});
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.css" />
<script src="https://unpkg.com/vue#2.6.2/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.js"></script>
<div id="app">
<b-table :items="items" #row-clicked="onRowClicked">
<template #row-details="{ item }">
<pre>{{ item }}</pre>
</template>
</b-table>
</div>
Add a new data property called visibleRow and set it to null.
Then create a computed getter that returns your items but has the _showDetails property set. The value should be false by default unless visibleRow matches the item:
computed: {
tableItems () {
return this.items.map((item) => {
return Object.assign({}, item, {
_showDetails: this.visibleRow && this.visibleRow.first_name === item.first_name && this.visibleRow.last_name === item.last_name
})
})
}
}
I've taken some liberties in determining what makes your rows unique but you're free to use whatever you want in the above comparison
Create a new method and modify your row click function to set the visibleRow on toggle:
methods: {
setVisibleRow (row) {
this.$set(this, 'visibleRow', row)
}
}
Update your click method:
#click="setVisileRow(row.item._showDetails ? null : row.item)"
Finally, update your items binding on b-table:
<b-table :items="tableItems"

Split row-details into multiple row in bootstrap-vue

I am currently writing my first full-stack app. I am using bootstrap <b-table> to display content. On row-click, I expand the row to display nested data. Is there a way to iterate over the nested data and display it in nested rows within the parent b-table?
Currently, I can display the data, however it displays in a single row.
component.vue:
<template>
<div id="report-table" class="report-table">
<b-container>
<b-table striped hover sticky-header="100%"
:items="reports"
:fields="fields"
responsive="xl"
#click="clearRowClick"
#row-clicked="reports=>$set(reports, '_showDetails', !reports._showDetails)"
>
<template slot="row-details" slot-scope="row">
<template v-for="(proc, index) in row.item.Processes">
<b-tr :key=index>
<td>{{ proc.Name }}</td>
<td>{{ proc.Id }}</td>
</b-tr>
</template>
</template>
</b-table>
</b-container>
</div>
</template>
example
In the attached image, the bottom row has been clicked. The content is displayed within a single row, but I would like it to be separate rows, so later I can further click on them to display even more nested content.
data example:
{"_id": <id>, "Hostname": <hostname>, "Address": <address>, "Processes": [{"Name": ApplicationHost, ...}, {"Name": svchost, ...}]
If this is not possible, is there some other Bootstrap element that makes more sense to achieve what I want?
To strictly answer your question: no, a BootstrapVue <b-table>'s row-details row can't be expanded into more than one row.
The row-details row has severe limitations:
it's only one row
it's actually only one cell which, through use of colspan is expanded to the full width of the row (which means you can't really use the table columns to align the content of the row-details row).
But... this is web. In web, because it's virtual, virtually anything is possible. When it's not, you're doing-it-wrong™.
What you want is achievable by replacing rows entirely when a row is expanded, using a computed and concatenating the children to their parent row when the parent is in expanded state. Proof of concept:
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#app',
data: () => ({
rows: [
{id: '1', name: 'one', expanded: false, children: [
{id: '1.1', name: 'one-one'},
{id: '1.2', name: 'one-two'},
{id: '1.3', name: 'one-three'}
]},
{id: '2', name: 'two', expanded: false, children: [
{id: '2.1', name: 'two-one'},
{id: '2.2', name: 'two-two'},
{id: '2.3', name: 'two-three'}
]}
]
}),
computed: {
renderedRows() {
return [].concat([...this.rows.map(row => row.expanded
? [row].concat(row.children)
: [row]
)]).flat()
}
}
})
tr.parent { cursor: pointer }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<tr v-for="row in renderedRows" :key="row.id"
#click="row.children && (row.expanded = !row.expanded)"
:class="{parent: row.children}">
<td>{{row.id}}</td>
<td>{{row.name}}</td>
</tr>
</table>
</div>
The example is rather basic (I haven't added BootstrapVue to it, nor have I used its fancy <b-table>), but it demonstrates the principle. Apply it to <b-table>'s :items.
One could even take it a step further and make it recursive, by moving the expansion logic into a method:
new Vue({
el: '#app',
data: () => ({
fields: ['id', { key: 'expanded', label: ''}, 'name'],
rows: [{
id: '1',
name: 'one',
expanded: false,
children: [
{ id: '1.1', name: 'one-one' },
{ id: '1.2', name: 'one-two' },
{
id: '1.3',
name: 'one-three',
expanded: false,
children: [
{ id: '1.3.1', name: 'one-three-one' },
{ id: '1.3.2', name: 'one-three-two' }
]
}
]
},
{
id: '2',
name: 'two',
expanded: false,
children: [
{ id: '2.1', name: 'two-one' },
{ id: '2.2', name: 'two-two' },
{ id: '2.3', name: 'two-three' }
]
}
]
}),
computed: {
items() {
return [].concat(this.rows.map(row => this.unwrapRow(row))).flat()
}
},
methods: {
unwrapRow(row) {
return row.children && row.expanded
? [row].concat(...row.children.map(child => this.unwrapRow(child)))
: [row]
},
tbodyTrClass(row) {
return { parent: row.children?.length, child: row.id.includes('.') }
}
}
})
.table td:not(:last-child) { width: 80px; }
.table .bi { cursor: pointer }
tr.child {
background-color: #f5f5f5;
font-style: italic;
}
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.css" />
<script src="//cdn.jsdelivr.net/npm/vue#2.6.14"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue-icons.min.js"></script>
<div id="app">
<b-table :items="items"
:fields="fields"
:tbody-tr-class="tbodyTrClass">
<template #cell(expanded)="{item}">
<b-icon v-if="item.children"
:icon="item.expanded ? 'chevron-up' : 'chevron-down'"
#click="item.expanded = !item.expanded" />
</template>
</b-table>
</div>
One approach (that I've personally used in the past) is simply to put a nested <b-table> inside your child row-details for child data, instead of trying to add them to the outer table.
It's also worth noting that adding child data rows to the outer table could be visually confusing if they don't look distinct enough from their parents.
Example:
new Vue({
el: '#app',
data() {
return {
reports: [{_id: 'ID#1', Hostname: 'Host1', Address: 'Addr1', Processes: [{Name: 'ApplicationHost', Id: '1'}, {Name: 'svchost', Id: '2'}]},
{_id: 'ID#2', Hostname: 'Host2', Address: 'Addr2', Processes: [{Name: 'ApplicationHost', Id: '3'}, {Name: 'svchost', Id: '4'}]},],
fields: ['Hostname', 'Address'],
}
},
});
<!-- Import Vue and Bootstrap-Vue -->
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap#4/dist/css/bootstrap.min.css" /><link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.css" /><script src="//unpkg.com/vue#latest/dist/vue.min.js"></script><script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<div id="app">
<b-table
bordered
striped
hover
:items="reports"
:fields="fields"
#row-clicked="reports=>$set(reports, '_showDetails', !reports._showDetails)"
>
<!-- <b-table> nested inside 'row-details' slot: -->
<template #row-details="row">
<b-table
bordered
:items="row.item.Processes"
:fields="['Name', 'Id']"
></b-table>
</template>
</b-table>
</div>

How to create a chart in a cell of the Bootstrap-vue tables?

I'm trying to use Bootstrap-vue tables. I'm trying to add a column "chart" where I can show the data in a chart. Something like:
I'm interested in the "graph" type chart (not shown in the image). Is it something that can be done with the Bootstrap-vue tables?
slot:cell
Here is the code from bootstrap-vue.org:
Inside the template you can put your chart element. And use the 'data' to get the information that you want. Like data.item is the entire that for that row
<template>
<div>
<b-table small :fields="fields" :items="items" responsive="sm">
<!-- A virtual column -->
<template v-slot:cell(index)="data">
{{ data.index + 1 }}
</template>
<!-- A custom formatted column -->
<template v-slot:cell(name)="data">
<b class="text-info">{{ data.value.last.toUpperCase() }}</b>, <b>{{ data.value.first }}</b>
</template>
<!-- A virtual composite column -->
<template v-slot:cell(nameage)="data">
{{ data.item.name.first }} is {{ data.item.age }} years old
</template>
<!-- Optional default data cell scoped slot -->
<template v-slot:cell()="data">
<i>{{ data.value }}</i>
</template>
</b-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: [
// A virtual column that doesn't exist in items
'index',
// A column that needs custom formatting
{ key: 'name', label: 'Full Name' },
// A regular column
'age',
// A regular column
'sex',
// A virtual column made up from two fields
{ key: 'nameage', label: 'First name and age' }
],
items: [
{ name: { first: 'John', last: 'Doe' }, sex: 'Male', age: 42 },
{ name: { first: 'Jane', last: 'Doe' }, sex: 'Female', age: 36 },
{ name: { first: 'Rubin', last: 'Kincade' }, sex: 'Male', age: 73 },
{ name: { first: 'Shirley', last: 'Partridge' }, sex: 'Female', age: 62 }
]
}
}
}
</script>
Here is the documentation
https://bootstrap-vue.org/docs/components/table#tables
Go to Custom data rendering

Why bootstrap table example not working for me?

I have a some project with table like this bootstrap table.
Codesandbox
template:
<b-table small :fields="fields" :items="items">
<template v-slot:cell(index)="data">
{{ data.index + 1 }}
</template>
<!-- A custom formatted column -->
<template v-slot:cell(name)="data">
<b class="text-info">{{ data.value.last.toUpperCase() }}</b>, <b>{{ data.value.first }}</b>
</template>
<!-- A virtual composite column -->
<template v-slot:cell(nameage)="data">
{{ data.item.name.first }} is {{ data.item.age }} years old
</template>
<!-- Optional default data cell scoped slot -->
<template v-slot:cell()="data">
<i>{{ data.value }}</i>
</template>
</b-table>
And script:
fields: [
// A virtual column that doesn't exist in items
'index',
// A column that needs custom formatting
{ key: 'name', label: 'Full Name' },
// A regular column
'age',
// A regular column
'sex',
// A virtual column made up from two fields
{ key: 'nameage', label: 'First name and age' }
],
items: [
{ name: { first: 'John', last: 'Doe' }, sex: 'Male', age: 42 },
{ name: { first: 'Jane', last: 'Doe' }, sex: 'Female', age: 36 },
{ name: { first: 'Rubin', last: 'Kincade' }, sex: 'Male', age: 73 },
{ name: { first: 'Shirley', last: 'Partridge' }, sex: 'Female', age: 62 }
]
Bootstrap table works. I am copy this code and example not work. And I do not understand why.
Question: So, Why bootstrap table example not working?
Helo, i have same problem.
I have some custom fields table, on vue devtools the data has been seen. in vuex bindings.
But on custom table no data can appear, just the amount of data available.
This is my template code
<template>
<div class="table-responsive">
<b-table striped hover :items="todos.data" :fields="fields" show-empty>
<template slot="jadwal" slot-scope="row">
<td class="parent-row">{{ row.item.name }}</td>
</template>
</b-table>
</div>
</template>
and this object vuex binding
{"current_page":1,"data":[{"id":1,"name":"Belajar Vue Laravel","note":"Apa aja boleh deh ini deskripsinnya","due_date":"2019-09-30","status":0,"created_at":"2019-09-29 18:23:51","updated_at":"2019-09-29 18:23:51"},{"id":2,"name":"Belajar mengerti kamu","note":"you are my everythings","due_date":"2019-10-01","status":1,"created_at":"2019-09-29 18:23:51","updated_at":"2019-09-29 18:23:51"}],"first_page_url":"http://localhost:8000/api/todo?page=1","from":1,"last_page":1,"last_page_url":"http://localhost:8000/api/todo?page=1","next_page_url":null,"path":"http://localhost:8000/api/todo","per_page":10,"prev_page_url":null,"to":2,"total":2}