Home > Blockchain >  Why doesn't my Angular component value update?
Why doesn't my Angular component value update?

Time:04-14

component1.html :

<div>
    {{nums}}
</div>

TS:

nums: Array<number> = [0, 1, 2, 3]; 

ngOnInit(): void {
    this.numService.getNum().subscribe((res) => {
    this.num = res;
  });
}

component2.html:

<div (click)="addNum()">
  Add
</div>

TS:

addNum(): void {
  this.numService.addNum(6);
}

NumService:

public getNum() {
    return of(this.nums);
  }
  public addNum(num) {
    this.nums.push(num);
  }

Why when i click on addNum, in component1 doesn't work subscribe and value doesn't change. How to do it properly on this example?

CodePudding user response:

You need to push the change to nums to the obersvable using .next()

https://rxjs.dev/guide/observable

CodePudding user response:

The issue is that when you call this.numService.getNum() in ngOnInit the subscribe method is only called once ngOnInit is executed. That happens because NumService.getNum() returns an observable created with the of() operator. Such an observable emits only once and then completes. So you get only initial value of NumService.nums.

Solution would be to turn numService.nums into a BehaviorSubject and subscribe to it.

class NumService {
  private numsSubject$: BehaviorSubject<number[]> = new Subject<number[]>([]);

  nums$: Observable<number[]> = this.numsSubject$.asObservable();

  addNum(num: number): void {
    const nums = this.numsSubject$.getValue();
    this.numsSubject$.next([...nums, num]);
  }
}


class Component1 {
  nums: Array<number> = [0, 1, 2, 3]; 

  ngOnInit(): void {
    this.numService.nums$.subscribe((res) => {
      this.num = res;
    });
  }

  addNum(): void {
    this.numService.addNum(6);
  }
}

CodePudding user response:

you are facing two problems:

  1. Every time you call the service function numService.getNum() you are creating a new observable.
  2. The function of creates a special observable which only emits its value once.

Instead you should have your service maintain a single observable, preferably a ReplaySubject (so it retains the previous value for subscriptions)

something like this should work, in your service:

  nums$ = new ReplaySubject<int[]>([]);
  nums = []

  public addNum(num) {
    this.nums.push(num);
    this.nums$.next(this.nums)
  }

then from your component, you should not subscribe (to avoid the usual observable memory leak), but expose the service's observable:

  numsAtComponent$ = this.numService.nums$

And finally from your template, you subscribe to the component variable and show its value like this:

<div>
 {{ numsAtComponent$ | async }}
</div>
  • Related