Filtering a Table with Angular Material: Using multiple filters and filter values simultaneously

Looking to implement dual filters for a table of objects, one being text-based and the other checkbox-based. The text filter works fine, but struggling with the checkbox filter labeled "Level 1", "Level 2", etc. Ideally, when a checkbox is checked, it should filter by the "level" column based on all currently selected checkboxes.

Attempted to use filterPredicate and referenced this template, but encountering issues. Here's the current code snippet:

HTML:

//Input for text filter
<mat-form-field >
  <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter...">
</mat-form-field>

...

//Checkboxes for level filter
<div fxLayout="row" fxLayoutAlign="space-between center" class="margin-1">
    <mat-checkbox (change)="customFilterPredicate()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.level}}</mat-checkbox>
</div>

TS

ngOnInit() {
     this.dataSource.filterPredicate = this.customFilterPredicate();
  }

...

applyFilter(filterValue: string) {
    this.dataSource.filter = filterValue.trim().toLowerCase();
  }

customFilterPredicate() {
    const myFilterPredicate = (data): boolean => {
      return this.levelsToShow.some(data['level'].toString().trim());
    };
    return myFilterPredicate;
  }

Update

Made progress thanks to Fabian Küng, but not fully resolved. The objective is to allow the text filter to search through multiple columns ('castingTime', 'duration', etc) while the checkboxes only filter by level.

Encountering an error when clicking a checkbox: 'Cannot read property 'toLowerCase' of undefined' pointing to this line of code:

return data['level'].toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1 &&

Despite logging out data showing a 'level' property exists, the issue remains unresolved.

Relevant snippets provided below:

    HTML:
    
        <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter..." [formControl]="textFilter">
        
        <mat-checkbox (change)="updateFilter()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.name}}</mat-checkbox>
    
    TS:
    
        levelsToShow: Level[] = [
            { level: '1', active: false, name: 'Level 1' },
            { level: '2', active: false, name: 'Level 2' },
            ...
          ];
        
          levelFilter = new FormControl();
          textFilter = new FormControl();
          globalFilter = '';
        
          filteredValues = {
            level: '', 
            text: '', 
          };

...

Answer №1

You were headed in the right direction by setting up the checkboxes and utilizing the model of the checkboxes to filter within the customFilterPredicate function.

I took the stackblitz link provided and made modifications to align it with your checkboxes setup, you can view it here.

In your template, the checkboxes are configured as follows:

<mat-checkbox (change)="updateFilter()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.name}}</mat-checkbox>

In the component, I introduced a new interface called Level:

export interface Level {
  active: boolean;
  name: string;
  level: number;
}

and included some sample data:

levelsToShow: Level[] = [
  { level: 1, active: false, name: 'Level 1' },
  { level: 2, active: false, name: 'Level 2' },
  { level: 3, active: false, name: 'Level 3' },
];

The crux lies within the customFilterPredicate function. Essentially, it filters based on the name property initially. Additionally, it checks if all levels are marked as false; in such instances, it returns true assuming a desire to display all levels when no checkbox is selected. If a level is active, it will filter by that specific level, or multiple levels if more than one are chosen.

return data.name.toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1 &&
        (this.levelsToShow.filter(level => !level.active).length === this.levelsToShow.length ||
         this.levelsToShow.filter(level => level.active).some(level => level.level === data.level));

An essential aspect is ensuring that the filtering occurs promptly upon checking/unchecking a checkbox. This can be achieved by updating the datasource filter each time a checkbox value changes:

updateFilter() {
  this.dataSource.filter = JSON.stringify(this.filteredValues);
}

Answer №2

A SIMPLER APPROACH

Utilize FilterPredicate within the data source to establish multiple expressions.

Utilize Form Group to allocate controls that will transmit filter values.

<form [formGroup]="filterForm">

  <mat-form-field>
    <input matInput placeholder="Model" formControlName="model" />
  </mat-form-field>

  <mat-form-field>
    <input matInput placeholder="Type" formControlName="modelType" />
  </mat-form-field>

  <mat-form-field appearance="fill">
    <mat-label>Status</mat-label>
    <mat-select formControlName="status">
      <mat-option value=""></mat-option>
      <mat-option value="Active">Active</mat-option>
      <mat-option value="InActive">InActive</mat-option>
    </mat-select>
  </mat-form-field>

#TypeScript Indicate the filterPredicate expression

// Filter Predicate Expression for executing Multiple Keyword Search
this.dataSource.filterPredicate = ((data: DeviceInventory, filter) => {          
const a = !filter.model || data.modelCode.trim().toLowerCase().includes(filter.model);
const b = !filter.modelType || data.modelType.trim().toLowerCase().includes(filter.modelType);
return a && b;
}) as (DeviceInventory, string) => boolean;

Subscribe to FormGroup Value changes to monitor filterValues.

this.filterForm.valueChanges.subscribe(value => {
this.dataSource.filter = value;
console.log(value);
});

}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the process for defining a property type as a textual representation of a Type name in TypeScript?

Consider the following classes and interface: class Foo {} interface Bar {} I am looking to define a type that includes a property with a specified type: type DynamicPropertyName<T> = ... <-- ??? After defining the type, I want to use it like th ...

