I'm figuring out how Angular Services work, but I don't understand why the heroCounter (number) doesn't update correctly while the hero array does. For instance, as I add a new dummy hero I expect the heroCounter to increment.
Thank you in advance if you can help me.
hero.services.ts
import { Injectable } from '@angular/core';
import { Hero } from '../model/hero.model';
import { HEROES } from '../constants/hero.constant';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class HeroService {
heroes!: Hero[];
heroesCounter!:number;
constructor() {
this.heroes = HEROES;
this.heroesCounter = this.heroes.length;
}
getHeroes(): Observable<Hero[]> {
return of(this.heroes);
}
getHeroesCounter(): Observable<number> {
return of(this.heroesCounter);
}
mockAddHero(name: string): void {
setTimeout(() => {
this.heroes.push(new Hero(name));
this.heroesCounter = this.heroes.length;
console.log(this.heroesCounter);
}, 1500);
}
}
app.component.ts
import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
import { HeroService } from '../services/hero.service';
import { Hero } from '../model/hero.model';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css',
})
export class AppComponent implements OnInit {
heroes!: Hero[];
heroesCounter!: number;
constructor(private heroService: HeroService) {}
getHeroes(): void {
this.heroService.getHeroes().subscribe((heroes) => (this.heroes = heroes));
}
getHeroesCounter(): void {
this.heroService
.getHeroesCounter()
.subscribe((counter) => {this.heroesCounter = counter});
}
ngOnInit(): void {
this.getHeroes();
this.getHeroesCounter();
}
addHero(): void {
this.heroService.mockedAddHero('new hero');
}
}
app.component.html
<div>
<button (click)="addHero()" >Add Dummy Hero</button>
<ul>
<!-- This works -->
@for(hero of heroes; track $index){
<li>{{hero.name}}</li>
}
</ul>
<!-- This does not work -->
<p> Total of heroes: {{this.heroesCounter}}</p>
</div>
Because the
getHeroesCountermethod of theHeroServicereturnof(this.heroesCounter), andoffunction will emit a variable amount of values in a sequence and then emit a complete notification.As a result, when you add a new hero to the list, the subscribe method will not be triggered, even though you trigger the change detection yourself with the
ChangeDetectorRef.detectChanges()orsetTimeout, ...The
heroService.getHeroes()also returnsof(heroes), but the heroes list is an array - a reference type.More information about of can be found at
Here is a demo Stackblitz
have a
getHeroesCounter complete!message in the ConsoleTo resolve your issue, you should use the
Subjectin HeroService to store the hero list.