how to get Angular search filter to work?

Viewed 1285

I have the following code where I am trying to search the column "Failure signature" by typing a string ,whenever I type a search string it doesn't appear to work ,the data is not filtered based on the search string given and throws the error shown below,I pointed out the line where its throwing the error.

I created a stackblitz link to replicate the error, surprisingly it works with dummy data ,the moment I switch to a backend URL to get data from the database I hit this error

Following is the stackblitz link with complete code

https://stackblitz.com/edit/angular-uxnrrr?file=src/app/failure-signature/failure-signature.component.html

failure-signature.component.html

<html ng-app="failure-signature">
  <h1>Root cause analysis Dashboard</h1>
  <nav class="navbar">
    <input
      class="form-control"
      type="text"
      name="Failure_signature"
      [(ngModel)]="Failure_signature"
      (ngModelChange)="Search($event)"
      placeholder="Search for failure signature...."
    />
  </nav>
  <table class="table table-bordered">
    <thead>
      <tr>
        <!--<th>id</th>-->
        <th>commit</th>
        <th>gerrit</th>
        <th>failure_signature</th>
        <!--<th>commit_message</th>-->
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let cm of wifi_gerrit_commit_messages?.posts">
        <!--<tr *ngFor= "let cm of wifi_gerrit_commit_messages">-->
        <!--<td>{{cm._id}}</td>-->
        <td>{{cm.commit_id}}</td>
        <td>{{cm.gerrit}}</td>
        <td>{{cm.Failure_signature}}</td>
        <!--<td>{{cm.commit_message}}</td>-->
      </tr>
    </tbody>
  </table>
</html>

failure-signature.component.ts

import { Component, OnInit, Injectable, ViewChild } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Subject } from 'rxjs';
import { map } from 'rxjs/operators';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { DomSanitizer } from '@angular/platform-browser';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { enableProdMode } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
import { wifi_gerrit_commit_messages } from '../_model/wifi_gerrit_commit_messages';
import { FailureSignatureService } from './failure-signature-service';
import { RESOURCE_CACHE_PROVIDER } from '@angular/platform-browser-dynamic';

@Component({
  selector: 'app-failure-signature',
  templateUrl: './failure-signature.component.html',
  styleUrls: ['./failure-signature.component.css'],
  animations: [
    trigger('detailExpand', [
      state('collapsed', style({ height: '0px', minHeight: '0' })),
      state('expanded', style({ height: '*' })),
      transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
    ]),
  ],
})
export class FailureSignatureComponent {
  wifi_gerrit_commit_messages: wifi_gerrit_commit_messages[] = [];
  Failure_signature: any;
  constructor(public fss: FailureSignatureService) {}
  ngOnInit(): void {
    this.fss.get_wifi_gerrit_commit_messages().subscribe((Response) => {
      this.wifi_gerrit_commit_messages = Response;
      //console.log(Response)
    });
  }

  Search(value: string) {
    if (value == '') {
      this.ngOnInit();
    } else {
      this.wifi_gerrit_commit_messages = this.wifi_gerrit_commit_messages.filter((res) => {
        return res.Failure_signature.toLocaleLowerCase().match(this.Failure_signature.toLocaleLowerCase());
      });
    }
  }
}
Error Line:-

  this.wifi_gerrit_commit_messages = this.wifi_gerrit_commit_messages.filter(res => {
    return res.Failure_signature.toLocaleLowerCase().match(this.Failure_signature.toLocaleLowerCase());

ERROR:-

   at FailureSignatureComponent.push../src/app/failure-signature/failure-signature.component.ts.FailureSignatureComponent.Search (failure-signature.component.ts:50)
    at Object.eval [as handleEvent] (FailureSignatureComponent.ngfactory.js:62)
    at Object.handleEvent (core.js:27341)
    at Object.handleEvent (core.js:27886)
    at dispatchEvent (core.js:18085)
    at core.js:19288
    at SafeSubscriber.schedulerFn [as _next] (core.js:22113)
    at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:194)
    at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next (Subscriber.js:132)
    at Subscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._next (Subscriber.js:76)

UPDATED ERROR:-

ERROR TypeError: Cannot read properties of undefined (reading 'toLocaleLowerCase')
    at failure-signature.component.ts:77
    at Array.filter (<anonymous>)
    at SearchPipe.push../src/app/failure-signature/failure-signature.component.ts.SearchPipe.transform (failure-signature.component.ts:71)
    at checkAndUpdatePureExpressionInline (core.js:27032)
    at checkAndUpdateNodeInline (core.js:27601)
    at checkAndUpdateNode (core.js:27559)
    at prodCheckAndUpdateNode (core.js:28100)
    at Object.eval [as updateDirectives] (FailureSignatureComponent.ngfactory.js:89)
    at Object.updateDirectives (core.js:27888)
    at checkAndUpdateView (core.js:27541)
4 Answers
  • To be sure, can you create new property for filtered results? instead to mutate wifi_gerrit_commit_messages in your component.ts

  • I see that your filter is on this.wifi_gerrit_commit_messages and you apply the *ngFor on wifi_gerrit_commit_messages?.posts (with posts field). Is this really your intention?

Demo You are missing important part also if u make filter with your original value like that and u will lose your data to another filters because you are changing your original data. If u don't want to use second variable then u can use pipe which only customize on html not in data.

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'search',
})
export class SearchPipe implements PipeTransform {
  transform(value: any[], param: string): any {
    return value.filter(
      (x) =>
        param == null ||
        param == '' ||
        x.commit_id.toLocaleLowerCase().includes(param.toLocaleLowerCase()) ||
        x.gerrit.toLocaleLowerCase().includes(param.toLocaleLowerCase()) ||
        x.Failure_signature.toLocaleLowerCase().includes(
          param.toLocaleLowerCase()
        )
    );
  }
}

and

implement it inside your html like

*ngFor="let cm of wifi_gerrit_commit_messages | search: Failure_signature "

First of all, this is a wrong pattern to call ngOnInit() for providing the initial data frequently. You are calling the Angular life cycle methods instead of using simple functions. Write a method instead and call it whether inside ngOnInit() or somewhere else. In addition, I advise storing initial data to reuse as DataSource. Your search logic is another problem. Please pay attention to the else part inside the Search method. What happens if I enter invalid/no-result characters? You simply lose the original data entries by filtering and assigning them to it again. Unless the user clears the input.

According to your final error, This is a problem with data manipulation not Angular search filter functionality:

TypeError: Cannot read properties of undefined (reading 'toLocaleLowerCase')

It simply indicates that Failure_signature at the execution time is not properly enumerated and is undefined. After making the enhancement listed above, try to design a better data manipulation mechanism with no undefined variables. You can also write test cases to find dark sides in your code.

Try replacing searchTerm undefined check in your Pipe's transform function

transform(wifi_gerrit_commit_messages: any[], searchTerm: string): any {
        return wifi_gerrit_commit_messages.filter(
          (x) =>
            !searchTerm ||
            x.commit_id
              ?.toLocaleLowerCase()
              .includes(searchTerm?.toLocaleLowerCase()) ||
            x.gerrit
              ?.toLocaleLowerCase()
              .includes(searchTerm.toLocaleLowerCase()) ||
            x.Failure_signature?.toLocaleLowerCase().includes(
              searchTerm?.toLocaleLowerCase()
            )
        );
      }
Related