BootstrapVue table pagination not rendering results properly - vue.js

I'm using bootstrap-vue with version 2.9.0. When navigating to another page, the result does not display unless I click on to another page then the previous page will show up then immediately show the current page, which I find it really weird.
Here's a snippet:
<b-table
:items="dataTable.customerList"
:fields="dataTable.fields"
:per-page="dataTable.perPage"
:current-page="dataTable.currentPage"
:sort-by.sync="dataTable.sortBy"
:sort-desc.sync="dataTable.sortDesc"
:no-local-sorting="true"
#sort-changed="sortingChanged"
responsive
striped
hover
show-empty
empty-text="Loading..."
id="tbl-customer-list"
busy.sync="true"
>
</b-table>
<b-pagination
v-model="dataTable.currentPage"
:total-rows="dataTable.totalRows"
:per-page="dataTable.perPage"
#input="getCustomers(dataTable.currentPage)"></b-pagination>
And the JS:
export default {
page: {
title: 'Customers',
meta: [{ name: 'description', content: appConfig.description }],
},
components: {
Layout,
PageHeader,
},
methods: {
getCustomers(currentPage) {
this.busy = true;
customerList({page: currentPage}).then((response) => {
this.dataTable.customerList = response.data;
this.dataTable.totalRows = response.total;
this.dataTable.currentPage = response.current_page;
this.dataTable.perPage = response.per_page;
this.dataTable.lastPage = response.last_page;
this.busy = false;
})
},
sortingChanged(ctx) {
console.log(ctx);
this.$root.$emit('bv::table::refresh', 'tbl-customer-list')
},
},
mounted() {
this.getCustomers();
},
data() {
return {
title: 'Customers',
busy: false,
dataTable: {
customerList: [],
fields: [
{ key: 'customer_name', label: 'Full Name', sortable: true },
{ key: 'actions', sortable: false },
],
totalRows: 0,
currentPage: 1,
perPage: 0,
lastPage: 1,
sortBy: 'first_name',
sortDesc: false,
filter: null,
filterOn: [],
},
}
},
}
When I remove :current-page="dataTable.currentPage" on the b-table, it works fine but I when clicking on the row headers will always display the current page as 1. (See sortingChanged)

Related

Cannot read properties of undefined on formatter, but data is showing fine

