How to deal with Observables in rxjs and angular 5 , problem with bootstrap typeahead implementation

Viewed 617

I'm trying to implement autocomplete (bootstrap typeahead) in angular 5.

After a few code refactoring, I got this as my best result so far. but after emit new value (type new term in input) it made this error...

TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
at Object.subscribeToResult (subscribeToResult.js:74)
at SwitchMapSubscriber.push../node_modules/rxjs/operators/switchMap.js.SwitchMapSubscriber._innerSub (switchMap.js:103)
at SwitchMapSubscriber.push../node_modules/rxjs/operators/switchMap.js.SwitchMapSubscriber._next (switchMap.js:96)
at SwitchMapSubscriber.push../node_modules/rxjs/Subscriber.js.Subscriber.next (Subscriber.js:90)
at DistinctUntilChangedSubscriber.push../node_modules/rxjs/operators/distinctUntilChanged.js.DistinctUntilChangedSubscriber._next (distinctUntilChanged.js:103)
at DistinctUntilChangedSubscriber.push../node_modules/rxjs/Subscriber.js.Subscriber.next (Subscriber.js:90)
at DebounceTimeSubscriber.push../node_modules/rxjs/operators/debounceTime.js.DebounceTimeSubscriber.debouncedNext (debounceTime.js:98)
at AsyncAction.dispatchNext (debounceTime.js:114)
at AsyncAction.push../node_modules/rxjs/scheduler/AsyncAction.js.AsyncAction._execute (AsyncAction.js:111)
at AsyncAction.push../node_modules/rxjs/scheduler/AsyncAction.js.AsyncAction.execute (AsyncAction.js:86)

and here is my code:

private searchTerm = new Subject<string>();
public termsArray: any = [];
constructor(private apiService: DataService){
       const result$ = this.searchTerm
       .debounceTime(100)
       .distinctUntilChanged()
       .switchMap(term => this.apiService.getSearchTest(term))
           .subscribe(result => {
                      this.termsArray = result.suggestions as Array<any>;
                      console.log(this.termsArray);
                      }
                      , err => console.log(err))
}

autocompleteTest = (text$: Observable<string>) =>
    text$.pipe(
        debounceTime(300),
        distinctUntilChanged(),
        map(term => term.length < 1 ? []
            : this.termsArray
                .filter(v => v.toLowerCase().indexOf(term.toLowerCase()) > -1)
                .slice(0, 10))
        )

onKeyUpSearch(searchText: string){
    if (searchText !== "") this.searchTerm.next(searchText);
}
1 Answers

Normally you has two things

private searchTerm = new Subject<string>();
observableSearchTerm=this.searchTerm.asObservable()

You make the "next" over searchTerm, but subscribe from observable

onKeyUpSearch(searchText: string){
    if (searchText !== "") this.searchTerm.next(searchText);
}
//But subscribe to the "Observable" 
 this.observableSearchTerm
   .debounceTime(100)
   .distinctUntilChanged()
   .switchMap(term => this.apiService.getSearchTest(term))
       .subscribe(result => {
          ...
   }

NOTE 1: In RxJs 6 (Angular 6), you must change the code using pipe

 this.observableSearchTerm.pipe(
   debounceTime(100),
   distinctUntilChanged(),
   switchMap(term => this.apiService.getSearchTest(term))
 ).subscribe(result => {
          ...
 }

NOTE2: You can use a FromControl and subscribe directy to values changes

//In .html
<input type="text" [formControl]="control">

//In .ts
control:FormControl=new FormControl()  //Define the control
//..after in ngOnInit
ngOnInit()
{
   this.control.valueChanges.pipe(
      debounceTime(100),
      distinctUntilChanged(),
      switchMap(term => this.apiService.getSearchTest(term)
   ).subscribe(result=>{
     ....
   })
 }

See stackblitz

Related