1
votes

How do I search / filter observable of type string array ?

For example , I have following observable

names$ = Observable.of(['Lenovo','Dell','Toshiba','Apple','Microsoft']);

Now I would like to filter this observable based on what user types in the input textbox.

So I have following code & I would like to return filtered observable based on user's searchTerm typed in the input box.

Please note that I am looking for client side solution. I have the data already on the clientside & for some reason I cannot send search term on server to filter the data. I also understand that in this example I can directly use filter on array itself but I want to do this through observable.

I also tried flatmap operator to flatten the array but still was not able to return the observable in the end which should be of type string array.

Any help will be greatly appreciated. Thanks in advance.

App.component.html

<!-- Textbox to receive user input -->
Search:<input type='text' [(ngModel)]='searchTerm' (keypress)='onkeypress($event.target.value)'>
<p>Search Term: {{searchTerm}}</p>
<hr>

<!-- Show search results here as ordered list -->
<ng-container *ngIf='(names$|async)?.length>0'>
    <ol>
        <li *ngFor='let name of names$|async'>
            {{name}}
        </li>
    </ol>
</ng-container>

App.component.ts

import {Component, OnInit} from '@angular/core';
import {Observable} from 'rxjs';

@Component({
    selector: 'my-app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
    names$: Observable<string[]>;
    filteredNames$: Observable<string[]>;
    searchTerm: string;

    ngOnInit() {
        this.names$ = Observable.of(['Lenovo', 'Dell', 'Toshiba', 'Apple', 'Microsoft']);
    }

    // HOW TO IMPLEMENT THIS FUNCTION ??
    onkeypress(value) {
        console.log(value);
        this.names$ = this.names$.map(names => names.indexOf(value) > 0)
        // .filter(x=>{console.log(x.indexOf(value));return x.indexOf(value)>0})
        //     .subscribe(
        // (data)=>console.log(data),      (error)=>console.log('Error'+error),
        //       ()=>console.log('complete'));
    }
}
2
This is a small thing but you're listening for a keypress event and calling the function keyup. I'm not sure if you have the behavior you want here. - Brendan Whiting
@Brendan Whiting Thanks for the pointing out the mistake. I fixed it. - hp8888

2 Answers

1
votes

There are a few mistakes that you make here.

  1. Why do you want to map the observable to names.indexOf(value) > 0? .map() literally transforms the observable, and you have just casted an observable of type string to type boolean.

  2. If you want the list of names to change when the user are keying (onkeyup), why do you want to reassign the this.names$ back to this.names$ again? That will make your code work once aka work on the first key stroke only. You should have two variables, one for holding the values, the other to be bind to your ngModel.

  3. If you want to use async pipe, there is no need for you to subscribe the observable in your typescript file.

In your ngOnInit(), create a variable to keep track of your names:

ngOnInit() {
    this.data$ = Observable.of(['Lenovo', 'Dell', 'Toshiba', 'Apple', 'Microsoft']);
    this.names$ = this.data$;
}

Assuming you are using the async pipe, this should be your onKeyUp function:

onKeyUp(value) {
    this.names$ = this.data$
        .map(x => {
             return x.filter(y=>y.toLowerCase().indexOf(value.toLowerCase())>-1);
        })
}

Working Stackblits: https://stackblitz.com/edit/angular-yeaer6

1
votes

try this

let value='Del'
 Rx.Observable.from(['Lenovo','Dell','Toshiba','Apple','Microsoft'])
 .filter(name=>name.indexOf(value)!==-1)
 .subscribe(console.log);