diff --git a/src/components/api/Api.d.ts b/src/components/api/Api.d.ts
new file mode 100644
index 0000000000..c71053aa97
--- /dev/null
+++ b/src/components/api/Api.d.ts
@@ -0,0 +1,50 @@
+export const FilterMatchMode: {
+ STARTS_WITH?: string,
+ CONTAINS?: string,
+ NOT_CONTAINS?: string,
+ ENDS_WITH?: string,
+ EQUALS?: string,
+ NOT_EQUALS?: string,
+ IN?: string,
+ LESS_THAN?: string,
+ LESS_THAN_OR_EQUAL_TO?: string,
+ GREATER_THAN?: string,
+ GREATER_THAN_OR_EQUAL_TO?: string,
+ BETWEEN?: string,
+ DATE_IS?: string,
+ DATE_IS_NOT?: string,
+ DATE_BEFORE?: string,
+ DATE_AFTER?: string
+}
+
+export const FilterOperator: {
+ AND?: string,
+ OR?: string
+}
+
+interface FiltersOptions {
+ startsWith?(value: any, filter: any, filterLocale: string): boolean,
+ contains?(value: any, filter: any, filterLocale: string): boolean,
+ notContains?(value: any, filter: any, filterLocale: string): boolean,
+ endsWith?(value: any, filter: any, filterLocale: string): boolean,
+ equals?(value: any, filter: any, filterLocale: string): boolean,
+ notEquals?(value: any, filter: any, filterLocale: string): boolean,
+ in?(value: any, filter: any): boolean,
+ between?(value: any, filter: any): boolean,
+ lt?(value: any, filter: any): boolean,
+ lte?(value: any, filter: any): boolean,
+ gt?(value: any, filter: any): boolean,
+ gte?(value: any, filter: any): boolean,
+ dateIs?(value: any, filter: any): boolean,
+ dateIsNot?(value: any, filter: any): boolean,
+ dateBefore?(value: any, filter: any): boolean,
+ dateAfter?(value: any, filter: any): boolean
+}
+
+interface FilterServiceOptions {
+ filter?(value: any[], fields: any[], filterValue: any, filterMatchMode: string, filterLocale: string): any[],
+ filters?: FiltersOptions,
+ register?(rule: string, fn: any): void
+}
+
+export const FilterService: FilterServiceOptions;
diff --git a/src/components/api/Api.js b/src/components/api/Api.js
new file mode 100644
index 0000000000..398c1db5d2
--- /dev/null
+++ b/src/components/api/Api.js
@@ -0,0 +1,5 @@
+import FilterMatchMode from './FilterMatchMode';
+import FilterOperator from './FilterOperator';
+import FilterService from './FilterService';
+
+export {FilterMatchMode,FilterOperator,FilterService};
\ No newline at end of file
diff --git a/src/components/api/FilterMatchMode.js b/src/components/api/FilterMatchMode.js
new file mode 100644
index 0000000000..4ff43bcde2
--- /dev/null
+++ b/src/components/api/FilterMatchMode.js
@@ -0,0 +1,20 @@
+const FilterMatchMode = {
+ STARTS_WITH : 'startsWith',
+ CONTAINS : 'contains',
+ NOT_CONTAINS : 'notContains',
+ ENDS_WITH : 'endsWith',
+ EQUALS : 'equals',
+ NOT_EQUALS : 'notEquals',
+ IN : 'in',
+ LESS_THAN : 'lt',
+ LESS_THAN_OR_EQUAL_TO : 'lte',
+ GREATER_THAN : 'gt',
+ GREATER_THAN_OR_EQUAL_TO : 'gte',
+ BETWEEN : 'between',
+ DATE_IS : 'dateIs',
+ DATE_IS_NOT : 'dateIsNot',
+ DATE_BEFORE : 'dateBefore',
+ DATE_AFTER : 'dateAfter'
+}
+
+export default FilterMatchMode;
\ No newline at end of file
diff --git a/src/components/api/FilterOperator.js b/src/components/api/FilterOperator.js
new file mode 100644
index 0000000000..76adf7d645
--- /dev/null
+++ b/src/components/api/FilterOperator.js
@@ -0,0 +1,6 @@
+const FilterOperator = {
+ AND: 'and',
+ OR: 'or'
+}
+
+export default FilterOperator;
\ No newline at end of file
diff --git a/src/components/api/FilterService.js b/src/components/api/FilterService.js
new file mode 100644
index 0000000000..7b0fa9ec03
--- /dev/null
+++ b/src/components/api/FilterService.js
@@ -0,0 +1,240 @@
+import ObjectUtils from '../utils/ObjectUtils';
+
+const FilterService = {
+ filter(value, fields, filterValue, filterMatchMode, filterLocale) {
+ let filteredItems = [];
+
+ if (value) {
+ for (let item of value) {
+ for (let field of fields) {
+ let fieldValue = ObjectUtils.resolveFieldData(item, field);
+
+ if (this.filters[filterMatchMode](fieldValue, filterValue, filterLocale)) {
+ filteredItems.push(item);
+ break;
+ }
+ }
+ }
+ }
+
+ return filteredItems;
+ },
+ filters: {
+ startsWith(value, filter, filterLocale) {
+ if (filter === undefined || filter === null || filter.trim() === '') {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ let filterValue = ObjectUtils.removeAccents(filter.toString()).toLocaleLowerCase(filterLocale);
+ let stringValue = ObjectUtils.removeAccents(value.toString()).toLocaleLowerCase(filterLocale);
+
+ return stringValue.slice(0, filterValue.length) === filterValue;
+ },
+ contains(value, filter, filterLocale) {
+ if (filter === undefined || filter === null || (typeof filter === 'string' && filter.trim() === '')) {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ let filterValue = ObjectUtils.removeAccents(filter.toString()).toLocaleLowerCase(filterLocale);
+ let stringValue = ObjectUtils.removeAccents(value.toString()).toLocaleLowerCase(filterLocale);
+
+ return stringValue.indexOf(filterValue) !== -1;
+ },
+ notContains(value, filter, filterLocale) {
+ if (filter === undefined || filter === null || (typeof filter === 'string' && filter.trim() === '')) {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ let filterValue = ObjectUtils.removeAccents(filter.toString()).toLocaleLowerCase(filterLocale);
+ let stringValue = ObjectUtils.removeAccents(value.toString()).toLocaleLowerCase(filterLocale);
+
+ return stringValue.indexOf(filterValue) === -1;
+ },
+ endsWith(value, filter, filterLocale) {
+ if (filter === undefined || filter === null || filter.trim() === '') {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ let filterValue = ObjectUtils.removeAccents(filter.toString()).toLocaleLowerCase(filterLocale);
+ let stringValue = ObjectUtils.removeAccents(value.toString()).toLocaleLowerCase(filterLocale);
+
+ return stringValue.indexOf(filterValue, stringValue.length - filterValue.length) !== -1;
+ },
+ equals(value, filter, filterLocale) {
+ if (filter === undefined || filter === null || (typeof filter === 'string' && filter.trim() === '')) {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ if (value.getTime && filter.getTime)
+ return value.getTime() === filter.getTime();
+ else
+ return ObjectUtils.removeAccents(value.toString()).toLocaleLowerCase(filterLocale) == ObjectUtils.removeAccents(filter.toString()).toLocaleLowerCase(filterLocale);
+ },
+ notEquals(value, filter, filterLocale) {
+ if (filter === undefined || filter === null || (typeof filter === 'string' && filter.trim() === '')) {
+ return false;
+ }
+
+ if (value === undefined || value === null) {
+ return true;
+ }
+
+ if (value.getTime && filter.getTime)
+ return value.getTime() !== filter.getTime();
+ else
+ return ObjectUtils.removeAccents(value.toString()).toLocaleLowerCase(filterLocale) != ObjectUtils.removeAccents(filter.toString()).toLocaleLowerCase(filterLocale);
+ },
+ in(value, filter) {
+ if (filter === undefined || filter === null || filter.length === 0) {
+ return true;
+ }
+
+ for (let i = 0; i < filter.length; i++) {
+ if (ObjectUtils.equals(value, filter[i])) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+ between(value, filter) {
+ if (filter == null || filter[0] == null || filter[1] == null) {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ if (value.getTime)
+ return filter[0].getTime() <= value.getTime() && value.getTime() <= filter[1].getTime();
+ else
+ return filter[0] <= value && value <= filter[1];
+ },
+ lt(value, filter) {
+ if (filter === undefined || filter === null) {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ if (value.getTime && filter.getTime)
+ return value.getTime() < filter.getTime();
+ else
+ return value < filter;
+ },
+ lte(value, filter) {
+ if (filter === undefined || filter === null) {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ if (value.getTime && filter.getTime)
+ return value.getTime() <= filter.getTime();
+ else
+ return value <= filter;
+ },
+ gt(value, filter) {
+ if (filter === undefined || filter === null) {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ if (value.getTime && filter.getTime)
+ return value.getTime() > filter.getTime();
+ else
+ return value > filter;
+ },
+ gte(value, filter) {
+ if (filter === undefined || filter === null) {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ if (value.getTime && filter.getTime)
+ return value.getTime() >= filter.getTime();
+ else
+ return value >= filter;
+ },
+ dateIs(value, filter) {
+ if (filter === undefined || filter === null) {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ return value.toDateString() === filter.toDateString();
+ },
+ dateIsNot(value, filter) {
+ if (filter === undefined || filter === null) {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ return value.toDateString() !== filter.toDateString();
+ },
+ dateBefore(value, filter) {
+ if (filter === undefined || filter === null) {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ return value.getTime() < filter.getTime();
+ },
+ dateAfter(value, filter) {
+ if (filter === undefined || filter === null) {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ return value.getTime() > filter.getTime();
+ }
+ },
+ register(rule, fn) {
+ this.filters[rule] = fn;
+ }
+}
+
+export default FilterService;
\ No newline at end of file
diff --git a/src/components/api/index.d.ts b/src/components/api/index.d.ts
new file mode 100644
index 0000000000..13289cd982
--- /dev/null
+++ b/src/components/api/index.d.ts
@@ -0,0 +1 @@
+export * from './Api';
\ No newline at end of file
diff --git a/src/components/api/index.js b/src/components/api/index.js
new file mode 100644
index 0000000000..cd2a5662cd
--- /dev/null
+++ b/src/components/api/index.js
@@ -0,0 +1,2 @@
+'use strict';
+module.exports = require('./Api.js');
\ No newline at end of file
diff --git a/src/components/api/plugins.js b/src/components/api/plugins.js
new file mode 100644
index 0000000000..2865b9f93c
--- /dev/null
+++ b/src/components/api/plugins.js
@@ -0,0 +1,4 @@
+import Vue from 'vue';
+import PrimeVue from 'primevue/config';
+
+Vue.use(PrimeVue);
\ No newline at end of file
diff --git a/src/views/filterservice/FilterServiceDemo.vue b/src/views/filterservice/FilterServiceDemo.vue
new file mode 100644
index 0000000000..b73de86bc9
--- /dev/null
+++ b/src/views/filterservice/FilterServiceDemo.vue
@@ -0,0 +1,97 @@
+
+
+
+
+
FilterService
+
FilterService is a helper utility to filter collections against constraints.
+
+
+
+
+
+
+
Table Integration
+
A custom equals filter that checks for exact case sensitive value is registered and defined as a match mode of a column filter.
+
+
+
+ No customers found.
+
+
+ Loading customers data. Please wait.
+
+
+
+ {{data.name}}
+
+
+
+
+
+
+
+
+ {{data.country.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/filterservice/FilterServiceDoc.vue b/src/views/filterservice/FilterServiceDoc.vue
new file mode 100644
index 0000000000..a62461a8f8
--- /dev/null
+++ b/src/views/filterservice/FilterServiceDoc.vue
@@ -0,0 +1,280 @@
+
+
+
+
+ Import
+
+import FilterService from 'primevue/api';
+
+
+ Getting Started
+ Filters are accessed with FilterService.filters.
+
+const value = 'PrimeVue';
+
+FilterService.filters.equals(value, 'Vue'); //false
+FilterService.filters.equals(value, 8); //false
+FilterService.filters.equals(value, new Date()); //false
+FilterService.filters.contains(value, 'Vue'); //true
+FilterService.filters.startsWith(value, 'Vue'); //false
+FilterService.filters.endsWith(value, 'Vue'); //true
+FilterService.filters.lt(10, 20); //true
+FilterService.filters.gt(50, 20); //true
+FilterService.filters.in(value, ['PrimeFaces', 'PrimeVue']); //true
+
+
+ Custom Constraint
+ FilterService can be extended by adding new constraints using the register function.
+
+FilterService.register('isPrimeNumber', (value, filter): boolean => {
+ if (filter === undefined || filter === null || filter.trim() === '') {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ return value.toString() === filter.toString();
+});
+
+FilterService.filters['isPrimeNumber'](3); //true
+FilterService.filters['isPrimeNumber'](5); //true
+FilterService.filters['isPrimeNumber'](568985673); //false
+
+ Built-in Constraints
+
+
+
+
+ Name |
+ Parameters |
+ Description |
+
+
+
+
+ startsWith |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the value starts with the filter value |
+
+
+ contains |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the value contains the filter value |
+
+
+ endsWith |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the value ends with the filter value |
+
+
+ equals |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the value equals the filter value |
+
+
+ notEquals |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the value does not equal the filter value |
+
+
+ in |
+ value: Value to filter
+ filter[]: An array of filter values
+ filterLocale: Locale to use in filtering |
+ Whether the value contains the filter value |
+
+
+ lt |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the value is less than the filter value |
+
+
+ lte |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the value is less than or equals to the filter value |
+
+
+ gt |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the value is greater than the filter value |
+
+
+ gte |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the value is greater than or equals to the filter value |
+
+
+ is |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the value equals the filter value, alias to equals |
+
+
+ isNot |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the value does not equal the filter value, alias to notEquals. |
+
+
+ before |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the date value is before the filter date. |
+
+
+ after |
+ value: Value to filter
+ filter: Filter value
+ filterLocale: Locale to use in filtering |
+ Whether the date value is after the filter date. |
+
+
+
+
+
FilterService API
+
+
+
+
+ Name |
+ Parameters |
+ Description |
+
+
+
+
+ filter |
+ value[]: An array of values to filter
+ fields[]: An array of properties in the value object
+ filterValue: Filter value
+ filterMatchMode: Constraint
+ filterLocale: Locale to use in filtering |
+ Whether the property values of the given value collection matches the filter. |
+
+
+ filters |
+ - |
+ Property to access constraints collection. |
+
+
+ register |
+ name: string
+ fn: Filter callback |
+ Registers a new constraint in filters. |
+
+
+
+
+
+
+ Dependencies
+ None.
+
+
+
+
+ View on GitHub
+
+
+
+<DataTable :value="customers" :paginator="true" :rows="10" responsiveLayout="scroll"
+ dataKey="id" :filters.sync="filters" filterDisplay="row" :loading="loading">
+ <template #empty>
+ No customers found.
+ </template>
+ <template #loading>
+ Loading customers data. Please wait.
+ </template>
+ <Column field="name" header="Name" :filterMatchModeOptions="matchModeOptions">
+ <template #body="{data}">
+ {{data.name}}
+ </template>
+ <template #filter="{filterModel,filterCallback}">
+ <InputText type="text" v-model="filterModel.value" @input="filterCallback()" class="p-column-filter" :placeholder="`Search by name - ${filterModel.matchMode}`"/>
+ </template>
+ </Column>
+ <Column header="Country" filterField="country.name" :filterMatchModeOptions="matchModeOptions">
+ <template #body="{data}">
+ <img src="../../assets/images/flag_placeholder.png" :class="'flag flag-' + data.country.code" width="30" />
+ <span class="image-text">{{data.country.name}}</span>
+ </template>
+ <template #filter="{filterModel,filterCallback}">
+ <InputText type="text" v-model="filterModel.value" @input="filterCallback()" class="p-column-filter" :placeholder="`Search by country - ${filterModel.matchMode}`" />
+ </template>
+ </Column>
+</DataTable>
+
+
+
+
+import FilterMatchMode from '../../../src/components/api/FilterMatchMode';
+import FilterService from '../../../src/components/api/FilterService';
+import CustomerService from '../../service/CustomerService';
+
+const YOUR_FILTER = 'YOUR FILTER';
+
+export default {
+ data() {
+ return {
+ customers: null,
+ filters: {
+ 'name': {value: null, matchMode: YOUR_FILTER},
+ 'country.name': {value: null, matchMode: FilterMatchMode.STARTS_WITH}
+ },
+ matchModeOptions: [
+ {label: 'Your Equals', value: YOUR_FILTER},
+ {label: 'Starts With', value: FilterMatchMode.STARTS_WITH}
+ ],
+ loading: true
+ }
+ },
+ created() {
+ this.customerService = new CustomerService();
+ },
+ mounted() {
+ this.customerService.getCustomersLarge().then(data => {
+ this.customers = data;
+ this.loading = false;
+ });
+
+ FilterService.register(YOUR_FILTER, (value, filter) => {
+ if (filter === undefined || filter === null || filter.trim() === '') {
+ return true;
+ }
+
+ if (value === undefined || value === null) {
+ return false;
+ }
+
+ return value.toString() === filter.toString();
+ });
+ }
+}
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/locale/LocaleDemo.vue b/src/views/locale/LocaleDemo.vue
index a2d9e11320..4961670ffd 100644
--- a/src/views/locale/LocaleDemo.vue
+++ b/src/views/locale/LocaleDemo.vue
@@ -39,94 +39,51 @@ export default {
Locale Options
-
-
-
-
- Key |
- Value |
-
-
-
-
- accept |
- Yes |
-
-
- reject |
- No |
-
-
- choose |
- Choose |
-
-
- upload |
- Upload |
-
-
- cancel |
- Cancel |
-
-
- dayNames |
- ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] |
-
-
- dayNamesShort |
- ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] |
-
-
- dayNamesMin |
- ["Su","Mo","Tu","We","Th","Fr","Sa"] |
-
-
- monthNames |
- ["January","February","March","April","May","June","July","August","September","October","November","December"] |
-
-
- monthNamesShort |
- ["Jan", "Feb", "Mar", "Apr", "May", "Jun","Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] |
-
-
- today |
- Today |
-
-
- today |
- Today |
-
-
- weekHeader |
- Wk |
-
-
- firstDayOfWeek |
- 0 |
-
-
- dateFormat |
- mm/dd/yy |
-
-
- weak |
- Weak |
-
-
- medium |
- Medium |
-
-
- strong |
- Strong |
-
-
- passwordPrompt |
- Enter a password |
-
-
-
-
+
+locale: {
+ startsWith: 'Starts with',
+ contains: 'Contains',
+ notContains: 'Not contains',
+ endsWith: 'Ends with',
+ equals: 'Equals',
+ notEquals: 'Not equals',
+ noFilter: 'No Filter',
+ lt: 'Less than',
+ lte: 'Less than or equal to',
+ gt: 'Greater than',
+ gte: 'Greater than or equal to',
+ dateIs: 'Date is',
+ dateIsNot: 'Date is not',
+ dateBefore: 'Date is before',
+ dateAfter: 'Date is after',
+ clear: 'Clear',
+ apply: 'Apply',
+ matchAll: 'Match All',
+ matchAny: 'Match Any',
+ addRule: 'Add Rule',
+ removeRule: 'Remove Rule',
+ accept: 'Yes',
+ reject: 'No',
+ choose: 'Choose',
+ upload: 'Upload',
+ cancel: 'Cancel',
+ dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"],
+ monthNames: ["January","February","March","April","May","June","July","August","September","October","November","December"],
+ monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun","Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: 'Today',
+ weekHeader: 'Wk',
+ firstDayOfWeek: 0,
+ dateFormat: 'mm/dd/yy',
+ weak: 'Weak',
+ medium: 'Medium',
+ strong: 'Strong',
+ passwordPrompt: 'Enter a password',
+ emptyFilterMessage: 'No results found',
+ emptyMessage: 'No available options'
+}
+