Is it possible to add custom filterfunction with PrimeNG DataTable?

10,425

Solution 1

I had a similar problem where I had to filter array column-data with multiple selectable filter-values (from a p-multiSelect, so the filter was an array too). I figured out a way to extend the available filterMatchModes with some help from the original source-code. Unfortunately I can't vouch for it being good practice, but hey, in the version I'm using (PrimeNG 4.1.2) it works.

You may have to adapt and tune your filter-function to your needs, but here is what my solution would look like in your case (with a p-dropdown):

  • in the view, export the DataTable into a template-reference-variable (#dt)
  • make it accessible in the component via the @ViewChild-decorator
  • add your own filter-function directly to the DataTable's array of filters (in ngOnInit())

component.ts

@Component({...})
export class DatatableComponent implements OnInit {
    ...
    @ViewChild('dt') dt: DataTable;
    ...
    ngOnInit() {
        this.dt.filterConstraints['inCollection'] = function inCollection(value: any[], filter: any): boolean {
            // value = array of data from the current row
            // filter = value from the filter that will be searched in the value-array

            if (filter === undefined || filter === null) {
                return true;
            }

            if (value === undefined || value === null || value.length === 0) {
                return false;
            }

            for (let i = 0; i < value.length; i++) {
                if (value[i].toLowerCase() === filter.toLowerCase()) {
                    return true;
                }
            }

            return false;
        }
    }
}

In your view you can now use the new filter-function by setting the filterMatchMode of the column to what you named it before:

component.html

<p-dataTable #dt ...>
    ...
    <p-column field="types" filterMatchMode="inCollection" header="{{'AIRPORTS.TYPES' | translate}}">
        <template let-airport="rowData" pTemplate="body">
            <span *ngFor="let type of airport.types; let isLast = last">
                {{('AIRPORTS.' + type) | translate}}{{isLast ? '' : ', '}}
            </span>
        </template>
        <template pTemplate="filter" let-col>
            <p-dropdown [options]="choices"
                        [style]="{'width':'100%'}"
                        (onChange)="dt.filter($event.value,col.field,col.filterMatchMode)"
                        styleClass="ui-column-filter">
            </p-dropdown>
        </template>
    </p-column>
    ...
</p-dataTable>

All I did in the view was set the filterMatchMode, otherwise I copied your code. (I also renamed the template-reference to #dt to make it shorter and more readable)
Hint: you don't need [filter]=true in this column, since the standard filter won't show when your column has a custom filter-template.

As I mentioned, with a custom filter function like this you can for example implement filtering array-data with a p-multiSelect filter (nested loop in filter-function), search for substrings in array or whatever else you can think of.

Solution 2

As a simple workaround, you can directily change the FilterUtils:

import { FilterUtils } from 'primeng/utils';

FilterUtils['filterTest'] = (value, filter) => value <= filter && value >= filter;

this.dt.filter(search, 'field', 'filterTest');
Share:
10,425
dendimiiii
Author by

dendimiiii

Why? Because I can.

Updated on June 15, 2022

Comments

  • dendimiiii
    dendimiiii about 2 years

    Basicly all info is provided in the title.

    It seems to me that I am bound to the filterMatchModes that are available ( contains, in, equals, endsWith, startsWith). In my usecase my column field is an array, and no single value.

    My specified column looks like this:

    <p-column field="types" [filter]="true" header="{{'AIRPORTS.TYPES' | translate}}">
        <template let-airport="rowData" pTemplate="body">
            <span *ngFor="let type of airport.types; let isLast = last">
                {{('AIRPORTS.' + type) | translate}}{{isLast ? '' : ', '}}
            </span>
        </template>
        <template pTemplate="filter" let-col>
            <p-dropdown [options]="choices"
                        [style]="{'width':'100%'}"
                        (onChange)="airportsDataTable.filter($event.value,col.field,col.filterMatchMode)"
                        styleClass="ui-column-filter">
            </p-dropdown>
        </template>
    </p-column>
    
    • Tim
      Tim almost 7 years
      Did you find a solution ?
    • dendimiiii
      dendimiiii almost 7 years
      Nope, only a workaround where i convert my array to a comma separated string, and i use the contains filter... There seems to be no built in way to use a filterfunction. In the source code it's pretty straight forward to where they coded the existing filter functions, so it would be easy to just add one but yeah.. I guess messing with source code is not advised as well :). I should make time for a feature request, but then again I got to few spare time.
    • mtx
      mtx almost 7 years
      @Tim Maybe my answer can help. (I mentioned you because you won't get notified on answers to somebody else's question)
    • Tim
      Tim almost 7 years
      Thanks @mtx - I will try your solution. In the mean time, I changed my data array to be flat to begin with.