diff --git a/src/components/column/Column.d.ts b/src/components/column/Column.d.ts index bb6f962cc..140edf842 100755 --- a/src/components/column/Column.d.ts +++ b/src/components/column/Column.d.ts @@ -3,21 +3,31 @@ interface ColumnProps { field?: string; sortField?: string | ((item: any) => any); filterField?: string; + dataType?: string; sortable?: boolean; header?: any; footer?: any; + style?: object; + class?: string; headerStyle?: object; headerClass?: string; bodyStyle?: object; bodyClass?: string; footerStyle?: object; footerClass?: string; + showFilterMenu?: boolean; + showFilterOperator?: boolean; + showClearButton?: boolean; + showApplyButton?: boolean; + showFilterMatchModes?: boolean; + showAddButton?: boolean; + filterMatchModeOptions?: any[]; + maxConstraints?: number; + excludeGlobalFilter?: boolean; filterHeaderStyle?: object; filterHeaderClass?: string; filterMenuStyle?: object; filterMenuClass?: string; - filterFunction?: Function; - excludeGlobalFilter?: boolean; selectionMode?: string; expander?: boolean; colspan?: number; @@ -27,6 +37,7 @@ interface ColumnProps { reorderableColumn?: boolean; rowEditor?: boolean; frozen?: boolean; + alignFrozen?: string; exportable?: boolean; } diff --git a/src/components/datatable/DataTable.vue b/src/components/datatable/DataTable.vue index 39902abab..015bbfa9c 100755 --- a/src/components/datatable/DataTable.vue +++ b/src/components/datatable/DataTable.vue @@ -75,9 +75,8 @@ export default { emits: ['update:first', 'update:rows', 'page', 'update:sortField', 'update:sortOrder', 'update:multiSortMeta', 'sort', 'filter', 'row-click', 'update:selection', 'row-select', 'row-unselect', 'update:contextMenuSelection', 'row-contextmenu', 'row-unselect-all', 'row-select-all', 'column-resize-end', 'column-reorder', 'row-reorder', 'update:expandedRows', 'row-collapse', 'row-expand', - 'update:expandedRowGroups', 'rowgroup-collapse', 'rowgroup-expand', 'update:filters', 'virtual-scroll', 'state-restore', 'state-save', - 'cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', 'update:editingRows', 'row-edit-init', 'row-edit-save', 'row-edit-cancel', - 'operator-change', 'matchmode-change', 'constraint-add', 'constraint-remove', 'filter-clear', 'apply-click'], + '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'], props: { value: { type: Array, @@ -295,10 +294,6 @@ export default { type: Array, default: null }, - frozenWidth: { - type: String, - default: null - }, virtualScroll: { type: Boolean, default: false diff --git a/src/views/datatable/DataTableDoc.vue b/src/views/datatable/DataTableDoc.vue index be3e67182..1212f86ff 100755 --- a/src/views/datatable/DataTableDoc.vue +++ b/src/views/datatable/DataTableDoc.vue @@ -175,67 +175,101 @@ export default {
Filtering is enabled by defining a filter template per column to populate the filters property of the DataTable. The filters - property should be an key-value object where keys are the field name and the value is the filter value. The filter template receives the column properties - via the slotProps and accepts any form element as the filter element. Default match mode is "startsWith" and this can be configured per column using the filterMatchMode property that also accepts - "contains", "endsWith", "equals", "notEquals", "in", "lt", "lte", "gt", "gte" and "custom" as available modes.
-Optionally a global filter is available to search against all the fields, in this case the special global keyword should be the property to be populated.
+DataTable has advanced filtering capabilities that does the heavy lifting while providing flexible customization. Filtering has two layout alternatives defined with the filterDisplay. + In row setting, filter elements are displayed in a separate row at the header section whereas + in menu mode filter elements are displayed inside an overlay. Filter metadata is specified using the filters as a v-model and UI elements for the filtering + are placed inside the filter template. The template filter gets a filterModel and filterCallback, + use filterModel.value to populate the filter with your own form components and call the filterCallback with the event of your choice like @input, @change, @click.
+ +
+import CustomerService from '../../service/CustomerService';
+import {FilterMatchMode} from 'primevue/api';
+
+export default {
+ data() {
+ return {
+ customers: null,
+ filters: {
+ 'name': {value: null, matchMode: FilterMatchMode.STARTS_WITH}
+ }
+ }
+ },
+ created() {
+ this.customerService = new CustomerService();
+ },
+ mounted() {
+ this.customerService.getCustomersLarge().then(data => this.customers = data);
+ }
+}
+
+
+ Input field is displayed in a separate header row.
+
-<DataTable :value="cars" :filters="filters" :paginator="true" :rows="10">
- <template #header>
- <div style="text-align: right">
- <i class="pi pi-search" style="margin: 4px 4px 0px 0px;"></i>
- <InputText v-model="filters['global']" placeholder="Global Search" size="50" />
- </div>
- </template>
- <Column field="vin" header="Vin" filterMatchMode="startsWith">
- <template #filter>
- <InputText type="text" v-model="filters['vin']" class="p-column-filter" />
+<DataTable :value="customers1"
+ dataKey="id" v-model:filters="filters" filterDisplay="row" :loading="loading">
+ <Column field="name" header="Name">
+ <template #filter="{filterModel,filterCallback}">
+ <InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" :placeholder="`Search by name - ${filterModel.matchMode}`"/>
</template>
</Column>
- <Column field="year" header="Year" filterMatchMode="contains">
- <template #filter>
- <InputText type="text" v-model="filters['year']" class="p-column-filter" />
- </template>
- </Column>
- <Column field="brand" header="Brand" filterMatchMode="equals">
- <template #filter>
- <Dropdown v-model="filters['brand']" :options="brands" optionLabel="brand" optionValue="value" placeholder="Select a Brand" class="p-column-filter">
- <template #option="slotProps">
- <div class="p-dropdown-car-option">
- <img :alt="slotProps.option.brand" :src="'demo/images/car/' + slotProps.option.brand + '.png'" />
- <span>{{slotProps.option.brand}}</span>
- </div>
- </template>
- </Dropdown>
- </template>
- </Column>
- <Column field="color" header="Color" filterMatchMode="in">
- <template #filter>
- <MultiSelect v-model="filters['color']" :options="colors" optionLabel="name" optionValue="value" placeholder="Select a Color" />
- </template>
- </Column>
-</DataTable>
+<DataTable>
+
- Custom filtering is implemented by setting the filterMatchMode to "custom" and defining a filter function.
+Input field is displayed in an overlay.
+
-<Column field="vin" header="Vin" filterMatchMode="myOwnEquals">
- <template #filter>
- <InputText type="text" v-model="filters['vin']" class="p-column-filter" />
+<DataTable :value="customers1"
+ dataKey="id" v-model:filters="filters" filterDisplay="menu" :loading="loading">
+ <Column field="name" header="Name">
+ <template #filter="{filterModel,filterCallback}">
+ <InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" :placeholder="`Search by name - ${filterModel.matchMode}`"/>
</template>
</Column>
+<DataTable>
+
+
+
+ In "menu" display, it is possible to add more constraints to a same filter. In this case, metadata could be an array of constraints. The operator + defines whether all or any of the constraints should match.
+ +
+data() {
+ return {
+ customers: null,
+ filters: {
+ 'name': {operator: FilterOperator.AND, constraints: [{value: null, matchMode: FilterMatchMode.STARTS_WITH}]},
+ }
+ }
+}
+
+
+ Providing a filters with predefined values would be enough to display the table as filtered by default.
+
+data() {
+ return {
+ customers: null,
+ filters: {
+ 'name': {operator: FilterOperator.AND, constraints: [
+ {value: 'Prime', matchMode: FilterMatchMode.STARTS_WITH},
+ {value: 'Vue', matchMode: FilterMatchMode.CONTAINS}
+ ]},
+ }
+ }
+}
+
+
+ Depending on the dataType of the column, suitable match modes are displayed. Default configuration is available at PrimeVue.filterMatchModeOptions which can be used to customize the modes globally for all tables.
+ +
+import {createApp} from 'vue';
+import PrimeVue from 'primevue/config';
+import FilterMatchMode from 'primevue/api',
+const app = createApp(App);
+
+app.use(PrimeVue, {
+ filterMatchModeOptions: {
+ text: [
+ FilterMatchMode.STARTS_WITH,
+ FilterMatchMode.CONTAINS,
+ FilterMatchMode.NOT_CONTAINS,
+ FilterMatchMode.ENDS_WITH,
+ FilterMatchMode.EQUALS,
+ FilterMatchMode.NOT_EQUALS
+ ],
+ numeric: [
+ FilterMatchMode.EQUALS,
+ FilterMatchMode.NOT_EQUALS,
+ FilterMatchMode.LESS_THAN,
+ FilterMatchMode.LESS_THAN_OR_EQUAL_TO,
+ FilterMatchMode.GREATER_THAN,
+ FilterMatchMode.GREATER_THAN_OR_EQUAL_TO
+ ],
+ date: [
+ FilterMatchMode.DATE_IS,
+ FilterMatchMode.DATE_IS_NOT,
+ FilterMatchMode.DATE_BEFORE,
+ FilterMatchMode.DATE_AFTER
+ ]
+ }
+});
+
+
+ If you need to override the match modes for a particular column use the filterMatchModeOptions property and provide an array with label-value pairs.
+
+<Column field="name" header="Name" :filterMatchModeOptions="matchModes">
+ <template #filter="{filterModel,filterCallback}">
+ <InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" :placeholder="`Search by name - ${filterModel.matchMode}`"/>
+ </template>
+</Column>
+
+
+
+
+
+matchModes: [
+ {label: 'Starts With', FilterMatchMode.STARTS_WITH},
+ {label: 'Contains', FilterMatchMode.CONTAINS},
+]
+
+
+ Custom filtering is implemented using the FilterService, first register your filter and add it to your filterMatchModeOptions.
+
+import {FilterService} from 'primevue/api';
+
+FilterService.register('myfilter', (a,b) => a === b);
-methods: {
- myOwnEquals(value, filter) {
- if (filter === undefined || filter === null || (typeof filter === 'string' && filter.trim() === '')) {
- return true;
- }
-
- if (value === undefined || value === null) {
- return false;
- }
-
- return value.toString().toLowerCase() === filter.toString().toLowerCase();
- }
-}
-
+matchModes: [
+ {label: 'My Filter', "myfilter"},
+ {label: 'Starts With', FilterMatchMode.STARTS_WITH},
+ {label: 'Contains', FilterMatchMode.CONTAINS},
+]
DataTable supports both horizontal and vertical scrolling as well as frozen columns and rows. Scrollable DataTable is enabled using scrollable property and scrollHeight to define the viewport height.
-<DataTable :value="cars" :scrollable="true" scrollHeight="200px">
+<DataTable :value="cars" :scrollable="true" scrollHeight="400px">
<Column field="vin" header="Vin"></Column>
<Column field="year" header="Year"></Column>
<Column field="brand" header="Brand"></Column>
@@ -683,7 +838,7 @@ methods: {
FlexScroll can also be used for cases where scrollable viewport should be responsive with respect to the window size. See the Full Page demo for an example.
+FlexScroll can also be used for cases where scrollable viewport should be responsive with respect to the window size. See the
<div style="height: calc(100vh - 143px)">
<DataTable :value="cars" :scrollable="true" scrollHeight="flex">
@@ -697,133 +852,75 @@ methods: {
In horizontal scrolling, it is required to give fixed widths to columns. In general when customizing the column widths of scrollable tables, use colgroup as below to avoid misalignment issues as it will apply both the header, body and footer sections which are different separate elements internally.
+For horizontal scrolling only, it is required to set scrollDirection to "horizontal" and give widths to columns.
-<DataTable :value="cars" :scrollable="true" scrollHeight="200px" style="width: 600px">
- <Column field="vin" header="Vin" headerStyle="width: 250px" columnKey="vin_1"></Column>
- <Column field="year" header="Year" headerStyle="width: 250px" columnKey="year_1"></Column>
- <Column field="brand" header="Brand" headerStyle="width: 250px" columnKey="brand_1"></Column>
- <Column field="color" header="Color" headerStyle="width: 250px" columnKey="color_1"></Column>
- <Column field="vin" header="Vin" headerStyle="width: 250px" columnKey="vin_2"></Column>
- <Column field="year" header="Year" headerStyle="width: 250px" columnKey="year_2"></Column>
- <Column field="brand" header="Brand" headerStyle="width: 250px" columnKey="brand_2"></Column>
- <Column field="color" header="Color" headerStyle="width: 250px" columnKey="color_2"></Column>
+<DataTable :value="customers" :scrollable="true" scrollDirection="horizontal">
+ <Column field="id" header="Id" footer="Id" :style="{width:'200px'}"></Column>
+ <Column field="name" header="Name" footer="Name" :style="{width:'200px'}"></Column>
+ <Column field="country.name" header="Country" footer="Country" :style="{width:'200px'}"></Column>
+ <Column field="date" header="Date" footer="Date" :style="{width:'200px'}"></Column>
+ <Column field="balance" header="Balance" footer="Balance" :style="{width:'200px'}"></Column>
+ <Column field="company" header="Company" footer="Company" :style="{width:'200px'}"></Column>
+ <Column field="status" header="Status" footer="Status" :style="{width:'200px'}"></Column>
+ <Column field="activity" header="Activity" footer="Activity" :style="{width:'200px'}"></Column>
+ <Column field="representative.name" header="Representative" footer="Representative" :style="{width:'200px'}"></Column>
</DataTable>
- Certain columns can be frozen by using the frozen property of the column component. Widths of the frozen section is specified by the frozenWidth property.
+Set scrollDirection to "both" and give widths to columns to scroll both ways.
-<DataTable :value="cars" :scrollable="true" scrollHeight="200px" frozenWidth="300px" :loading="loading">
- <Column field="vin" header="Vin" headerStyle="width: 300px" columnKey="vin_1" :frozen="true">
- <template #body="slotProps">
- <span style="font-weight: bold">{{slotProps.data.vin}}</span>
- </template>
- </Column>
- <Column field="year" header="Year" headerStyle="width: 300px" columnKey="year_1"></Column>
- <Column field="brand" header="Brand" headerStyle="width: 300px" columnKey="brand_1"></Column>
- <Column field="color" header="Color" headerStyle="width: 300px" columnKey="color_1"></Column>
- <Column field="year" header="Year" headerStyle="width: 300px" columnKey="year_2"></Column>
- <Column field="brand" header="Brand" headerStyle="width: 300px" columnKey="brand_2"></Column>
- <Column field="color" header="Color" headerStyle="width: 300px" columnKey="color_2"></Column>
- <Column field="year" header="Year" headerStyle="width: 300px" columnKey="year_3"></Column>
- <Column field="brand" header="Brand" headerStyle="width: 300px" columnKey="brand_3"></Column>
- <Column field="color" header="Color" headerStyle="width: 300px" columnKey="color_3"></Column>
+<DataTable :value="customers" :scrollable="true" scrollHeight="400px" scrollDirection="both">
+ <Column field="id" header="Id" footer="Id" :style="{width:'200px'}"></Column>
+ <Column field="name" header="Name" footer="Name" :style="{width:'200px'}"></Column>
+ <Column field="country.name" header="Country" footer="Country" :style="{width:'200px'}"></Column>
+ <Column field="date" header="Date" footer="Date" :style="{width:'200px'}"></Column>
+ <Column field="balance" header="Balance" footer="Balance" :style="{width:'200px'}"></Column>
+ <Column field="company" header="Company" footer="Company" :style="{width:'200px'}"></Column>
+ <Column field="status" header="Status" footer="Status" :style="{width:'200px'}"></Column>
+ <Column field="activity" header="Activity" footer="Activity" :style="{width:'200px'}"></Column>
+ <Column field="representative.name" header="Representative" footer="Representative" :style="{width:'200px'}"></Column>
</DataTable>
- Note that frozen columns are enabled, frozen and scrollable cells may have content with varying height which leads to misalignment. Provide fixed height to cells to avoid alignment issues.
+Frozen rows are used to fix certain rows while scrolling, this data is defined with the frozenValue property.
+
-<DataTable :value="cars" :scrollable="true" scrollHeight="200px" frozenWidth="300px" :loading="loading">
- <Column field="vin" header="Vin" headerStyle="width: 300px" bodyStyle="height: 25px" columnKey="vin" :frozen="true">
- <template #body="slotProps">
- <span style="font-weight: bold">{{slotProps.data.vin}}</span>
- </template>
- </Column>
- <Column field="year" header="Year" headerStyle="width: 300px" bodyStyle="height: 25px" columnKey="year"></Column>
- <Column field="brand" header="Brand" headerStyle="width: 300px" bodyStyle="height: 25px" columnKey="brand"></Column>
- <Column field="color" header="Color" headerStyle="width: 300px" bodyStyle="height: 25px" columnKey="color"></Column>
+<DataTable :value="customers" :frozenValue="lockedCustomers" :scrollable="true" scrollHeight="400px">
+ <Column field="name" header="Name"></Column>
+ <Column field="country.name" header="Country"></Column>
+ <Column field="representative.name" header="Representative"></Column>
+ <Column field="status" header="Status"></Column>
</DataTable>
- One or more rows can be displayed as fixed using the frozenValue property.
+Certain columns can be frozen by using the frozen property of the column component. In addition alignFrozen is available to define whether the column should + be fixed on the left or right.
+
-<DataTable :value="cars" :frozenValue="frozenCars" :scrollable="true" scrollHeight="200px" :loading="loading">
- <Column field="vin" header="Vin"></Column>
- <Column field="year" header="Year"></Column>
- <Column field="brand" header="Brand"></Column>
- <Column field="color" header="Color"></Column>
+<DataTable :value="customers" :scrollable="true" scrollHeight="400px" scrollDirection="both">
+ <Column field="name" header="Name" :style="{width:'200px'}" frozen></Column>
+ <Column field="id" header="Id" :style="{width:'100px'}" :frozen="idFrozen"></Column>
+ <Column field="name" header="Name" :style="{width:'200px'}"></Column>
+ <Column field="country.name" header="Country" :style="{width:'200px'}"></Column>
+ <Column field="date" header="Date" :style="{width:'200px'}"></Column>
+ <Column field="company" header="Company" :style="{width:'200px'}"></Column>
+ <Column field="status" header="Status" :style="{width:'200px'}"></Column>
+ <Column field="activity" header="Activity" :style="{width:'200px'}"></Column>
+ <Column field="representative.name" header="Representative" :style="{width:'200px'}"></Column>
+ <Column field="balance" header="Balance" :style="{width:'200px'}" frozen alignFrozen="right"></Column>
</DataTable>
- When using frozen columns with column grouping, use frozenheadergroup and frozenfootergroup types to define grouping for the frozen section.
- -Virtual scrolling is enabled using virtualScroll and onVirtualScroll properties combined with lazy loading so that data is loaded on the fly during scrolling.
- For smooth scrolling twice the amount of rows property is loaded on a lazy load event. In addition, to avoid performance problems row height is not calculated automatically and
- should be provided using virtualRowHeight property which defaults to 28px. View the
-<DataTable :value="lazyCars" :scrollable="true" scrollHeight="200px" :lazy="true" :rows="20"
- :virtualScroll="true" :virtualRowHeight="30" @virtual-scroll="onVirtualScroll" :totalRecords="lazyTotalRecords">
- <Column field="vin" header="Vin">
- <template #loading>
- <span class="loading-text"></span>
- </template>
- </Column>
- <Column field="year" header="Year">
- <template #loading>
- <span class="loading-text"></span>
- </template>
- </Column>
- <Column field="brand" header="Brand">
- <template #loading>
- <span class="loading-text"></span>
- </template>
- </Column>
- <Column field="color" header="Color">
- <template #loading>
- <span class="loading-text"></span>
- </template>
- </Column>
-</DataTable>
-
-
-
-
-import CarService from '../../service/CarService';
-
-export default {
- data() {
- return {
- lazyCars: null,
- lazyTotalRecords: 0
- }
- },
- carService: null,
- mounted() {
- this.lazyCars = this.loadChunk(0, 40);
- this.lazyTotalRecords = //retrieve logical number of rows from a datasource;
- },
- methods: {
- loadChunk(index, length) {
- //return data from a datasource between [index, index + length];
- },
- onVirtualScroll(event) {
- //last chunk
- if (event.first === (this.lazyTotalRecords - 20))
- this.lazyCars = this.loadChunk(event.first, 20)
- else
- this.lazyCars = this.loadChunk(event.first, event.rows)
- }
- }
-}
-
-
+ Row groups with subheaders have exclusive support for filtering, when the table scrolls the subheaders stay fixed as long as their
+ data are still displayed. No additional configuration is required to enable this feature. View the
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 such as paging and sorting. Sample belows imitates lazy paging by using an in memory list. @@ -1804,18 +1901,6 @@ export default {
Group customers by their representative.