0

At https://www.learnrxjs.io/recipes/smartcounter.html I found a great example of an numbercounter in Angular 2+, the code looks likes:

@Component({
  selector: 'number-tracker',
  template: `
    <h3> {{ currentNumber }}</h3>
  `
})
export class NumberTrackerComponent implements OnDestroy {
  @Input()
  set end(endRange: number) {
    this._counterSub$.next(endRange);
  }
  public currentNumber = 0;
  private _counterSub$ = new Subject();
  private _subscription : Subscription;

  constructor() {
    this._subscription = this._counterSub$
      .switchMap(endRange => {
        return timer(0, 20)
            .mapTo(this.positiveOrNegative(endRange, this.currentNumber))
            .startWith(this.currentNumber)
            .scan((acc, curr) => acc + curr)
            // .delayWhen(i => {
            //   easing here
            // })
            .takeWhile(this.takeUntilFunc(endRange, this.currentNumber));
      })
      .subscribe(val => this.currentNumber = val);
  }

  private positiveOrNegative(endRange, currentNumber) {
    return endRange > currentNumber ? 1 : -1;
  }

  private takeUntilFunc(endRange, currentNumber) {
    return endRange > currentNumber
      ? val => val <= endRange
      : val => val >= endRange;
  }

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

Now this works good, but I want to pass a variable currentNumber instead of the this.currentNumber default 0.

Until now I've come so far:

  public currentNumber: number;
...    
  @Input()
  set fanCountPrev(startRange: number) {
    this.currentNumber = startRange
    console.log('startRange: ', this.currentNumber)
  }

...

But when a new value arrives, there this.currentNumber is not set to the previous version.

Do you have any tips or example to accomplish this?

Frederik Struck-Schøning
  • 12,981
  • 8
  • 59
  • 68
Johan Walhout
  • 1,446
  • 3
  • 22
  • 38

1 Answers1

0

The problem here is that the subscription is made on the constructor, while the Input() binding are resolved on the ngOnInit lifehook. Also, in this case there is not a good practise to modify manually on runtime the this.currentNumber variable, so is an internal state of the observable calculation. The only think I seee posible is to modify it onInit, for this, and as I said, you must create the subscription in the ngOnInit lifehook function like so:

@Component({
  selector: 'number-tracker',
  template: `
    <h3> {{ currentNumber }}</h3>
  `
})
export class NumberTrackerComponent implements OnDestroy {
  @Input()
  set end(endRange: number) {
    this._counterSub$.next(endRange);
  }
  @Input()
  set fanCountPrev(startRange: number) {
    this.currentNumber = startRange
    console.log('startRange: ', this.currentNumber)
  }
  private currentNumber = 0;
  private _counterSub$ = new Subject();
  private _subscription : Subscription;

  constructor() {
    
  }
  
  ngOnInit() {
    this._subscription = this._counterSub$
      .switchMap(endRange => {
        return timer(0, 20)
            .mapTo(this.positiveOrNegative(endRange, this.currentNumber))
            .startWith(this.currentNumber)
            .scan((acc, curr) => acc + curr)
            // .delayWhen(i => {
            //   easing here
            // })
            .takeWhile(this.takeUntilFunc(endRange, this.currentNumber));
      })
      .subscribe(val => this.currentNumber = val);
  }

  private positiveOrNegative(endRange, currentNumber) {
    return endRange > currentNumber ? 1 : -1;
  }

  private takeUntilFunc(endRange, currentNumber) {
    return endRange > currentNumber
      ? val => val <= endRange
      : val => val >= endRange;
  }

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

But remember, this solution will work only for the initilitzation. If you change the variable during the existence of this component, this wouldn't work. But as I said, it is not a good practise in this case because is an intern calculated state of the component, it only make sense on the initialitzation.

Hope this helps.