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 getHeroesCounter
method of the HeroService
return of(this.heroesCounter)
, and of
function 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()
or setTimeout
, ...
The heroService.getHeroes()
also returns of(heroes)
, but the heroes list is an array - a reference type.
More information about of can be found at
have a getHeroesCounter complete!
message in the Console
To resolve your issue, you should use the Subject
in HeroService to store the hero list.