I have an Angular component with a BehaviourSubject initialized to the current month:
textLabel: string;
private monthIndex$: BehaviorSubject<number>;
private MONTHS = [
"Gennaio",
"Febbraio",
"Marzo",
"Aprile",
"Maggio",
"Giugno",
"Luglio",
"Agosto",
"Settembre",
"Ottobre",
"Novembre",
"Dicembre"
];
constructor(private listService: ShoppingListService) {}
ngOnInit() {
this.monthIndex$ = new BehaviorSubject<number>(new Date().getMonth());
// Like this it does not display the label
this.setMonthLabel(this.monthIndex$.value);
this.model$ = this.monthIndex$.pipe(
switchMap((monthValue: number) => {
// this.setMonthLabel(monthValue); // This way it works!
return this.listService.getHistoryChartModel(monthValue);
}),
takeUntil(this.destroy$)
);
}
private setMonthLabel(monthIndex: number) {
this.textLabel = this.MONTHS[monthIndex];
}
setMonth(direction: number) {
let monthIndex = this.monthIndex$.getValue();
monthIndex += direction;
monthIndex =
monthIndex < 0 ? 0 : monthIndex > 11 ? 11 : monthIndex;
this.monthIndex$.next(monthIndex);
this.setMonthLabel(monthIndex);
}
And the template:
<div class="view-sel-container">
<button
color="primary" mat-icon-button
(click)="setMonth(-1)"
[disabled]="monthIndex === 0">
<i class="material-icons">
keyboard_arrow_left
</i>
</button>
<span class="label-text" *ngIf="textLabel">{{ textLabel }}</span>
<button
color="primary" mat-icon-button
(click)="setMonth(1)"
[disabled]="monthIndex === 11">
<i class="material-icons">
keyboard_arrow_right
</i>
</button>
Is it a timing reason why by passing the BehavourSubject value to the method this.setMonthLabel(this.monthIndex$.value)
, the label is not displayed in the template?
UPDATE
The solution provided by Deborah Kurata using get/set instead of BehaviourSubject is the best way to go. I leave the original question/code open as I still do not get why the code does now work by passing the behaviourSubject value as parameter.
Consider going with something like this that does not require a Subject
or BehaviorSubject
:
Component
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
@Component({
templateUrl: './history-chart.component.html'
})
export class HistorChartComponent {
textLabel: string;
model$: Observable<any>;
private _monthIndex: number;
get monthIndex(): number {
return this._monthIndex;
}
set monthIndex(value: number) {
console.log("setter called with: " + value);
// Set the label
this.setMonthLabel(value);
// Get the data
this.getMonthData(value);
this._monthIndex = value;
}
private MONTHS = [
"Gennaio",
"Febbraio",
"Marzo"
];
constructor() { }
ngOnInit() {
// This calls the setter
this.monthIndex = new Date().getMonth();
}
// Increment or decrement the month index
// This calls the setter
setMonth(value: number) {
this.monthIndex += value;
}
private setMonthLabel(monthIndex: number) {
this.textLabel = this.MONTHS[monthIndex];
}
private getMonthData(monthIndex: number): void {
// Commented out because I don't have the service code
//this.model$ = this.listService.getHistoryChartModel(monthIndex);
// Faking out the call to the service
this.model$ = of(
{ id: 1, value: "some data for month : " + this.MONTHS[monthIndex] },
);
}
}
The setter is automatically called each time the user changes the value OR when the value is changed in code. So the setter is a good place to execute any code that needs to respond to the change.
With the code above, the data for the month is retrieved on ngOnInt AND every time the user clicks either of the buttons. If you are not seeing this behavior with the provided stackblitz, please let me know.
Template
<div class="view-sel-container">
<button
color="primary" mat-icon-button
(click)="setMonth(-1)"
[disabled]="monthIndex === 0">
<i class="material-icons">
keyboard_arrow_left
</i>
</button>
<span class="label-text" *ngIf="textLabel">{{ textLabel }}</span>
<button
color="primary" mat-icon-button
(click)="setMonth(1)"
[disabled]="monthIndex === 11">
<i class="material-icons">
keyboard_arrow_right
</i>
</button>
<div>
<span class="label-text" *ngIf="textLabel">
{{textLabel}}
</span>
</div>
<div *ngIf="(model$ | async) as model">
<div>
{{ model.value }}
</div>
</div>
Here is the associated stackblitz: https://stackblitz.com/edit/angular-bpusk2