Fixed #1841 - DataTable component does not work correctly "Checkbox selection" together with "lazy"

pull/1770/head
mertsincan 2021-12-06 11:45:21 +03:00
parent e08fd2d1d0
commit 229ba94153
5 changed files with 169 additions and 52 deletions

View File

@ -82,7 +82,7 @@ import TableFooter from './TableFooter.vue';
export default { export default {
name: 'DataTable', name: 'DataTable',
emits: ['value-change', 'update:first', 'update:rows', 'page', 'update:sortField', 'update:sortOrder', 'update:multiSortMeta', 'sort', 'filter', 'row-click', 'row-dblclick', emits: ['value-change', 'update:first', 'update:rows', 'page', 'update:sortField', 'update:sortOrder', 'update:multiSortMeta', 'sort', 'filter', 'row-click', 'row-dblclick',
'update:selection', 'row-select', 'row-unselect', 'update:contextMenuSelection', 'row-contextmenu', 'row-unselect-all', 'row-select-all', 'update:selection', 'row-select', 'row-unselect', 'update:contextMenuSelection', 'row-contextmenu', 'row-unselect-all', 'row-select-all', 'select-all-change',
'column-resize-end', 'column-reorder', 'row-reorder', 'update:expandedRows', 'row-collapse', 'row-expand', 'column-resize-end', 'column-reorder', 'row-reorder', 'update:expandedRows', 'row-collapse', 'row-expand',
'update:expandedRowGroups', 'rowgroup-collapse', 'rowgroup-expand', 'update:filters', 'state-restore', 'state-save', 'update:expandedRowGroups', 'rowgroup-collapse', 'rowgroup-expand', 'update:filters', 'state-restore', 'state-save',
'cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', 'update:editingRows', 'row-edit-init', 'row-edit-save', 'row-edit-cancel'], 'cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', 'update:editingRows', 'row-edit-init', 'row-edit-save', 'row-edit-cancel'],
@ -211,6 +211,10 @@ export default {
type: Object, type: Object,
default: null default: null
}, },
selectAll: {
type: Boolean,
default: null
},
rowHover: { rowHover: {
type: Boolean, type: Boolean,
default: false default: false
@ -883,15 +887,24 @@ export default {
} }
}, },
toggleRowsWithCheckbox(event) { toggleRowsWithCheckbox(event) {
const processedData = this.processedData; if (this.selectAll !== null) {
const checked = this.allRowsSelected; this.$emit('select-all-change', event);
const _selection = checked ? [] : (this.frozenValue ? [...this.frozenValue, ...processedData]: processedData); }
this.$emit('update:selection', _selection); else {
const { originalEvent, checked } = event;
let _selection = [];
if (checked) if (checked) {
this.$emit('row-unselect-all', {originalEvent: event}); _selection = this.frozenValue ? [...this.frozenValue, ...this.processedData] : this.processedData;
else this.$emit('row-select-all', {originalEvent, data: _selection});
this.$emit('row-select-all', {originalEvent: event, data: _selection}); }
else {
this.$emit('row-unselect-all', {originalEvent});
}
this.$emit('update:selection', _selection);
}
}, },
isSingleSelectionMode() { isSingleSelectionMode() {
return this.selectionMode === 'single'; return this.selectionMode === 'single';
@ -1887,9 +1900,13 @@ export default {
return ['p-datatable-loading-icon pi-spin', this.loadingIcon]; return ['p-datatable-loading-icon pi-spin', this.loadingIcon];
}, },
allRowsSelected() { allRowsSelected() {
const val = this.frozenValue ? [...this.frozenValue, ...this.processedData]: this.processedData; if (this.selectAll !== null) {
const length = this.lazy ? this.totalRecords : (val ? val.length : 0); return this.selectAll;
return (val && length > 0 && this.selection && this.selection.length > 0 && this.selection.length === length); }
else {
const val = this.frozenValue ? [...this.frozenValue, ...this.processedData] : this.processedData;
return val && this.selection && Array.isArray(this.selection) && val.every(v => this.selection.some(s => this.equals(s, v)));
}
}, },
attributeSelector() { attributeSelector() {
return UniqueComponentId(); return UniqueComponentId();

View File

@ -24,7 +24,10 @@ export default {
onClick(event) { onClick(event) {
if (!this.$attrs.disabled) { if (!this.$attrs.disabled) {
this.focused = true; this.focused = true;
this.$emit('change', event); this.$emit('change', {
originalEvent: event,
checked: !this.checked
});
} }
}, },
onFocus() { onFocus() {

View File

@ -21,7 +21,7 @@ export default class CustomerService {
} }
getCustomers(params) { getCustomers(params) {
const queryParams = Object.keys(params).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])).join('&'); const queryParams = params ? Object.keys(params).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])).join('&') : '';
return fetch('https://www.primefaces.org/data/customers?' + queryParams).then(res => res.json()) return fetch('https://www.primefaces.org/data/customers?' + queryParams).then(res => res.json())
} }
} }