I have a bootstrap table that shows a list of appliances. I am importing my data with Axios and for this specific table I am outputting data from two database tables, so I have one object which is called applianceReferences which stores another object called activeAppliances.
Not sure if it is relevant for this question, but just so you know.
Before talking about the problem, let me just post the whole code and below I will talk about the section that is giving me issues.
<template>
<b-container class="my-2">
<b-card v-if="showTable" class="ml-4 mr-4">
<b-table
search-placeholder="search"
:filter-included-fields="fields.map(f => f.key)"
include-filter
:items="applianceReferences"
:fields="fields"
/>
</b-card>
</b-container>
</template>
<script>
import {applianceService} from "#/services/appliance";
import CommonCollapsible from "#/components/common/CommonCollapsible";
import moment from 'moment';
export default {
components: { CommonCollapsible, CommonTable },
props: {
ownerId: String,
ownerType: String,
showDocuments: Boolean,
goToAppliances: "",
importAppliances: ""
},
data() {
return {
applianceReferences: [],
showTable: true
}
},
computed: {
fields() {
return [
{
key: 'referenceName',
label: this.$t('referenceName'),
sortable: true
},
{
key: 'activeAppliance.type',
label: this.$t('type'),
sortable: true,
},
{
key: 'activeAppliance.brandName',
label: this.$t('brand'),
sortable: true
},
{
key: 'activeAppliance.purchaseDate',
label: this.$t('purchaseDate'),
sortable: true,
template: {type: 'date', format: 'L'}
},
{
key: 'activeAppliance.warrantyDuration',
label: this.$t('warrantyDuration'),
sortable: true,
formatter: (warrantyDuration, applianceId, appliance) =>
this.$n(warrantyDuration) + ' ' +
this.$t(appliance.activeAppliance.warrantyDurationType ?
`model.appliance.warrantyDurationTypes.${appliance.activeAppliance.warrantyDurationType}` :
''
).toLocaleLowerCase(this.$i18n.locale),
sortByFormatted: (warrantyDuration, applianceId, appliance) =>
appliance.activeAppliance.warrantyDurationType === 'YEARS' ? warrantyDuration * 12 : warrantyDuration
},
{
key: 'activeAppliance.purchaseAmount',
label: this.$t('amount'),
sortable: true,
template: {
type: 'number', format: {minimumFractionDigits: '2', maximumFractionDigits: '2'},
foot: sum
}
},
{
key: 'actions',
template: {
type: 'actions',
head: [
{
text: 'overviewOfAppliances',
icon: 'fas fa-fw fa-arrow-right',
action: this.createAppliance
},
{
icon: 'fas fa-fw fa-file-excel',
action: this.importAppliance,
tooltip: this.$t('importAppliances'),
}
],
cell: [
{
icon: 'fa-trash',
variant: 'outline-danger',
action: this.remove
},
]
}
}
]
},
},
methods: {
load() {
Object.assign(this.$data, this.$options.data.apply(this));
this.applianceReferences = null;
applianceService.listApplianceReferences(this.ownerId).then(({data: applianceReferences}) => {
this.applianceReferences = applianceReferences;
this.applianceReferences.forEach( reference => {
applianceService.listAppliances(reference.id).then(result => {
this.$set(reference, 'appliances', result.data);
this.$set(reference, 'activeAppliance', result.data.find(appliance => appliance.active))
this.loaded = true
})
})
}).catch(error => {
console.error(error);
})
},
createAppliance(){
this.goToAppliances()
},
importAppliance(){
this.importAppliances()
},
},
watch: {
ownerId: {
immediate: true,
handler: 'load'
}
},
}
</script>
Okay, so the error occurs in this specific property:
{
key: 'activeAppliance.warrantyDuration',
label: this.$t('warrantyDuration'),
sortable: true,
formatter: (warrantyDuration, applianceId, appliance) =>
this.$n(warrantyDuration) + ' ' +
this.$t(appliance.activeAppliance.warrantyDurationType ?
`model.appliance.warrantyDurationTypes.${appliance.activeAppliance.warrantyDurationType}` :
''
).toLocaleLowerCase(this.$i18n.locale),
sortByFormatted: (warrantyDuration, applianceId, appliance) =>
appliance.activeAppliance.warrantyDurationType === 'YEARS' ? warrantyDuration * 12 : warrantyDuration
},
What I am basically doing here is combining two values from the object: warrantyDuration and warrantyDurationType and putting them in one single row in my bootstrap table.
The problem is that this is giving me an error: Cannot read properties of undefined (reading 'warrantyDurationType'
Yet the data actually outputs normally.
So what exactly does it want me to do?
I tried wrapping a v-if around the table to make sure that the application checks if the data exist before outputting it, but this does not solve the issue.
<div v-if="applianceReferences && applianceReferences.activeAppliance">
<b-card v-if="showTable" class="ml-4 mr-4">
<common-table
search-placeholder="search"
:filter-included-fields="fields.map(f => f.key)"
include-filter
:items="applianceReferences"
:fields="fields"
/>
</b-card>
</div>
Last, just to give you a full overview, my array looks like this:
Any ideas?

Display Selected values in v-select multiple

I am new in vue js. Using vue2, I have a v-select implemented on my site now, I want to select multiple values and save and show them while editing. But I can't show multiple values properly using :reduce
Here is my code:
<v-select name="allowed_extensions"
:reduce="allowed_extensions => allowed_extensions.value"
multiple
:closeOnSelect="false"
v-model="form.allowed_extensions"
:options="file_options"
v-validate="'required'" > </v-select>
In js:
data () {
return {
isDisabled: false, //Submit Button
form: {
maximum_file_size: '',
allowed_extensions: ''
},
be_errors: {},
// Options
file_options: [
{ label: 'doc', value: 'doc' },
{ label: 'docx', value: 'docx' },
{ label: 'pdf', value: 'pdf' },
{ label: 'txt', value: 'txt' },
{ label: 'gif', value: 'gif' },
{ label: 'png', value: 'png' },
{ label: 'jpg', value: 'jpg' },
{ label: 'jpeg', value: 'jpeg' }
]
}
}
IN Mysql DB, sample data saved as : ["doc","txt"]
But when I want to display them in edit, it showing wrongly in a single tag.
How can I solve this

VueJS SideMenuBar component

I am using SideMenuBar from https://www.npmjs.com/package/vue-sidebar-menu to generate a side menu for my app. The elements in the bar should be dynamic based on whether the person is logged-in. The problem I have is that when I login, the items arent reactive - I need to refresh my browser for the menu to refresh. Any idea what I am doing wrong?
<div id="app">
<sidebar-menu :menu="dynamicMenu" #toggle-collapse="onToggleCollapse" #item-click="onItemClick" :collapsed="collapseMenu" >
</sidebar-menu>
<router-view />
computed: {
isAuthorized: function() {
return UserStorageService.GetUser() === null ? false : true;
},
dynamicMenu() {
return [
{
header: true,
title: "Menu",
hiddenOnCollapse: true
},
{
href: { path: "/" },
title: "Home",
icon: "fas fa-home"
},
{
href: { path: "/Dashboard" },
title: "Dashboard",
icon: "fas fa-folder",
hidden: !this.isAuthorized
},
{
href: { path: "/" },
title: "Reporting",
icon: "fas fa-archive",
hidden: !this.isAuthorized
},
{
href: { path: "/" },
title: "My profile",
icon: "fas fa-share-alt",
hidden: !this.isAuthorized
}
]
}
},
When I sign-in, the value of "isAuthorized" should change to true. That value should then be assigned to my dynamic menu (the inverse) and should toggle the menu option hidden/not-hidden.

vue good table - 3 requests to the service

I use vue-good-table object to render table in Vue.js. I use paging and sorting serverside.
My code:
<vue-good-table v-if="authorizations"
id="AuthorizationsTable"
ref="refToAuthorizationsTable"
#on-page-change="onPageChange"
#on-sort-change="onSortChange"
#on-column-filter="onColumnFilter"
#on-per-page-change="onPerPageChange"
mode="remote"
:columns="columns"
:rows="authorizations"
:totalRows="totalRecords"
:pagination-options="{
enabled: true,
mode: 'pages',
nextLabel: 'następna',
prevLabel: 'poprzednia',
ofLabel: 'z',
pageLabel: 'strona',
rowsPerPageLabel: 'wierszy na stronie',
allLabel: 'wszystko',
dropdownAllowAll: false
}"
:sort-options="{
enabled: true,
initialSortBy: {
field: 'id',
type: 'asc'
}
}">
(...)
export default {
name: 'AuthoritiesAdministration',
components: {},
data() {
return {
totalRecords: 0,
serverParams: {
columnFilters: {},
sort: {
field: 'id',
type: 'asc'
},
page: 1,
perPage: 10
},
rows: [],
columns: [
{
label: 'ID',
field: 'id',
type: 'number',
tdClass: 'vue-good-table-col-100'
},
{
label: 'Data wystawienia',
field: 'issueDate',
formatFn: this.formatDate,
tdClass: 'vue-good-table-col-200',
},
{
label: 'Nazwa operatora',
field: 'operator',
sortable: true,
formatFn: this.formatOperatorName,
},
{
label: 'Login',
field: 'operator.login'
},
{
label: 'Spółka',
field: 'company.description',
type: 'text',
},
{
label: 'Data ważności',
field: 'endDate',
type: 'text',
formatFn: this.formatDate,
},
{
label: 'Akcje',
field: 'btn',
tdClass: 'vue-good-table-col-250',
sortable: false
}
],
}
},
(...)
methods: {
updateParams(newProps) {
this.serverParams = Object.assign({}, this.serverParams, newProps);
},
onPageChange(params) {
this.updateParams({page: params.currentPage});
this.loadAuthorizations();
},
onPerPageChange(params) {
this.updateParams({
perPage: params.currentPerPage
});
this.loadAuthorizations();
},
onSortChange(params) {
this.updateParams({
sort: {
type: params[0].type,
field: params[0].field
}
});
this.loadAuthorizations();
},
onColumnFilter(params) {
this.updateParams(params);
this.loadAuthorizations();
},
loadAuthorizations() {
getAllAuthorizationsLightWithPagination(this.$store.getters.loggedUser.token, this.serverParams).then(response => {
this.totalRecords = response.data.totalRecords;
this.rows = response.data.authorizations;
}).catch(err => {
this.$showError(err, true);
});
},
I have noticed that there are sent 3 requests to the server instead of 1: there are called methods like onPageChange, onPerPageChange and onSortChange but only the first time when my page is loaded. It is unnecessary. I have one method in "mounted" section where I load the first 10 results (sorting and paging by default). It's common, well-known problem with vue-good-table? Or maybe should I add an additional flag to not to invoke these 3 methods unnecessarily when the page is loaded?
Your onSortChange method is called at the table loading because you made a initialSortBy with specific values. To remove this calling juste remove
initialSortBy: {
field: 'id',
type: 'asc'
}
from you table configuration, but your sort will not be set, so I think this should be a legit function call.
The onPerPageChange and onPageChange are triggered by the config below
:pagination-options="{
enabled: true,
...
}
just remove the colon before pagination-options to have a config like this
pagination-options="{
enabled: true,
...
}

How to combine Filtering, Grouping, and Sorting in Kendo UI Vue Grid (native)

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>