i've been struggling for almost 6 hours with this, but i didn't find a way to accomplish what i need.
the thing is that i need to filter the table of bootstrap vue with a function, but i can't make it work, there is a step that i need, but i can't find it.
here it is a jsfiddle that i grab from the web, that has the simple b-table with property filter.
https://jsfiddle.net/rothariger/oxbrhcqk/1/
now if you check on this example I've replaced the property for a function, but, it just get called in the load, when you change the filter textbox, it doesn't do anything, and here is where i think i'm missing a step.
https://jsfiddle.net/rothariger/oxbrhcqk/3/
new Vue({
el: '#app',
data: {
items: [{
isActive: true,
age: 40,
name: {
first: 'Dickerson',
last: 'Macdonald'
}
}, {
isActive: false,
age: 21,
name: {
first: 'Larsen',
last: 'Shaw'
}
}, {
isActive: false,
age: 9,
state: 'success',
name: {
first: 'Mitzi',
last: 'Navarro'
}
}, {
isActive: false,
age: 89,
name: {
first: 'Geneva',
last: 'Wilson'
}
}, {
isActive: true,
age: 38,
name: {
first: 'Jami',
last: 'Carney'
}
}, {
isActive: false,
age: 27,
name: {
first: 'Essie',
last: 'Dunlap'
}
}, {
isActive: true,
age: 40,
name: {
first: 'Dickerson',
last: 'Macdonald'
}
}, {
isActive: false,
age: 21,
name: {
first: 'Larsen',
last: 'Shaw'
}
}, {
isActive: false,
age: 26,
name: {
first: 'Mitzi',
last: 'Navarro'
}
}, {
isActive: false,
age: 22,
name: {
first: 'Geneva',
last: 'Wilson'
}
}, {
isActive: true,
age: 38,
name: {
first: 'Jami',
last: 'Carney'
}
}, {
isActive: false,
age: 27,
name: {
first: 'Essie',
last: 'Dunlap'
}
}],
fields: {
name: {
label: 'Person Full name',
sortable: true
},
age: {
label: 'Person age',
sortable: true
},
isActive: {
label: 'is Active'
},
actions: {
label: 'Actions'
}
},
currentPage: 1,
perPage: 5,
filter: null
},
methods: {
details(item) {
alert(JSON.stringify(item));
},
filterGrid(val) {
console.log(val);
return true;
}
}
})
any info on what i have to do?
regards.
As Vue Bootstrap introdues:,
The filter prop value can be a string, a RegExp or a function
reference. If a function is provided, the first argument is the
original item record data object. The function should return true if
the record matches your criteria or false if the record is to be
filtered out.
Then look into the source code at Vue Bootstrap Github, you will find Vue Bootstrap only invoke your filter function inside one computed property=computedItems.
So for your case:
filterGrid(val) {
console.log(val);
return true;
}
It does not trigger any reactivity (print then always return true), so it will do nothing (computed property=computedItems will not be re-calculated, so filter function will not be executed either).
so one simple filter function with reactivity will be like:
filterGrid(val){
return !this.filter || JSON.stringify(val).includes(this.filter)
}
Also you can involves other data/prop/computed property, the filter function will be executed also when reactivity is triggered.
The full demo:
new Vue({
el: '#app',
data: {
items: [{
isActive: true,
age: 40,
name: {
first: 'Dickerson',
last: 'Macdonald'
}
}, {
isActive: false,
age: 21,
name: {
first: 'Larsen',
last: 'Shaw'
}
}, {
isActive: false,
age: 9,
state: 'success',
name: {
first: 'Mitzi',
last: 'Navarro'
}
}, {
isActive: false,
age: 89,
name: {
first: 'Geneva',
last: 'Wilson'
}
}, {
isActive: true,
age: 38,
name: {
first: 'Jami',
last: 'Carney'
}
}, {
isActive: false,
age: 27,
name: {
first: 'Essie',
last: 'Dunlap'
}
}, {
isActive: true,
age: 40,
name: {
first: 'Dickerson',
last: 'Macdonald'
}
}, {
isActive: false,
age: 21,
name: {
first: 'Larsen',
last: 'Shaw'
}
}, {
isActive: false,
age: 26,
name: {
first: 'Mitzi',
last: 'Navarro'
}
}, {
isActive: false,
age: 22,
name: {
first: 'Geneva',
last: 'Wilson'
}
}, {
isActive: true,
age: 38,
name: {
first: 'Jami',
last: 'Carney'
}
}, {
isActive: false,
age: 27,
name: {
first: 'Essie',
last: 'Dunlap'
}
}],
fields: {
name: {
label: 'Person Full name',
sortable: true
},
age: {
label: 'Person age',
sortable: true
},
isActive: {
label: 'is Active'
},
actions: {
label: 'Actions'
}
},
currentPage: 1,
perPage: 5,
filter: null
},
methods: {
details(item) {
alert(JSON.stringify(item));
},
filterGrid(val){
return !this.filter || JSON.stringify(val).includes(this.filter)
}
}
})
<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#latest/dist/bootstrap-vue.css"/>
<!-- Add this after vue.js -->
<script src="https://unpkg.com/vue#2.5.17/dist/vue.min.js"></script>
<script src="https://unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<div class="justify-content-centermy-1 row">
<b-form-fieldset horizontal label="Rows per page" class="col-6" :label-size="6">
<b-form-select :options="[{text:5,value:5},{text:10,value:10},{text:15,value:15}]" v-model="perPage">
</b-form-select>
</b-form-fieldset>
<b-form-fieldset horizontal label="Filter" class="col-6" :label-size="2">
<b-form-input v-model="filter" placeholder="Type to Search"></b-form-input>
</b-form-fieldset>
</div>
<!-- Main table element -->
<b-table striped hover :items="items" :fields="fields" :current-page="currentPage" :per-page="perPage" :filter="filterGrid">
<template slot="name" scope="item">
{{item.value.first}} {{item.value.last}}
</template>
<template slot="isActive" scope="item">
{{item.value?'Yes :)':'No :('}}
</template>
<template slot="actions" scope="item">
<b-btn size="sm" #click="details(item.item)">Details</b-btn>
</template>
</b-table>
<div class="justify-content-center row my-1">
<b-pagination size="md" :total-rows="this.items.length" :per-page="perPage" v-model="currentPage" />
</div>
</div>
Related
I have a v-treeview component (Vuetify 2.6.7)
<v-treeview
class="location-tree"
v-model="location_tree"
ref="location_tree"
:search="location_search"
selectable
dense=true
open-on-click=true
expand-icon="$plus"
selected-color="success"
:items="locations"
selection-type="all"
transition=true
#input="location_tree_update"
></v-treeview>
How can I uncheck all checkboxes with a some button click?
you just need to empty the array you passed to v-model on a button click like location_tree.splice(0) or location_tree = [].
check the example below:
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
selection: [],
items: [{
id: 1,
name: 'Applications :',
children: [{
id: 2,
name: 'Calendar : app'
},
{
id: 3,
name: 'Chrome : app'
},
{
id: 4,
name: 'Webstorm : app'
},
],
},
{
id: 5,
name: 'Documents :',
children: [{
id: 6,
name: 'vuetify :',
children: [{
id: 7,
name: 'src :',
children: [{
id: 8,
name: 'index : ts'
},
{
id: 9,
name: 'bootstrap : ts'
},
],
}, ],
},
{
id: 10,
name: 'material2 :',
children: [{
id: 11,
name: 'src :',
children: [{
id: 12,
name: 'v-btn : ts'
},
{
id: 13,
name: 'v-card : ts'
},
{
id: 14,
name: 'v-window : ts'
},
],
}, ],
},
],
},
{
id: 15,
name: 'Downloads :',
children: [{
id: 16,
name: 'October : pdf'
},
{
id: 17,
name: 'November : pdf'
},
{
id: 18,
name: 'Tutorial : html'
},
],
},
{
id: 19,
name: 'Videos :',
children: [{
id: 20,
name: 'Tutorials :',
children: [{
id: 21,
name: 'Basic layouts : mp4'
},
{
id: 22,
name: 'Advanced techniques : mp4'
},
{
id: 23,
name: 'All about app : dir'
},
],
},
{
id: 24,
name: 'Intro : mov'
},
{
id: 25,
name: 'Conference introduction : avi'
},
],
},
],
}),
});
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<div id="app">
<v-app>
<v-container>
<v-btn color="primary" #click="selection.splice(0)">clear selection</v-btn>
<v-treeview v-model="selection" selectable :items="items"></v-treeview>
</v-container>
</v-app>
</div>
I am currently trying to show/hide dynamically with a bootstrap-vue table (https://bootstrap-vue.js.org/docs/components/table).
So far, I only managed to hide the header but the cells won't disappear, which is a problem because the cells are not in front of the good headers (the numbers should be under days and 'ingredient' should be under item.
Here what's 'worked':
fields: [
{key: "day", label: "Day",sortable: true, thStyle:"display:none"},
{key: "item", label: "Item",sortable: true}
]
I was wondering if it was possible to maybe do that outside of vueJS, and change the properties of the column with CSS directly..
Thanks for the help !
Louis
You could add an additional property to your fields. For example visible and create a computed property that filters out all fields with visible: false.
That way you can simply toggle this property to show/hide your columns.
window.onload = () => {
new Vue({
el: '#app',
computed: {
visibleFields() {
return this.fields.filter(field => field.visible)
}
},
data() {
return {
items: [
{ id: 1, first: 'Mike', last: 'Kristensen', age: 16 },
{ id: 2, first: 'Peter', last: 'Madsen', age: 52 },
{ id: 3, first: 'Mads', last: 'Mikkelsen', age: 76 },
{ id: 4, first: 'Mikkel', last: 'Hansen', age: 34 },
],
fields: [
{ key: 'id', label: 'ID', visible: true },
{ key: 'first', label: 'First Name', visible: true },
{ key: 'last', label: 'Last Name', visible: true },
{ key: 'age', label: 'Age', visible: true },
]
}
}
})
}
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#2.2.2/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.2.2/dist/bootstrap-vue.min.js"></script>
<div id='app'>
<b-checkbox
:disabled="visibleFields.length == 1 && field.visible"
v-for="field in fields"
:key="field.key"
v-model="field.visible"
inline
>
{{ field.label }}
</b-checkbox>
<br /><br />
<b-table :items="items" :fields="visibleFields" bordered>
</b-table>
</div>
I'm trying to enable some operations on my grid such as grouping, filtering and sorting, individually they works as shown in the docs but there is no an example of those functionality working together.
By myself I was able to combine sorting and filtering but grouping does not work when i'm adding it as it shown in the docs. look at at my code
<template>
<div>
<Grid :style="{height: '100%'}"
ref="grid"
:data-items="getData"
:resizable="true"
:reorderable="true"
#columnreorder="columnReorder"
:filterable="true"
:filter="filter"
#filterchange="filterChange"
:sortable="true"
:sort= "sort"
#sortchange="sortChangeHandler"
:groupable="true"
:group= "group"
#dataStateChange="dataStateChange"
:columns="columns">
</Grid>
</div>
</template>
<script>
export default {
data() {
return {
items: [],
editID: null,
columns: [
{ field: 'AbsenceEmployeID', filterable:false, editable: false, title: '#'},
{ field: 'Employe', title: 'Employer', cell: DropDownEmployes},
{ field: 'Remarque', title: 'Remarque'},
{ field: 'Type', title: 'Type', cell: DropDownTypes},
{ field: 'CreatedDate', filter:'date', editable: false, editor: 'date', title: 'créé le', format: '{0:d}'},
{ title: 'Actions', filterable:false, cell: CommandCell}
],
filter: {
logic: "and",
filters: []
},
sort: [
{ field: 'CreatedDate', dir: 'desc' }
],
group: [],
gridData: []
}
}
mounted() {
this.loadItems()
},
computed: {
absencesList() {
return this.items.map((item) => Object.assign({ inEdit: item.AbsenceEmployeID === this.editID}, item));
},
getData() {
return orderBy(filterBy(this.absencesList, this.filter), this.sort);
},
...mapState({
absences: state => state.absences.absences
})
}
methods: {
loadItems () {
this.$store.dispatch('absences/getAbsences')
.then(resp => {
this.items = this.absences.map(item => item)
})
},
filterChange: function(ev) {
this.filter = ev.filter;
},
columnReorder: function(options) {
this.columns = options.columns;
},
sortChangeHandler: function(e) {
this.sort = e.sort;
},
// the following is for grouping but not yet used, read more
groupedData: function () {
this.gridData = process(this.getData, {group: this.group});
},
createAppState: function(dataState) {
this.group = dataState.group;
this.groupedData();
},
dataStateChange: function (event) {
this.createAppState(event.data);
},
}
}
</script>
The last three methods are not used yet, so filtering and sorting is working perfectly as of now. then in other to enable grouping I want to replace :data-items="getData" by :data-items="gridData" and run this.groupedData() method after the items are loaded but grouping doesn't work.
I think everything should be handle by the dataStateChange event and process() function but I also tried but without success
If you define the filterchange and sortchange events they are being triggered for filter and sort and you will have to updated data in their handlers. If you rather want to use datastatechage event for all the changes you have to remove the filterchange and sortchange events and the datastatechage event will be triggered instead of them. In this case you will have to update the data in its handler.
You can use the process method of #progress/kendo-data-query by passing the respective parameter each data change that is needed as in the example below:
const result = process(data, {
skip: 10,
take: 20,
group: [{
field: 'category.categoryName',
aggregates: [
{ aggregate: "sum", field: "unitPrice" },
{ aggregate: "sum", field: "unitsInStock" }
]
}],
sort: [{ field: 'productName', dir: 'desc' }],
filter: {
logic: "or",
filters: [
{ field: "discontinued", operator: "eq", value: true },
{ field: "unitPrice", operator: "lt", value: 22 }
]
}
});
Hers is a sample stackblitz example where such example is working correctly - https://stackblitz.com/edit/3ssy1k?file=index.html
You need to implement the groupchange method to handle Grouping
I prefer to use process from #progress/kendo-data-query
The following is a complete example of this
<template>
<Grid :style="{height: height}"
:data-items="gridData"
:skip="skip"
:take="take"
:total="total"
:pageable="pageable"
:page-size="pageSize"
:filterable="true"
:filter="filter"
:groupable="true"
:group="group"
:sortable="true"
:sort="sort"
:columns="columns"
#sortchange="sortChangeHandler"
#pagechange="pageChangeHandler"
#filterchange="filterChangeHandler"
#groupchange="groupChangeHandler"
/>
</template>
<script>
import '#progress/kendo-theme-default/dist/all.css';
import { Grid } from '#progress/kendo-vue-grid';
import { process } from '#progress/kendo-data-query';
const sampleProducts = [
{
'ProductID': 1,
'ProductName': 'Chai',
'UnitPrice': 18,
'Discontinued': false,
},
{
'ProductID': 2,
'ProductName': 'Chang',
'UnitPrice': 19,
'Discontinued': false,
},
{
'ProductID': 3,
'ProductName': 'Aniseed Syrup',
'UnitPrice': 10,
'Discontinued': false,
},
{
'ProductID': 4,
'ProductName': "Chef Anton's Cajun Seasoning",
'UnitPrice': 22,
'Discontinued': false,
},
];
export default {
components: {
Grid,
},
data () {
return {
gridData: sampleProducts,
filter: {
logic: 'and',
filters: [],
},
skip: 0,
take: 10,
pageSize: 5,
pageable: {
buttonCount: 5,
info: true,
type: 'numeric',
pageSizes: true,
previousNext: true,
},
sort: [],
group: [],
columns: [
{ field: 'ProductID', filterable: false, title: 'Product ID', width: '130px' },
{ field: 'ProductName', title: 'Product Name' },
{ field: 'UnitPrice', filter: 'numeric', title: 'Unit Price' },
{ field: 'Discontinued', filter: 'boolean', title: 'Discontinued' },
],
};
},
computed: {
total () {
return this.gridData ? this.gridData.length : 0;
},
},
mounted () {
this.getData();
},
methods: {
getData: function () {
this.gridData = process(sampleProducts,
{
skip: this.skip,
take: this.take,
group: this.group,
sort: this.sort,
filter: this.filter,
});
},
// ------------------Sorting------------------
sortChangeHandler: function (event) {
this.sort = event.sort;
this.getData();
},
// ------------------Paging------------------
pageChangeHandler: function (event) {
this.skip = event.page.skip;
this.take = event.page.take;
this.getData();
},
// ------------------Filter------------------
filterChangeHandler: function (event) {
this.filter = event.filter;
this.getData();
},
// ------------------Grouping------------------
groupChangeHandler: function (event) {
this.group = event.group;
this.getData();
},
},
};
</script>
Hi am trying to put this script on my file.
<template>
<b-table striped hover :items="items"></b-table>
</template>
<script>
const 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' },
{ isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' }
]
export default {
data () {
return {
items: items
}
}
}
</script>
in my file i take the data not from const items like the example but i use this script:
computed: {
siteObject() {
console.log(this.site);
return this.site;
},
how can put this info in my table ?
i use this to show my data:
<div v-for="(item, index) in siteObject.cases" :key="index" >
I'm using vue 2.5 and the library Bootstrap-vue.
I'm interested by the table component of this library: https://bootstrap-vue.js.org/docs/components/table
But I want to encapsulate this component to make my own component with custom configuration which I don't want to repeat. This way I don't have to manage pagination and filtering every time and I can add other features like data export.
So I created a table-helper component (only pagination is handled for now)
<template>
<div>
<b-table striped hover responsive
:items="items" :fields="fields"
:current-page="currentPage" per-page="10">
<slot></slot>
</b-table>
<b-pagination :total-rows="items.length" per-page="10" v-model="currentPage"></b-pagination>
</div>
</template>
<script>
import bTable from 'bootstrap-vue/es/components/table/table'
import bPagination from 'bootstrap-vue/es/components/pagination/pagination'
export default {
name: "table-helper",
props: ['items', 'fields'],
data() {
return {
currentPage: 1,
}
},
components: {
'b-table': bTable,
'b-pagination': bPagination
}
}
</script>
And I want to use my component like this (using the bootstrap-vue slot possibility to reformat columns):
<table-helper :items="users" :fields="fields">
<template slot="fullName" slot-scope="data">
{{data.item.first_name}} {{data.item.last_name}}
</template>
</table-helper>
Obviously it doesn't work (I get the table but not the formatted columns) because the <template slot="fullName" slot-scope="data"> refers to my custom component and not to the b-table component.
So I'd like to know a way to encapsulated a library component which uses slots and slot scopes like this.
Thank you for your help.
The Key points:
Template vs JSX: You may have to use JSX to implement it, especially for slot.
Slot: for your case, the slot of parent component will be the children VNodes of b-table, so reshape the slot to one array and change the context from parent to current (if not scopedSlot will be rendered incorrectly.), then put them into the third parameter of function=h (or you can call createElement). For the details, check Vue Guide: Create Element Arguments.
Anyway, read Best Way To Implement HOC carefully , then you should be able to reach the goal.
Vue.config.productionTip = false
Vue.component('table-helper', {
render (h) {
const slots = Object.keys(this.$slots)
.reduce((arr, key) => arr.concat(this.$slots[key]), [])
.map(vnode => {
vnode.context = this._self
return vnode
})
const self = this
return h('div', [
h('b-table', {
on: self.$listeners,
props: Object.assign(self.$props, {currentPage: self.currentPage}),
scopedSlots: self.$scopedSlots,
attrs: self.$attrs
},slots),
h('b-pagination', {
props: self.$props,
domProps: {
value: self.currentPage
},
on: {
input: function (event) {
self.currentPage = event
}
}
})
])
},
//mixins: [{bTable.props}, {bPagination.props}],
props: ['items', 'fields', 'perPage','totaRows'],
data() {
return {
currentPage: 1,
}
}
})
new Vue({
el: '#app',
data() {
return {
fields: [ 'first_name', 'last_name', 'age', 'fullName' ],
users: [
{ 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' },
{ isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' },
{ 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' },
{ isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' },
{ 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' },
{ isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' }
]
}
}
})
<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.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<script src="//unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue/dist/bootstrap-vue.js"></script>
<div id="app">
<table-helper :items="users" :fields="fields" :per-page="10" :total-rows="users.length">
<template slot="fullName" slot-scope="data">
{{data.item.first_name}} {{data.item.last_name}}
</template>
</table-helper>
</div>