I learning Angular and I have first problem. My service look like this
import { Injectable } from '@angular/core';
import { COSTInterface } from './COSTSInterface'
import {Costs} from './mock-cost'
import { Observable, of } from 'rxjs'
@Injectable({
providedIn: 'root'
})
export class CostService {
getCosts(): Observable<COSTInterface[]> {
return of(Costs);
}
getBalance(): Observable<number> {
let balance = 0;
this.getCosts().subscribe(costs => {
for (const cost of costs) {
if (cost.type === 'income') {
balance += cost.cost;
} else {
balance -= cost.cost;
}
}
});
return of(balance)
}
add(element: COSTInterface): void {
Costs.push(element)
}
getCount(): Observable<number>{
return of(Costs.length);
}
constructor() { }
}
OK. I can add new element to the array
profileForm = this.fb.group({
id: [0],
name: [''],
type: [''],
cost: [0]
})
count:number = 0;
getCount() {
this.costService.getCount().subscribe(count => {
this.count = count + 1;
})
}
onSubmit() {
const newCost: COSTInterface = {
id: this.count || 0, // Jeśli wartość jest null lub undefined, ustawiamy 0 jako domyślną wartość
name: this.profileForm?.value.name || '',
type: this.profileForm?.value.type || '',
cost: this.profileForm?.value.cost || 0
};
this.costService.add(newCost)
}
constructor(private fb: FormBuilder, private costService: CostService){}
ngOnInit(){
this.getCount();
}
But the balance is not calculated automatically when a new element is added.
Summary Component when I have the balance is very simple and looks like this
balance:number = 0;
getBalance(): void {
this.costService.getBalance().subscribe(balance => {
this.balance = balance;
});
}
constructor(private costService: CostService){}
ngOnInit() {
this.getBalance();
}
can guess where the problem is, but I have no idea how to solve it. Propably service’s method getBalance don’t know that we change array of costs but I don’t know how solve this problem.
How can I display new total cost after I add new element?
>Solution :
Your CostService.getBalance is not handling asynchronous events properly. Rewrite it this way:
getBalance(): Observable<number> {
return this.getCosts()
.pipe(map(costs => {
let balance = 0;
for (const cost of costs) {
if (cost.type === 'income') {
balance += cost.cost;
} else {
balance -= cost.cost;
}
}
return balance;
}));
}
It makes the "getBalance" return the correct balance amount. But it wouldn’t retrigger when a new cost is added. For this, you need BehaviorSubjects:
private balance$: BehaviorSubject<number> = new BehaviorSubject(0); // 0 is default value)
constructor(/*someStuff*/) {
// init balance
this.updateBalance();
}
getBalance(): Observable<number> {
return this.balance$.asObservable();
}
private updateBalance() {
return this.getCosts()
.subscribe(costs => {
let balance = 0;
for (const cost of costs) {
if (cost.type === 'income') {
balance += cost.cost;
} else {
balance -= cost.cost;
}
}
// triggers the event to all subscribers
this.balance$.next(balance);
}));
add(element: COSTInterface): void {
Costs.push(element);
this.updateBalance();
}