488 lines
21 KiB
Vue
488 lines
21 KiB
Vue
<template>
|
|
<DocSectionText v-bind="$attrs">
|
|
<p>
|
|
Lazy mode is handy to deal with large datasets, instead of loading the entire data, small chunks of data is loaded by invoking corresponding callbacks everytime <i>paging</i>, <i>sorting</i> and <i>filtering</i> occurs. Sample below
|
|
imitates lazy loading data from a remote datasource using an in-memory list and timeouts to mimic network connection.
|
|
</p>
|
|
<p>
|
|
Enabling the <i>lazy</i> property and assigning the logical number of rows to <i>totalRecords</i> by doing a projection query are the key elements of the implementation so that paginator displays the UI assuming there are actually records
|
|
of totalRecords size although in reality they are not present on page, only the records that are displayed on the current page exist.
|
|
</p>
|
|
<p>Note that, the implementation of <i>checkbox selection</i> in lazy mode needs to be handled manually as in this example since the DataTable cannot know about the whole dataset.</p>
|
|
</DocSectionText>
|
|
<div class="card p-fluid">
|
|
<DataTable
|
|
ref="dt"
|
|
v-model:filters="filters"
|
|
v-model:selection="selectedCustomers"
|
|
:value="customers"
|
|
lazy
|
|
paginator
|
|
:first="first"
|
|
:rows="10"
|
|
dataKey="id"
|
|
:totalRecords="totalRecords"
|
|
:loading="loading"
|
|
@page="onPage($event)"
|
|
@sort="onSort($event)"
|
|
@filter="onFilter($event)"
|
|
filterDisplay="row"
|
|
:globalFilterFields="['name', 'country.name', 'company', 'representative.name']"
|
|
:selectAll="selectAll"
|
|
@select-all-change="onSelectAllChange"
|
|
@row-select="onRowSelect"
|
|
@row-unselect="onRowUnselect"
|
|
tableStyle="min-width: 75rem"
|
|
>
|
|
<Column selectionMode="multiple" headerStyle="width: 3rem"></Column>
|
|
<Column field="name" header="Name" filterMatchMode="startsWith" sortable>
|
|
<template #filter="{ filterModel, filterCallback }">
|
|
<InputText v-model="filterModel.value" type="text" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search" />
|
|
</template>
|
|
</Column>
|
|
<Column field="country.name" header="Country" filterField="country.name" filterMatchMode="contains" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex align-items-center gap-2">
|
|
<img alt="flag" src="https://primefaces.org/cdn/primevue/images/flag/flag_placeholder.png" :class="`flag flag-${data.country.code}`" style="width: 24px" />
|
|
<span>{{ data.country.name }}</span>
|
|
</div>
|
|
</template>
|
|
<template #filter="{ filterModel, filterCallback }">
|
|
<InputText v-model="filterModel.value" type="text" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search" />
|
|
</template>
|
|
</Column>
|
|
<Column field="company" header="Company" filterMatchMode="contains" sortable>
|
|
<template #filter="{ filterModel, filterCallback }">
|
|
<InputText v-model="filterModel.value" type="text" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search" />
|
|
</template>
|
|
</Column>
|
|
<Column field="representative.name" header="Representative" filterField="representative.name" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex align-items-center gap-2">
|
|
<img :alt="data.representative.name" :src="`https://primefaces.org/cdn/primevue/images/avatar/${data.representative.image}`" style="width: 32px" />
|
|
<span>{{ data.representative.name }}</span>
|
|
</div>
|
|
</template>
|
|
<template #filter="{ filterModel, filterCallback }">
|
|
<InputText v-model="filterModel.value" type="text" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search" />
|
|
</template>
|
|
</Column>
|
|
</DataTable>
|
|
</div>
|
|
<DocSectionCode :code="code" :service="['CustomerService']" />
|
|
</template>
|
|
|
|
<script>
|
|
import { CustomerService } from '@/service/CustomerService';
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
loading: false,
|
|
totalRecords: 0,
|
|
customers: null,
|
|
selectedCustomers: null,
|
|
selectAll: false,
|
|
first: 0,
|
|
filters: {
|
|
name: { value: '', matchMode: 'contains' },
|
|
'country.name': { value: '', matchMode: 'contains' },
|
|
company: { value: '', matchMode: 'contains' },
|
|
'representative.name': { value: '', matchMode: 'contains' }
|
|
},
|
|
lazyParams: {},
|
|
columns: [
|
|
{ field: 'name', header: 'Name' },
|
|
{ field: 'country.name', header: 'Country' },
|
|
{ field: 'company', header: 'Company' },
|
|
{ field: 'representative.name', header: 'Representative' }
|
|
],
|
|
code: {
|
|
basic: `
|
|
<DataTable :value="customers" lazy paginator :first="first" :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"
|
|
:globalFilterFields="['name','country.name', 'company', 'representative.name']"
|
|
v-model:selection="selectedCustomers" :selectAll="selectAll" @select-all-change="onSelectAllChange" @row-select="onRowSelect" @row-unselect="onRowUnselect" tableStyle="min-width: 75rem">
|
|
<Column selectionMode="multiple" headerStyle="width: 3rem"></Column>
|
|
<Column field="name" header="Name" filterMatchMode="startsWith" sortable>
|
|
<template #filter="{filterModel,filterCallback}">
|
|
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search"/>
|
|
</template>
|
|
</Column>
|
|
<Column field="country.name" header="Country" filterField="country.name" filterMatchMode="contains" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex align-items-center gap-2">
|
|
<img alt="flag" src="https://primefaces.org/cdn/primevue/images/flag/flag_placeholder.png" :class="\`flag flag-\${data.country.code}\`" style="width: 24px" />
|
|
<span>{{ data.country.name }}</span>
|
|
</div>
|
|
</template>
|
|
<template #filter="{filterModel,filterCallback}">
|
|
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search"/>
|
|
</template>
|
|
</Column>
|
|
<Column field="company" header="Company" filterMatchMode="contains" sortable>
|
|
<template #filter="{filterModel,filterCallback}">
|
|
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search"/>
|
|
</template>
|
|
</Column>
|
|
<Column field="representative.name" header="Representative" filterField="representative.name" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex align-items-center gap-2">
|
|
<img :alt="data.representative.name" :src="\`https://primefaces.org/cdn/primevue/images/avatar/\${data.representative.image}\`" style="width: 32px" />
|
|
<span>{{ data.representative.name }}</span>
|
|
</div>
|
|
</template>
|
|
<template #filter="{filterModel,filterCallback}">
|
|
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search"/>
|
|
</template>
|
|
</Column>
|
|
</DataTable>
|
|
`,
|
|
options: `
|
|
<template>
|
|
<div class="card p-fluid">
|
|
<DataTable :value="customers" lazy paginator :first="first" :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"
|
|
:globalFilterFields="['name','country.name', 'company', 'representative.name']"
|
|
v-model:selection="selectedCustomers" :selectAll="selectAll" @select-all-change="onSelectAllChange" @row-select="onRowSelect" @row-unselect="onRowUnselect" tableStyle="min-width: 75rem">
|
|
<Column selectionMode="multiple" headerStyle="width: 3rem"></Column>
|
|
<Column field="name" header="Name" filterMatchMode="startsWith" sortable>
|
|
<template #filter="{filterModel,filterCallback}">
|
|
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search"/>
|
|
</template>
|
|
</Column>
|
|
<Column field="country.name" header="Country" filterField="country.name" filterMatchMode="contains" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex align-items-center gap-2">
|
|
<img alt="flag" src="https://primefaces.org/cdn/primevue/images/flag/flag_placeholder.png" :class="\`flag flag-\${data.country.code}\`" style="width: 24px" />
|
|
<span>{{ data.country.name }}</span>
|
|
</div>
|
|
</template>
|
|
<template #filter="{filterModel,filterCallback}">
|
|
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search"/>
|
|
</template>
|
|
</Column>
|
|
<Column field="company" header="Company" filterMatchMode="contains" sortable>
|
|
<template #filter="{filterModel,filterCallback}">
|
|
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search"/>
|
|
</template>
|
|
</Column>
|
|
<Column field="representative.name" header="Representative" filterField="representative.name" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex align-items-center gap-2">
|
|
<img :alt="data.representative.name" :src="\`https://primefaces.org/cdn/primevue/images/avatar/\${data.representative.image}\`" style="width: 32px" />
|
|
<span>{{ data.representative.name }}</span>
|
|
</div>
|
|
</template>
|
|
<template #filter="{filterModel,filterCallback}">
|
|
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search"/>
|
|
</template>
|
|
</Column>
|
|
</DataTable>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { CustomerService } from '@/service/CustomerService';
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
loading: false,
|
|
totalRecords: 0,
|
|
customers: null,
|
|
selectedCustomers: null,
|
|
selectAll: false,
|
|
first: 0,
|
|
filters: {
|
|
'name': {value: '', matchMode: 'contains'},
|
|
'country.name': {value: '', matchMode: 'contains'},
|
|
'company': {value: '', matchMode: 'contains'},
|
|
'representative.name': {value: '', matchMode: 'contains'},
|
|
},
|
|
lazyParams: {},
|
|
columns: [
|
|
{field: 'name', header: 'Name'},
|
|
{field: 'country.name', header: 'Country'},
|
|
{field: 'company', header: 'Company'},
|
|
{field: 'representative.name', header: 'Representative'}
|
|
]
|
|
}
|
|
},
|
|
mounted() {
|
|
this.loading = true;
|
|
|
|
this.lazyParams = {
|
|
first: this.$refs.dt.first,
|
|
rows: this.$refs.dt.rows,
|
|
sortField: null,
|
|
sortOrder: null,
|
|
filters: this.filters
|
|
};
|
|
|
|
this.loadLazyData();
|
|
},
|
|
methods: {
|
|
loadLazyData() {
|
|
this.loading = true;
|
|
this.lazyParams = { ...this.lazyParams, first: event?.first || this.first };
|
|
|
|
setTimeout(() => {
|
|
CustomerService.getCustomers({ lazyEvent: JSON.stringify(this.lazyParams) }).then((data) => {
|
|
this.customers = data.customers;
|
|
this.totalRecords = data.totalRecords;
|
|
this.loading = false;
|
|
});
|
|
}, Math.random() * 1000 + 250);
|
|
},
|
|
onPage(event) {
|
|
this.lazyParams = event;
|
|
this.loadLazyData(event);
|
|
},
|
|
onSort(event) {
|
|
this.lazyParams = event;
|
|
this.loadLazyData(event);
|
|
},
|
|
onFilter(event) {
|
|
this.lazyParams.filters = this.filters;
|
|
this.loadLazyData(event);
|
|
},
|
|
onSelectAllChange(event) {
|
|
const selectAll = event.checked;
|
|
|
|
if (selectAll) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
<\/script>
|
|
|
|
`,
|
|
composition: `
|
|
<template>
|
|
<div class="card p-fluid">
|
|
<DataTable :value="customers" lazy paginator :first="first" :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"
|
|
:globalFilterFields="['name','country.name', 'company', 'representative.name']"
|
|
v-model:selection="selectedCustomers" :selectAll="selectAll" @select-all-change="onSelectAllChange" @row-select="onRowSelect" @row-unselect="onRowUnselect" tableStyle="min-width: 75rem">
|
|
<Column selectionMode="multiple" headerStyle="width: 3rem"></Column>
|
|
<Column field="name" header="Name" filterMatchMode="startsWith" sortable>
|
|
<template #filter="{filterModel,filterCallback}">
|
|
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search"/>
|
|
</template>
|
|
</Column>
|
|
<Column field="country.name" header="Country" filterField="country.name" filterMatchMode="contains" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex align-items-center gap-2">
|
|
<img alt="flag" src="https://primefaces.org/cdn/primevue/images/flag/flag_placeholder.png" :class="\`flag flag-\${data.country.code}\`" style="width: 24px" />
|
|
<span>{{ data.country.name }}</span>
|
|
</div>
|
|
</template>
|
|
<template #filter="{filterModel,filterCallback}">
|
|
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search"/>
|
|
</template>
|
|
</Column>
|
|
<Column field="company" header="Company" filterMatchMode="contains" sortable>
|
|
<template #filter="{filterModel,filterCallback}">
|
|
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search"/>
|
|
</template>
|
|
</Column>
|
|
<Column field="representative.name" header="Representative" filterField="representative.name" sortable>
|
|
<template #body="{ data }">
|
|
<div class="flex align-items-center gap-2">
|
|
<img :alt="data.representative.name" :src="\`https://primefaces.org/cdn/primevue/images/avatar/\${data.representative.image}\`" style="width: 32px" />
|
|
<span>{{ data.representative.name }}</span>
|
|
</div>
|
|
</template>
|
|
<template #filter="{filterModel,filterCallback}">
|
|
<InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" placeholder="Search"/>
|
|
</template>
|
|
</Column>
|
|
</DataTable>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue';
|
|
import { CustomerService } from '@/service/CustomerService';
|
|
|
|
onMounted(() => {
|
|
loading.value = true;
|
|
|
|
lazyParams.value = {
|
|
first: dt.value.first,
|
|
rows: dt.value.rows,
|
|
sortField: null,
|
|
sortOrder: null,
|
|
filters: filters.value
|
|
};
|
|
|
|
loadLazyData();
|
|
});
|
|
|
|
const dt = ref();
|
|
const loading = ref(false);
|
|
const totalRecords = ref(0);
|
|
const customers = ref();
|
|
const selectedCustomers = ref();
|
|
const selectAll = ref(false);
|
|
const first = ref(0);
|
|
const filters = ref({
|
|
'name': {value: '', matchMode: 'contains'},
|
|
'country.name': {value: '', matchMode: 'contains'},
|
|
'company': {value: '', matchMode: 'contains'},
|
|
'representative.name': {value: '', matchMode: 'contains'},
|
|
});
|
|
const lazyParams = ref({});
|
|
const columns = ref([
|
|
{field: 'name', header: 'Name'},
|
|
{field: 'country.name', header: 'Country'},
|
|
{field: 'company', header: 'Company'},
|
|
{field: 'representative.name', header: 'Representative'}
|
|
]);
|
|
|
|
const loadLazyData = (event) => {
|
|
loading.value = true;
|
|
lazyParams.value = { ...lazyParams.value, first: event?.first || first.value };
|
|
|
|
setTimeout(() => {
|
|
CustomerService.getCustomers({ lazyEvent: JSON.stringify(lazyParams.value) }).then((data) => {
|
|
customers.value = data.customers;
|
|
totalRecords.value = data.totalRecords;
|
|
loading.value = false;
|
|
});
|
|
}, Math.random() * 1000 + 250);
|
|
};
|
|
const onPage = (event) => {
|
|
lazyParams.value = event;
|
|
loadLazyData(event);
|
|
};
|
|
const onSort = (event) => {
|
|
lazyParams.value = event;
|
|
loadLazyData(event);
|
|
};
|
|
const onFilter = (event) => {
|
|
lazyParams.value.filters = filters.value ;
|
|
loadLazyData(event);
|
|
};
|
|
const onSelectAllChange = (event) => {
|
|
selectAll.value = event.checked;
|
|
|
|
if (selectAll) {
|
|
CustomerService.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;
|
|
};
|
|
|
|
<\/script>
|
|
`,
|
|
data: `
|
|
{
|
|
id: 1000,
|
|
name: 'James Butt',
|
|
country: {
|
|
name: 'Algeria',
|
|
code: 'dz'
|
|
},
|
|
company: 'Benton, John B Jr',
|
|
date: '2015-09-13',
|
|
status: 'unqualified',
|
|
verified: true,
|
|
activity: 17,
|
|
representative: {
|
|
name: 'Ioni Bowcher',
|
|
image: 'ionibowcher.png'
|
|
},
|
|
balance: 70663
|
|
},
|
|
...`
|
|
}
|
|
};
|
|
},
|
|
mounted() {
|
|
this.loading = true;
|
|
|
|
this.lazyParams = {
|
|
first: this.$refs.dt.first,
|
|
rows: this.$refs.dt.rows,
|
|
sortField: null,
|
|
sortOrder: null,
|
|
filters: this.filters
|
|
};
|
|
|
|
this.loadLazyData();
|
|
},
|
|
methods: {
|
|
loadLazyData(event) {
|
|
this.loading = true;
|
|
this.lazyParams = { ...this.lazyParams, first: event?.first || this.first };
|
|
|
|
setTimeout(() => {
|
|
CustomerService.getCustomers({ lazyEvent: JSON.stringify(this.lazyParams) }).then((data) => {
|
|
this.customers = data.customers;
|
|
this.totalRecords = data.totalRecords;
|
|
this.loading = false;
|
|
});
|
|
}, Math.random() * 1000 + 250);
|
|
},
|
|
onPage(event) {
|
|
this.lazyParams = event;
|
|
this.loadLazyData(event);
|
|
},
|
|
onSort(event) {
|
|
this.lazyParams = event;
|
|
this.loadLazyData(event);
|
|
},
|
|
onFilter(event) {
|
|
this.lazyParams.filters = this.filters;
|
|
this.loadLazyData(event);
|
|
},
|
|
onSelectAllChange(event) {
|
|
const selectAll = event.checked;
|
|
|
|
if (selectAll) {
|
|
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;
|
|
}
|
|
}
|
|
};
|
|
</script>
|