Home > Net >  Updation to subject is not happening in Rxjs
Updation to subject is not happening in Rxjs

Time:12-14

I'm trying to update an RxJS Subject via a component's method . But it is not happening properly .

cricketData$ = new Subject<string>();

ngOnChanges() {
  this.cricketData$
    .asObservable()
    .pipe(
      tap(() =>
        console.log(
          'Triggering REST call for cricket tab with the year ',
          this.year
        )
      ),
      take(1)
    )
    .subscribe();
  this.cricketData$.next('sachin');
}

updateCricketData() {
   this.cricketData$.next('dravid');
}

If I remove the take operator , subject is updated . But multiple service calls are triggered while selecting the values from dropdown . Is there any other operator / approach in RxJS using which I can achieve the below scenarios?

  1. service calls are triggered only once while selecting the values from dropdown
  2. subject is also updated via component's method

Please refer the below StackBlitz link . Thanks in advance .

Editor URL Stackblitz: EditorURL

App URL Stackblitz: AppURL

CodePudding user response:

There are two issues.
First: ngOnChanges triggers multiple times, and every time you change the value you are adding a new pipe working parally. take(1) makes your previous pipe spot and only the recent pipe tab is triggered.

ngOnInit(): void { // runs once
    this.cricketData$
      .asObservable()
      .pipe( // runs on every next
        tap(() =>
          console.log(
            'Triggering REST call for cricket tab with the year ',
            this.year
          )
        )
      )
      .subscribe();
  }

  ngOnChanges() {
    this.cricketData$.next('sachin'); // runs on every event on the page
  }

Second issue: Do you really want to trigger this function on ngChange when every there a update on the page rather than just the dropdown, if the answer is only when there is a change in the dropdown then you can do this. you have to use a combination of Input set year(value: string)

example

    export class CricketComponent implements OnChanges {
  private _selectedYear: string;

  @Input() set year(value: string) {
    this._selectedYear = value;
    this.cricketData$.next('sachin'); // will trigger with every drop down change
  }

  cricketData$ = new Subject<string>();

  constructor() {}

  ngOnInit(): void {
    this.cricketData$
      .asObservable()
      .pipe(
        tap((value) =>
          console.log(
            'Triggering REST call for cricket tab with the year ',
            this._selectedYear,
            value
          )
        )
      )
      .subscribe();
  }

  ngOnChanges() {}

  updateCricketData() {
    this.cricketData$.next('dravid');
  }
}

CodePudding user response:

How about using a formcontrol for the year selector, we can listen to the changes of the formcontrol and the subject you have created with combineLatest, which will trigger when either of them emit (as long as they have emitted at least once). Don't know your exact case of how/when your subject is updated initially, so I just added a startWith(null) value. Apply it to your usecase...

So the formcontrol for the parent:

year = new FormControl(2019);

and template where we mark the form control as well as pass the formcontrol to the child:

<select [formControl]="year">
  <option value="2019">2019</option>
  <option value="2020">2020</option>
  <option value="2021">2021</option>
</select>

<app-cricket [year]="year"></app-cricket>

Then the child listens to this in OnInit together with the Subject:

ngOnInit() {
  combineLatest([
    this.year.valueChanges.pipe(startWith(this.year.value)),
    this.cricketData$.asObservable().pipe(startWith(null)),
  ]).pipe(
    switchMap(values => {
      return REST API request...
    })
  ).subscribe(result => console.log(result))
}

Using startWith to make them trigger initially. Remember to unsubscribe!!

STACKBLITZ (without the switchMap)

  • Related