0

im struggling with subscribing to select dropdown change in Angular 4. The change of my searchType variable is only visible after calling a test() function through click event but ofc I need Angular to subscribe to changes instantly.

Sidenote:

  • I'm subscribing in the filter pipe.
  • Service is provided in the module so working on the same instance

App.component.html

      <select  name="searchType" id="searchType" #searchType>
        <option value="movie_title"> movie title</option>
        <option value="director">director</option>
        <option value="actor"> actor</option>
      </select>

App.component.ts

  @ViewChild('searchType') select: ElementRef;
  searchType: number;

constructor(private service: ServiceService) {}

  ngOnInit() {
    this.searchType = this.select.nativeElement.options.selectedIndex;
    this.service.sendSearchType(this.searchType);
  }

  test() {
  this.searchType = this.select.nativeElement.options.selectedIndex;
  this.service.sendSearchType(this.searchType);
  }

service.service.ts

    searchType = new Subject<number>();

 sendSearchType(id: number) {
     this.searchType.next(id);
 }

 getSearchType(): Observable<number> {
     return this.searchType.asObservable();
 }

and finally filter.pipe.ts subscribing to the change

searchType: number;
private subscription: Subscription;

constructor(private service: ServiceService) {
    this.service.getSearchType().subscribe(
        (id) => (this.searchType = id)
    );
}

    transform(value: any[], filter: string): any[] {
        filter = filter ? filter.toLocaleLowerCase() : null;
        return filter ? value.filter(
                (product) =>
                    this.auxiliaryFunction(product, filter)
            ) : value;
    }

    auxiliaryFunction(product, filter) {
        if (this.searchType === 2) {
        return (product.actor.toLocaleLowerCase().indexOf(filter) !== -1)
        } else if (this.searchType === 1) {
        return (product.director.toLocaleLowerCase().indexOf(filter) !== -1)
        }  else {
        return (product.movie.toLocaleLowerCase().indexOf(filter) !== -1)
      }
    }

    ngOnDestroy() {
        this.subscription.unsubscribe();
    }

Where did i go wrong ? Will be grateful for any solutions.

  • Pipes are meant for pure functions, you should not use a pipe for this case, instead put the filter logic in the service, create searchType subject in your service and subscribe to it ` – Murhaf Sousli Feb 16 '18 at 20:58
  • you're right, i implemented your remark regarding putting logic in the service but i dont quite get the second part - the code (sorry about that, im a beginner;) . what should i change to follow best practice conventions. I put my already working code under [link]https://stackblitz.com/edit/ng-search-bar-enhanced?file=app%2Finput-filter.pipe.ts could you please have a look and recommend if anything else can be optimised ? – Bartłomiej Blue Feb 17 '18 at 18:14
  • update: unfortunately if i try to outsource the logic into service the 'search by' filter stos to work. the problem is that i'd have to pass the value searchType value through subject again - is it even possible using the same subject or would i have to create anothe subject for that ? – Bartłomiej Blue Feb 17 '18 at 18:33

2 Answers2

0

You have to watch for changes on select element. I made this live example, I hope it helps. (app.component.ts)

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import { AfterViewInit } from '@angular/core';

...

ngAfterViewInit() {
    const $selector = Observable.fromEvent(this.select.nativeElement, 'change');
    $selector.subscribe(() => {
        this.searchType = this.select.nativeElement.options.selectedIndex;
        this.service.sendSearchType(this.searchType);
    });
}

(filter.pipe.ts)

constructor(private service: AppService) {
    this.service.getSearchType().subscribe(
        (id) => { 
            console.log(`${id}px`);
            return (this.searchType = id);
        }
    );
}
Luillyfe
  • 6,183
  • 8
  • 36
  • 46
  • thank you so much Luuillyfe, this is working as expected now:)) so basically, do i always have to subscribe to a subject from service in a component that emits the data through observable ? is it the only way to get the data streams instaniously to another component through rxjs ? – Bartłomiej Blue Feb 17 '18 at 17:45
  • Not it is not, you can use event binding, check my new answer ! – Luillyfe Feb 17 '18 at 18:57
0

Also you could use event binding (live exmaple), but I really prefer the reactive approached. (app.component.html)

<select  name="searchType" id="searchType" (change)="onSelect()" #searchType>
    <option value="movie_title">movie title</option>
    <option value="director">director</option>
    <option value="actor"> actor</option>
</select>

(app.component.ts)

onSelect() {
      this.searchType = this.select.nativeElement.options.selectedIndex;
      this.service.sendSearchType(this.searchType);
  }
Luillyfe
  • 6,183
  • 8
  • 36
  • 46