Search by Multiple Keys in @ng-select Select Filter

In this tutorial, we’ll discuss how to enable smart search in the @ng-select select dropdown filters. Where a user can search within multiple property values on JSON which are separated by spaces. So that all values are there in the filtered values.
In the @ng-select filter, we have a requirement to show only those results which have all of the words( or only a part of it ) typed in the search field. These term keywords can be separated by a space.

For example, in the search bar if we type “asia shann 777” then it filters the following result:
Filter/ Search algorithm of the ng-select component can be configured by using the [searchFn] property. This input property takes in a function to return the term (Typed by user) and items (All items of the available in the data set) of the select.
The custom logic in this function returns true or false for each result in the dataset deciding its visibility in the select options.
In this tutorial, we will only discuss how to override this custom function added in [searchFn] property on the <ng-select/> element directive.
If you are looking for installation steps for @ng-select package and overview of its features, check this previous post.
In the component template, we have the following <ng-select> element with some input property options:
<ng-select 
  [items]="userList" 
  [(ngModel)]="selectedUser"
  bindLabel="search_label"  
  bindValue="id"
  [searchFn]="customSearchFn">

    <ng-template ng-option-tmp let-item="item">
                
                Name: {{item.name}} ({{item.username}}) <br  />
                <small>Contact: {{item.email}},{{item.phone}}</small> <br  />
                <small>Address: {{item.address.street}}, {{item.address.suite}}, {{item.address.city}},
                {{item.address.zipcode}}</small> <br  />
                <small>Site: {{item.website}}</small>
                
    </ng-template>

</ng-select>

In the above HTML template, we have some required properties

[items]: Object of items to show in the select options.

[(ngModel)]: Model can be used to set default items or to fetch the selected values of ID’s or any property which we define in the bindValue.

[searchFn]: As we already discussed, this property is optional and used if we want to override the search algorithm. By default, it ng-select only filter out the property of the object which we define in the bindLabel property.

<ng-template>: The NgTemplate element tag is used to customize the select option look and feel. To show multiple values from the Object, we used ng-template wrapper.

Up to here, there is nothing cool and we are just using a simple implementation of the ng-select in the component.

let’s add the magic function in the component class to customize the filter behavior and make it smarter for a complex dataset with multiple properties.

Using this method a user will be able to filter out the required information.

Quickly update the component class with the following code, after that we will discuss it in more detail.

// app.component.ts
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'ng-select-filter-demo';

  userList = [];
  selectedUser: number;

  constructor(
    private http: HttpClient
  ) { }

  ngOnInit() {
    this.getUsersList();
  }

  // Fetching users data 
  getUsersList() {
    this.http
      .get<any>('https://jsonplaceholder.typicode.com/users')
      .subscribe(response => {
        this.userList = response.map(o => {
          o.search_label =
            ` ${o.id} ${o.name} ${o.username} ${o.email} ${o.phone} ${o.website} ${o.phone} ${o.address.street} ${o.address.suite} ${o.address.city} ${o.address.zipcode} 
          `
          return o
        });
        console.log(this.userList);

      }, error => {
        console.log(error);
      });
  }

  customSearchFn(term: string, item: any) {
    term = term.toLowerCase();

    // Creating and array of space saperated term and removinf the empty values using filter
    let splitTerm = term.split(' ').filter(t => t);

    let isWordThere = [];

    // Pushing True/False if match is found
    splitTerm.forEach(arr_term => {
      let search = item['search_label'].toLowerCase();
      isWordThere.push(search.indexOf(arr_term) != -1);
    });

    const all_words = (this_word) => this_word;
    // Every method will return true if all values are true in isWordThere.
    return isWordThere.every(all_words);
  }
}

In the above code, we have two functions. The getUsersList() is fetching remote results using HTTP’s get method which is are feeding to the userList.

Here we are doing something strange by using the map() function.

Actually we are creating a new property search_label where you can add the properties you want to filter out for an item. So this property will play a major role in our search logic for the customSearchFn function.

Going through each section of function, it returns the term having string types by a user and item containing each record to be shown in the options.

First, we will split the term by spaces and remove every item which is empty using the filter.

let splitTerm = term.split(' ').filter(t => t);

After that, we are using the forEach method to parse each value typed by the user and matching it with the item’ssearch_label property having all values using indexOf.

    splitTerm.forEach(arr_term => {
      let search = item['search_label'].toLowerCase();
      isWordThere.push(search.indexOf(arr_term) != -1);
    });
The isWordThere array tracks for the words which are there in the item’s search_label property.
Then only we return true if all words are there in the item using every method.

Conclusion

The @ng-select package is very popular module to add select boxes with many features like virtual scroll, checkboxes, single multiple selections. In this post, we discussed how to customize filter login so that a user can filter not only from multiple properties but also simultaneously by separating queries with space.

Leave a Comment

Your email address will not be published. Required fields are marked *