Danniel Rolfe
Danniel Rolfe

Reputation: 83

Reset RXJS interval and hide field when form control value changes Angular 12

Expected behavior:

Reset interval when input value changes. When interval reaches 0 hide element in the dom.

My implementation does not consistently restart the interval. I believe the issue is because the value changes observable only fires off when the input value is a distinct values. I have add a Stackblitz to show the behavior. Any help is greatly appreciated.

View

<input [formControl]="inputFC">

Controller

public inputFC: FormControl = new FormControl('', []);

public counter$ = interval(1000).pipe(
 mapTo(-1), // subtract from timer
 scan((accumulator, current) => {
  return accumulator + current;
 }, 3),
 takeWhile(value => {
  return value >= 0;
 }),
 map(value => {
  this.hide = (value >= 0);
  return value;
 }),
 repeatWhen(() => this.inputFC.valueChanges));

public input$ = this.inputFC.valueChanges.pipe(throttle(() => this.counter$));

StackBlitz example

Upvotes: 1

Views: 254

Answers (1)

martin
martin

Reputation: 96969

There're a couple of things:

  • With this.hide = (value >= 0) hide will always be true because the last value passed by takeWhile is 1. When the counter reaches 0 it will swallow the value and complete the chain.

  • takeWhile accepts an optional argument that tells takeWhile to pass the last value that didn't pass the predicate function. So you can use it like this takeWhile(value => value >= 0, true).

  • throttle() will unsubscribe from this.counter$ after its first emission and then subscribe again when the form value changes. This means that you don't need to use repeatWhen at all and the timer will reset automatically.

  • For sideeffects it's better to use tap() for convinience. When using timers in Angular you might need in some situations trigger change detection manually.

So I'm not sure what exactly you want to do but I guess this could be fairly close and hopefuly you'll get the idea how it works.

public counter$ = interval(1000).pipe(
  mapTo(-1), // subtract from timer
  scan((accumulator, current) => accumulator + current, 3),
  takeWhile(value => value >= 0, true),
  tap(value => this.hide = (value > 0)),
  filter(value => value === 0), // only when the timer reaches `0` we'll disable `throttle`'s timer and it'll unsubscribe.
);

public input$ = this.inputFC
  .valueChanges
  .pipe(
    map(value => value.substr(value.length - 1)),
    throttle(() => this.counter$)
  );

Your updated live demo: https://stackblitz.com/edit/rxjs-interval-form-control-qquuyb?file=src%2Fapp%2Fapp.component.ts

Upvotes: 1

Related Questions