Search code examples
angularrxjsangular-forms

Angular Form RxJS multiple filter on one form input


I have the below array of objects to filter

companies = [
    {
      division: "Company 1",
      departments: ["Department 1", "Department 2", "Department 3"]
    },
    {
      division: "Company 2",
      departments: ["Department 2", "Department 3", "Department 4"]
    },
    {
      division: "Company 3",
      departments: ["Department 3", "Department 4", "Department 5"]
    }
    ...
  ]

I can filter on the division using a value changes subscription on a single form input.

this.myForm.valueChanges.pipe(
    debounceTime(400),
    distinctUntilChanged(),
    tap(val => {
      let searchTerm = val.searchString;
      this.filteredDivisions = this.companies.filter((company) =>
        company.division
          .toLowerCase()
          .indexOf(searchTerm.toLowerCase()) !== -1)

    })
  ).subscribe()

I have been asked to filter on both the division and the array of departments based on the one input. So if the user types Department 1 then only Company 1 would show, but the user could also type Company 1 in the same input box and then only Company 1 would show then as well. The user should be able to type either a division or department to filter the array.

So how is it possible to set up the filters to filter both on departments and divisions at the same time, using the same input?


Solution

  • Well you will just need to widen a bit the fields that will be matched against the given string

    this.myForm.valueChanges
        .pipe(
            debounceTime(400),
            distinctUntilChanged(),
            tap(val => {
                let searchTerm = val.searchString.toLowerCase();
                this.filteredDivisions = this.companies.filter(company => {
                    // filter against the 'division' property
                    const divisionMatch =
                        company.division.toLowerCase().indexOf(searchTerm) !== -1;
    
                    // filter against the 'departments' property
                    const departmentsMatch = () =>
                        company.departments.find(
                            department => department.toLowerCase().indexOf(searchTerm) !== -1,
                        );
    
                    // If divisionMatch is true, no need to check further, return true
                    return divisionMatch || departmentsMatch();
                });
            }),
        )
        .subscribe()
    

    I focus your attention on the fact that the departmentsMatch is a function, as opposed to a simple value, this allows for lazy evaluation. If the division matches then the departmentsMatch will never be invoked.