Refreshing data from firebase on each route change

I created an Angular service that retrieves items from Firebase using snapshotChanges and returns them. In my component, I subscribe to the data and store it in an array. I then display the data in cards using ngFor. However, whenever I navigate between p ...

What to do in Angular2 when two 'root' elements are required?

I've taken on the challenge of teaching myself angular2 and am currently working on a basic app that follows a common website layout: Header for items like search and logo, and main body for content. My current dilemma is this: Take Facebook as an ex ...

Why does the onBlur event function in Chrome but fails to work in Safari?

I've encountered a problem with the onBlur event in react-typescript. To replicate the issue, I clicked the upButton repeatedly to increase the number of nights to 9 or more, which is the maximum allowed. Upon further clicking the upButton, an error m ...

The patchValue() function in FormGroup does not fire the input event

In my code, I have a FormGroup dedicated to credit card inputs. To format the inputs, I'm using a directive with the selector 'appCreditCardFormat'. Here's a simplified version: <input formControlName="cardNo" appC ...

Testing React Hooks in TypeScript with Jest and @testing-library/react-hooks

I have a unique custom hook designed to manage the toggling of a product id using a boolean value and toggle function as returns. As I attempt to write a unit test for it following a non-typescripted example, I encountered type-mismatch errors that I' ...

What should be transmitted to the front-end following the successful validation of a token on the server?

My process starts with a login page and then moves to the profile page. When it comes to handling the token on the backend, I use the following code: app.use(verifyToken); function verifyToken(req, res, next) { if (req.path === '/auth/google&ap ...

Insert HTML elements into the variable that holds data retrieved from firestore

Recently, I received a Firestore response in the following format: Within my TypeScript variable {{task.title}}, I have access to this data on my component. My goal is to incorporate a hyperlink specifically on the person's name (dev2) that directs t ...

Error message in Angular Reactive Forms: Control with specified path cannot be found

My current challenge lies within the component: cabinet-clinic-form.component.ts. I am facing an issue with FormGroup validation in my clinics FormArray. Upon rendering the page, I encounter an error as shown in this screenshot. Despite attempting to dynam ...

The identifier 'id' is not recognized within the 'never' type in Angular

Hello there, I am currently working on a small project for a store website. You can find the project here. However, I have encountered an issue when trying to move items to the cart. Specifically, in the source code file app/components/product-list/produc ...

I'm curious about why I'm receiving the error "Unable to bind to 'ngFor' since it is not recognized as a property of 'li'. Can someone please explain why this is happening?

The issue is related to the *ngFor directive for nonvegfoodlist app.component.ts import { Component } from '@angular/core'; export class Menu { id : number; name :string; } const veg : Menu[] = [ { id:1 , name:'Rice'}, { id: ...

Date selection feature in Material UI causing application malfunction when using defaultValue attribute with Typescript

Recently, I discovered the amazing functionality of the Material UI library and decided to try out their date pickers. Everything seemed fine at first, but now I'm facing an issue that has left me puzzled. Below is a snippet of my code (which closely ...

Anticipating the resolution of promises and observables in Angular 2

Within my accountService module, there is a dialog prompt that requests the user's username and password, returning a promise. If the user clicks on close instead of dismissing the dialog box and the validators require the input data before allowing t ...

What is preventing me from setting the User object to null in my Angular application?

Currently, I am working on a project in Angular and encountering a specific issue. In my service class, the structure looks like this: export class AuthService { authchange: new Subject<boolean>(); private user: User; registerUser(authD ...

Unable to find solutions for all parameters in AnalysisComponent: ([object Object], ?, ?, [

As a newcomer to the Angular framework, I encountered an issue when adding project services. Error: Can't resolve all parameters for AnalysisComponent: ([object Object], ?, ?, [object Object], [object Object], [object Object], [object Object], [obj ...

What could be causing the Properties Array to come back as undefined?

When attempting to add an item to an array stored in the properties, I am encountering an error: "Cannot read properties of undefined (reading 'value')." Within the props, the following interfaces are defined: ILinkItemProps.ts export interface ...

Can you explain why it prints to the console twice every time I try to add an item?

This is a note-taking application created using Angular and Firebase. The primary functionalities include adding items and displaying them. I noticed a strange behavior where my ngOnInit() method is being called twice every time I add an item. As shown in ...

Issues with modals appearing improperly after transitioning to NG-Bootstrap 15 and Bootstrap 5 upgrade

UPDATED following a thorough examination: In the process of upgrading our application from NG-Boostrap v11 with bootstrap 4 to NG-Boostrap v15 with Bootstrap 5 and Popper, we also made the switch from Angular 15 to Angular 16. Despite successfully resolvi ...

What could be causing React to generate an error when attempting to utilize my custom hook to retrieve data from Firebase using context?

Currently, I am restructuring my code to improve organization by moving data fetching to custom hooks instead of within the component file. However, I am encountering issues with the hook not functioning properly when used in conjunction with my context. ...

The NestJS framework encountered an error due to a method being undefined in the

Encountering Error with NestJS Function create123: TypeError - Cannot read properties of undefined (reading 'create123') The constructor is displayed below \`export class AuthenticationService { constructor( private readonly usersServ ...