Home > Enterprise >  Angular: Combining interval with increment & decrementss
Angular: Combining interval with increment & decrementss

Time:06-04

I have a random number between 1 and 20 at intervals. I need to add buttons to move up and down. (increment & decrement). I could not combine the intervals I have with my functions

my stackblitz: https://stackblitz.com/edit/angular-ivy-zbrapb?file=src/app/app.component.ts,src/app/app.component.html,src/app/app.component.css

The function that does a random number between 1 and 20:

  floor!: Observable<Number>;
  floorNumber = -1;

  ngOnInit(): void {
    this.floor = this.getNumbersInfinite();
  }

  getNumbersInfinite() {
    return interval(1000)
    .pipe(
      map(() => Math.floor(Math.random() * 20)   1),
      tap(res =>this.floorNumber=res));
  }

Unsuccessful implement of up and down:

 up(){
    if (Number(this.floorNumber) < 20 && Number(this.floorNumber) >= 1) {
       return Number(this.floorNumber  );
    }
    return false;
  }
  down(){
    if (Number(this.floorNumber) < 20 && Number(this.floorNumber) >= 1) {
       return Number(this.floorNumber--);
    }
    return false;
  }

html:

<p>
  {{ floor | async }}
</p>

<input type="button" value="Up" (click)="up()" />
<input type="button" value="down" (click)="down()" />

CodePudding user response:

This should do the trick

export class AppComponent implements OnInit {
  floor = new ReplaySubject<number>(1);

  ngOnInit(): void {
    this.getNumbersInfinite();
  }

  getNumbersInfinite() {
    return interval(1000)
      .pipe(map(() => Math.floor(Math.random() * 20)   1))
      .subscribe(this.floor);
  }

  up() {
    this.floor
      .pipe(
        take(1),
        filter((v) => v < 20),
        map((v) => v   1)
      )
      .subscribe((v) => this.floor.next(v));
  }

  down() {
    this.floor
      .pipe(
        take(1),
        filter((v) => v > 1),
        map((v) => v - 1)
      )
      .subscribe((v) => this.floor.next(v));
  }
}

https://stackblitz.com/edit/angular-ivy-dxu7xx?file=src/app/app.component.ts,src/app/app.component.html,src/app/app.component.css

CodePudding user response:

You can use the merge() function to merge the three actions into one observable. Everytime an event happens from one of the observables the value will get sent to the template.

See a working example here.

The new template is pretty much the same, we just now remove the click event and create it within the TypeScript using fromEvent().

<p>
  {{ output$ | async }}
</p>

<input type="button" value="Up" #up />
<input type="button" value="down" #down />

The TypeScript will:

  • Create the up event
  • Create the down event
  • Create the interval
  • listen to all three and emit a single observable
export class AppComponent implements AfterViewInit {
  // The current value
  current = 1;

  // Create an Observable from the click event of the up button
  up$!: Observable<number>;
  @ViewChild('up')
  set up(val: ElementRef<HTMLButtonElement>) {
    this.up$ = fromEvent(val.nativeElement, 'click').pipe(
      filter(() => this.current < 20),
      map(() =>   this.current)
    );
  }

  // Create an Observable from the click event of the down button
  down$!: Observable<number>;
  @ViewChild('down')
  set down(val: ElementRef<HTMLButtonElement>) {
    this.down$ = fromEvent(val.nativeElement, 'click').pipe(
      filter(() => this.current > 0),
      map(() => --this.current)
    );
  }

  // Create a timer observable that fires every "x" milliseconds
  interval$ = () =>
    timer(0, 1000).pipe(
      map(() => Math.floor(Math.random() * 20)   1),
      tap((i) => (this.current = i))
    );

  output$?: Observable<number>;

  // We need to merge here so the buttons are ready.
  // Merging sooner will cause an `undefined` error for the buttons
  ngAfterViewInit() {
    // Merge the three observables and send the results to the template
    this.output$ = merge(this.up$, this.down$, this.interval$());
  }
}
  • Related