6. Adding a Simple Store
🔀 Branch: For this lab, please switch to the branch 07-state-service-starter:
git reset --hard
git checkout 07-state-service-starter
In this lab, you add a Service acting as a simple store that supports unidirectional dataflow.
-
Open the file
dessert.store.ts(src/app/data/dessert.store.ts) that was added to this branch. It contains a simple store. Have a look at it and find out which properties and methods it provides. -
Switch to the file
desserts.component.ts(src/app/desserts/desserts.component.ts), inject the store, and use it for providing the needed properties and methods:import { JsonPipe } from '@angular/common'; -import { - ChangeDetectionStrategy, - Component, - computed, - inject, - signal, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { Dessert } from '../data/dessert'; import { DessertFilter } from '../data/dessert-filter'; -import { DessertService } from '../data/dessert.service'; -import { DessertIdToRatingMap, RatingService } from '../data/rating.service'; +import { DessertStore } from '../data/dessert.store'; import { DessertCardComponent } from '../dessert-card/dessert-card.component'; -import { ToastService } from '../shared/toast'; +import { FormUpdateDirective } from '../shared/form-update.directive'; @Component({ selector: 'app-desserts', standalone: true, - imports: [DessertCardComponent, FormsModule, JsonPipe], + imports: [DessertCardComponent, FormsModule, JsonPipe, FormUpdateDirective], templateUrl: './desserts.component.html', styleUrl: './desserts.component.css', changeDetection: ChangeDetectionStrategy.OnPush, }) export class DessertsComponent { - #dessertService = inject(DessertService); - #ratingService = inject(RatingService); - #toastService = inject(ToastService); + #store = inject(DessertStore); - originalName = signal(''); - englishName = signal(''); - loading = signal(false); + originalName = this.#store.originalName; + englishName = this.#store.englishName; + loading = this.#store.loading; - desserts = signal<Dessert[]>([]); - ratings = signal<DessertIdToRatingMap>({}); - ratedDesserts = computed(() => this.toRated(this.desserts(), this.ratings())); + ratedDesserts = this.#store.ratedDesserts; constructor() { - this.search(); + this.#store.loadDesserts(); } search(): void { - const filter: DessertFilter = { - originalName: this.originalName(), - englishName: this.englishName(), - }; - - this.loading.set(true); - - this.#dessertService.find(filter).subscribe({ - next: (desserts) => { - this.desserts.set(desserts); - this.loading.set(false); - }, - error: (error) => { - this.loading.set(false); - this.#toastService.show('Error loading desserts!'); - console.error(error); - }, - }); - } - - toRated(desserts: Dessert[], ratings: DessertIdToRatingMap): Dessert[] { - return desserts.map((d) => - ratings[d.id] ? { ...d, rating: ratings[d.id] } : d, - ); + this.#store.loadDesserts(); } loadRatings(): void { - this.loading.set(true); - - this.#ratingService.loadExpertRatings().subscribe({ - next: (ratings) => { - this.ratings.set(ratings); - this.loading.set(false); - }, - error: (error) => { - this.#toastService.show('Error loading ratings!'); - console.error(error); - this.loading.set(false); - }, - }); + this.#store.loadRatings(); } updateRating(id: number, rating: number): void { - this.ratings.update((ratings) => ({ - ...ratings, - [id]: rating, - })); + this.#store.updateRating(id, rating); + } + + updateFilter(filter: DessertFilter): void { + this.#store.updateFilter(filter); } } -
Open the file
desserts.component.html(src/app/desserts/desserts.component.html) and switch from two way bindings withngModelto property bindings:-<form> +<form (appUpdate)="updateFilter($event)"> <div class="card-grid"> <div> <label for="name">Original Name</label> - <input [(ngModel)]="originalName" name="originalName" /> + <input [ngModel]="originalName()" name="originalName" type="text" /> </div> <div> <label for="englishName">English Name</label> - <input [(ngModel)]="englishName" name="englishName" /> + <input [ngModel]="englishName()" name="englishName" /> </div> </div> [...]Hint: Use the
FormUpdateDirectiveincluded in this branch to react to form changes.
💾 Please find this lab's solution in the branch 07a-state-service (get reset --hard && git checkout 07a-state-service). You don't have to switch to it branch, if you are fine.
> Follow us on X (Twitter) > More Trainings (German) > More Trainings (English)