7
votes

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>
3
Did you find a solution ? - Tim
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. - dendimiiii
@Tim Maybe my answer can help. (I mentioned you because you won't get notified on answers to somebody else's question) - mtx
Thanks @mtx - I will try your solution. In the mean time, I changed my data array to be flat to begin with. - Tim

3 Answers

8
votes

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.

1
votes

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');
0
votes

In this GitHub issue we see that they removed filterConstraints which could be used with prior PrimeNG versions to extend the filter functionality.

So... if you're on PrimeNG version 9 like me you can use FilterUtils instead.

import { FilterUtils } from 'primeng/utils';

/**
* Custom filter to be used with PrimeNG Table
* https://www.primefaces.org/primeng/showcase/#/table
* Once we upgrade to version 11, this can be improved with FilterService:
* https://www.primefaces.org/primeng/showcase/#/filterservice
*/
setupCustomTableFilter() {
    FilterUtils['customContains'] = function customContains(value: any, filter: any): boolean {
        if (filter === undefined || filter === null)
            return true;

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

        let searchStringParts = filter.toLowerCase().split(' ') as string[];

        let columnValue = value.toString().toLowerCase();

        //debugger;
        // Make sure that all string parts are contained\included in the column value...
        var isMatch = searchStringParts.every(p => columnValue.includes(p));

        return isMatch;
    }
}

Note that you need to import it from import { FilterUtils } from 'primeng/utils';

To be able to use it everywhere you can define the code above inside your app.component.ts file for example.

The latest version of PrimeNG which is 11 as of now has added a FilterService which makes it more straightforward to implement custom filters.