2. Component Communication With Signals
🔀 Branch: For this lab, please switch to the branch 02-sub-comp-starter:
git reset --hard
git checkout 02-sub-comp-starter
2.1 Using Input Signals for Property Bindings
Now, let's use Signal-based inputs in our DessertCardComponent!
-
Open the file
dessert-card.component.ts(src/app/dessert-card/dessert-card.component.ts) and replace the traditional@Inputwith a requiredInputSignalusing the functioninput.required:-import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, input, output } from '@angular/core'; import { Dessert } from '../data/dessert'; import { RatingComponent } from '../rating/rating.component'; [...] styleUrl: './dessert-card.component.css', }) export class DessertCardComponent { - @Input({ required: true }) - dessert!: Dessert; + dessert = input.required<Dessert>(); - @Output() - ratingChange = new EventEmitter<number>(); + ratingChange = output<number>(); updateRating(newRating: number): void { - this.dessert.rating = newRating; this.ratingChange.emit(newRating); } } -
Switch to the file
dessert-card.component.html(src/app/dessert-card/dessert-card.component.html) and call the introducedInputSignals's getter:<div class="card"> - <h2 class="card-title">{{ dessert.originalName }}</h2> - <i class="card-subtitle">{{ dessert.englishName }}</i> + <h2 class="card-title">{{ dessert().originalName }}</h2> + <i class="card-subtitle">{{ dessert().englishName }}</i> <img class="w-full mt-5" - src="/assets/{{ dessert.image }}" + src="/assets/{{ dessert().image }}" alt="Bildbeschreibung" /> <div class="card-body mb-0"> - <span class="card-text">{{ dessert.description }}</span> + <span class="card-text">{{ dessert().description }}</span> </div> <div class="card-footer"> - <div class="card-text mt-0 mb-5">{{ dessert.kcal }} kcal</div> + <div class="card-text mt-0 mb-5">{{ dessert().kcal }} kcal</div> <app-rating - [rating]="dessert.rating" + [rating]="dessert().rating" (ratingChange)="updateRating($event)" ></app-rating> </div>
💾 Please find this lab's solution in the branch 02a-input-signals (get reset --hard && git checkout 02a-input-signals). You don't have to switch to it branch, if you are fine.
2.2 Using Model Signals for Two Way Bindings
Our RatingComponent provides a Two Way Binding. Let's also switch to Signals here!
-
Open the file
rating.component.ts(src/app/rating/rating.component.ts) and replace the@Input/@Outputcombination ofrating/ratingChangewith a soleModelInput. For this, you can use themodelfunction:-import { - Component, - EventEmitter, - Input, - OnChanges, - Output, -} from '@angular/core'; +import { Component, computed, model, signal } from '@angular/core'; const maxRatingInCheatMode = 500; [...] templateUrl: './rating.component.html', styleUrl: './rating.component.css', }) -export class RatingComponent implements OnChanges { - @Input({ required: true }) - rating = 0; +export class RatingComponent { + rating = model.required<number>(); - @Output() - ratingChange = new EventEmitter<number>(); + cheatMode = signal(false); - maxRating = 5; - stars: Array<boolean> = []; - - ngOnChanges(): void { - if (this.rating > this.maxRating) { - this.maxRating = maxRatingInCheatMode; - } - this.#updateStars(); - } - - #updateStars() { - this.stars = this.toStars(this.rating, this.maxRating); - } + maxRating = computed(() => + this.cheatMode() || this.rating() > 5 ? maxRatingInCheatMode : 5, + ); + stars = computed(() => this.toStars(this.rating(), this.maxRating())); private toStars(rating: number, maxRating: number): Array<boolean> { const stars = new Array<boolean>(rating); [...] } rate(rating: number): void { - this.rating = rating; - this.#updateStars(); - this.ratingChange.next(rating); + this.rating.set(rating); } enterCheatMode() { - this.maxRating = maxRatingInCheatMode; - this.#updateStars(); + this.cheatMode.set(true); } }Remarks: After switching to Signals, we need to derive the remaining component state (
maxRating,starts) usingcomputed. Also, add a further signal informing whether we are in cheat mode. -
Switch to the file
rating.component.html(src/app/rating/rating.component.html) and call thestarSignal's setter in the for loop:-@for (star of stars; track $index) { +@for (star of stars(); track $index) { @if (star) { <img src="/assets/star.svg"
💾 Please find this lab's solution in the branch 02b-model-signals (get reset --hard && git checkout 02b-model-signals). You don't have to switch to it branch, if you are fine.
> Follow us on X (Twitter) > More Trainings (German) > More Trainings (English)