View File

@ -13,9 +13,11 @@
<div class="content-section implementation"> <div class="content-section implementation">
<div class="card"> <div class="card">
<DataTable :value="customers" :lazy="true" :paginator="true" :rows="10" v-model:filters="filters" ref="dt" <DataTable :value="customers" :lazy="true" :paginator="true" :rows="10" v-model:filters="filters" ref="dt" dataKey="id"
:totalRecords="totalRecords" :loading="loading" @page="onPage($event)" @sort="onSort($event)" @filter="onFilter($event)" filterDisplay="row" :totalRecords="totalRecords" :loading="loading" @page="onPage($event)" @sort="onSort($event)" @filter="onFilter($event)" filterDisplay="row"
:globalFilterFields="['name','country.name', 'company', 'representative.name']" responsiveLayout="scroll" > :globalFilterFields="['name','country.name', 'company', 'representative.name']" responsiveLayout="scroll"
v-model:selection="selectedCustomers" :selectAll="selectAll" @select-all-change="onSelectAllChange" @row-select="onRowSelect" @row-unselect="onRowUnselect">
<Column selectionMode="multiple" headerStyle="width: 3em"></Column>
<Column field="name" header="Name" filterMatchMode="startsWith" ref="name" :sortable="true"> <Column field="name" header="Name" filterMatchMode="startsWith" ref="name" :sortable="true">
<template #filter="{filterModel,filterCallback}"> <template #filter="{filterModel,filterCallback}">
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search by name"/> <InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search by name"/>
@ -53,6 +55,8 @@ export default {
loading: false, loading: false,
totalRecords: 0, totalRecords: 0,
customers: null, customers: null,
selectedCustomers: null,
selectAll: false,
filters: { filters: {
'name': {value: '', matchMode: 'contains'}, 'name': {value: '', matchMode: 'contains'},
'country.name': {value: '', matchMode: 'contains'}, 'country.name': {value: '', matchMode: 'contains'},
@ -72,9 +76,11 @@ export default {
content: ` content: `
<template> <template>
<div> <div>
<DataTable :value="customers" :lazy="true" :paginator="true" :rows="10" v-model:filters="filters" ref="dt" <DataTable :value="customers" :lazy="true" :paginator="true" :rows="10" v-model:filters="filters" ref="dt" dataKey="id"
:totalRecords="totalRecords" :loading="loading" @page="onPage($event)" @sort="onSort($event)" @filter="onFilter($event)" filterDisplay="row" :totalRecords="totalRecords" :loading="loading" @page="onPage($event)" @sort="onSort($event)" @filter="onFilter($event)" filterDisplay="row"
:globalFilterFields="['name','country.name', 'company', 'representative.name']" responsiveLayout="scroll"> :globalFilterFields="['name','country.name', 'company', 'representative.name']" responsiveLayout="scroll"
v-model:selection="selectedCustomers" :selectAll="selectAll" @select-all-change="onSelectAllChange" @row-select="onRowSelect" @row-unselect="onRowUnselect">
<Column selectionMode="multiple" headerStyle="width: 3em"></Column>
<Column field="name" header="Name" filterMatchMode="startsWith" ref="name" :sortable="true"> <Column field="name" header="Name" filterMatchMode="startsWith" ref="name" :sortable="true">
<template #filter="{filterModel,filterCallback}"> <template #filter="{filterModel,filterCallback}">
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search by name"/> <InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search by name"/>
@ -108,6 +114,8 @@ export default {
loading: false, loading: false,
totalRecords: 0, totalRecords: 0,
customers: null, customers: null,
selectedCustomers: null,
selectAll: false,
filters: { filters: {
'name': {value: '', matchMode: 'contains'}, 'name': {value: '', matchMode: 'contains'},
'country.name': {value: '', matchMode: 'contains'}, 'country.name': {value: '', matchMode: 'contains'},
@ -166,6 +174,26 @@ export default {
onFilter() { onFilter() {
this.lazyParams.filters = this.filters; this.lazyParams.filters = this.filters;
this.loadLazyData(); this.loadLazyData();
},
onSelectAllChange(event) {
const selectAll = event.checked;
if (selectAll) {
this.customerService.getCustomers().then(data => {
this.selectAll = true;
this.selectedCustomers = data.customers;
});
}
else {
this.selectAll = false;
this.selectedCustomers = [];
}
},
onRowSelect() {
this.selectAll = this.selectedCustomers.length === this.totalRecords
},
onRowUnselect() {
this.selectAll = false;
} }
} }
} }
@ -177,9 +205,11 @@ export default {
content: ` content: `
<template> <template>
<div> <div>
<DataTable :value="customers" :lazy="true" :paginator="true" :rows="10" v-model:filters="filters" ref="dt" <DataTable :value="customers" :lazy="true" :paginator="true" :rows="10" v-model:filters="filters" ref="dt" dataKey="id"
:totalRecords="totalRecords" :loading="loading" @page="onPage($event)" @sort="onSort($event)" @filter="onFilter($event)" filterDisplay="row" :totalRecords="totalRecords" :loading="loading" @page="onPage($event)" @sort="onSort($event)" @filter="onFilter($event)" filterDisplay="row"
:globalFilterFields="['name','country.name', 'company', 'representative.name']" responsiveLayout="scroll"> :globalFilterFields="['name','country.name', 'company', 'representative.name']" responsiveLayout="scroll"
v-model:selection="selectedCustomers" :selectAll="selectAll" @select-all-change="onSelectAllChange" @row-select="onRowSelect" @row-unselect="onRowUnselect">
<Column selectionMode="multiple" headerStyle="width: 3em"></Column>
<Column field="name" header="Name" filterMatchMode="startsWith" ref="name" :sortable="true"> <Column field="name" header="Name" filterMatchMode="startsWith" ref="name" :sortable="true">
<template #filter="{filterModel,filterCallback}"> <template #filter="{filterModel,filterCallback}">
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search by name"/> <InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search by name"/>
@ -228,6 +258,8 @@ export default {
const loading = ref(false); const loading = ref(false);
const totalRecords = ref(0); const totalRecords = ref(0);
const customers = ref(); const customers = ref();
const selectedCustomers = ref();
const selectAll = ref(false);
const customerService = ref(new CustomerService()); const customerService = ref(new CustomerService());
const filters = ref({ const filters = ref({
'name': {value: '', matchMode: 'contains'}, 'name': {value: '', matchMode: 'contains'},
@ -269,8 +301,28 @@ export default {
lazyParams.value.filters = filters.value ; lazyParams.value.filters = filters.value ;
loadLazyData(); loadLazyData();
} }
const onSelectAllChange = (event) => {
const selectAll = event.checked;
return { dt, loading, totalRecords, customers, filters, lazyParams, columns, loadLazyData, onPage, onSort, onFilter } if (selectAll) {
customerService.value.getCustomers().then(data => {
selectAll.value = true;
selectedCustomers.value = data.customers;
});
}
else {
selectAll.value = false;
selectedCustomers.value = [];
}
}
const onRowSelect = () => {
selectAll.value = selectedCustomers.value.length === totalRecords.value;
}
const onRowUnselect = () => {
selectAll.value = false;
}
return { dt, loading, totalRecords, customers, filters, lazyParams, columns, loadLazyData, onPage, onSort, onFilter, onSelectAllChange, onRowSelect, onRowUnselect }
} }
} }
<\\/script> <\\/script>
@ -282,9 +334,11 @@ export default {
<script src="https://unpkg.com/primevue@^3/column/column.min.js"><\\/script> <script src="https://unpkg.com/primevue@^3/column/column.min.js"><\\/script>
<script src="./CustomerService.js"><\\/script>`, <script src="./CustomerService.js"><\\/script>`,
content: `<div id="app"> content: `<div id="app">
<p-datatable :value="customers" :lazy="true" :paginator="true" :rows="10" v-model:filters="filters" ref="dt" <p-datatable :value="customers" :lazy="true" :paginator="true" :rows="10" v-model:filters="filters" ref="dt" dataKey="id"
:total-records="totalRecords" :loading="loading" @page="onPage($event)" @sort="onSort($event)" @filter="onFilter($event)" filter-display="row" :totalRecords="totalRecords" :loading="loading" @page="onPage($event)" @sort="onSort($event)" @filter="onFilter($event)" filterDisplay="row"
:global-filter-fields="['name','country.name', 'company', 'representative.name']" responsive-layout="scroll"> :globalFilterFields="['name','country.name', 'company', 'representative.name']" responsiveLayout="scroll"
v-model:selection="selectedCustomers" :selectAll="selectAll" @select-all-change="onSelectAllChange" @row-select="onRowSelect" @row-unselect="onRowUnselect">
<p-column selectionMode="multiple" headerStyle="width: 3em"></Column>
<p-column field="name" header="Name" filter-match-mode="startsWith" ref="name" :sortable="true"> <p-column field="name" header="Name" filter-match-mode="startsWith" ref="name" :sortable="true">
<template #filter="{filterModel,filterCallback}"> <template #filter="{filterModel,filterCallback}">
<p-inputtext type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search by name"></p-inputtext> <p-inputtext type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search by name"></p-inputtext>
@ -331,6 +385,8 @@ export default {
const loading = ref(false); const loading = ref(false);
const totalRecords = ref(0); const totalRecords = ref(0);
const customers = ref(); const customers = ref();
const selectedCustomers = ref();
const selectAll = ref(false);
const customerService = ref(new CustomerService()); const customerService = ref(new CustomerService());
const filters = ref({ const filters = ref({
'name': {value: '', matchMode: 'contains'}, 'name': {value: '', matchMode: 'contains'},
@ -373,7 +429,28 @@ export default {
loadLazyData(); loadLazyData();
} }
return { dt, loading, totalRecords, customers, filters, lazyParams, columns, loadLazyData, onPage, onSort, onFilter } const onSelectAllChange = (event) => {
const selectAll = event.checked;
if (selectAll) {
customerService.value.getCustomers().then(data => {
selectAll.value = true;
selectedCustomers.value = data.customers;
});
}
else {
selectAll.value = false;
selectedCustomers.value = [];
}
}
const onRowSelect = () => {
selectAll.value = selectedCustomers.value.length === totalRecords.value;
}
const onRowUnselect = () => {
selectAll.value = false;
}
return { dt, loading, totalRecords, customers, filters, lazyParams, columns, loadLazyData, onPage, onSort, onFilter, onSelectAllChange, onRowSelect, onRowUnselect }
}, },
components: { components: {
"p-datatable": primevue.datatable, "p-datatable": primevue.datatable,
@ -431,6 +508,26 @@ export default {
onFilter() { onFilter() {
this.lazyParams.filters = this.filters; this.lazyParams.filters = this.filters;
this.loadLazyData(); this.loadLazyData();
},
onSelectAllChange(event) {
const selectAll = event.checked;
if (selectAll) {
this.customerService.getCustomers().then(data => {
this.selectAll = true;
this.selectedCustomers = data.customers;
});
}
else {
this.selectAll = false;
this.selectedCustomers = [];
}
},
onRowSelect() {
this.selectAll = this.selectedCustomers.length === this.totalRecords
},
onRowUnselect() {
this.selectAll = false;
} }
} }
} }

View File

@ -72,7 +72,7 @@
<div class="card"> <div class="card">
<h5>Checkbox</h5> <h5>Checkbox</h5>
<DataTable :value="products" v-model:selection="selectedProducts3" dataKey="id" responsiveLayout="scroll" > <DataTable :value="products" v-model:selection="selectedProducts3" dataKey="id" responsiveLayout="scroll">
<Column selectionMode="multiple" headerStyle="width: 3em"></Column> <Column selectionMode="multiple" headerStyle="width: 3em"></Column>
<Column field="code" header="Code"></Column> <Column field="code" header="Code"></Column>
<Column field="name" header="Name"></Column> <Column field="name" header="Name"></Column>