Skip to content

3. Signals

Andrei Antal edited this page Oct 24, 2024 · 2 revisions

Challenge 3 - Refactor to signals

In this exercise we will refactor our components to use signals and update the template bindings to use them.

1. Movie list component

  • Update the MovieListComponent to use a signal for the movies$ property, by converting the Observable to a signal using the toSignal method from @angular/core
movies$ = toSignal(
  ...
);
  • update the reference in the template to call the signal -> movies$()

2. Movie item component

  • Change the movie and editable inputs from decorated properties to required input signals
movie = input.required<Movie>();
editable = input(true);
  • update the commentUpdate and movieDelete outputs from decorated properties to output signals
commentUpdate = output<CommentUpdate>();
movieDelete = output<string>();
  • create remove existing movieComment and commentSaved properties and create a state computed signal that will hold the movie state (movieComment and commentSaved as signals) and change on the movie input:
state = computed(() => {
  const movie = this.movie();
  return {
    movieComment: signal(movie.comment),
    commentSaved: signal(movie.comment.length > 0),
  };
});
  • update the reference in the template to call the signals -> movie(), editable(), state().commentSaved(), state().movieComment()

3. Movie detail component

  • Enable routing component input binding in the app.config.ts file:
export const appConfig: ApplicationConfig = {
  providers: [
    ...
    provideRouter(
      routes, 
      withComponentInputBinding() // this enables component input binding for route params
    ),
  ],
};
  • We can nou bind directly to the id url parameter in the MovieDetailComponent class using an input signal:
id = input<string>('');
  • update the #movie variable to a signal and the #isNewMovie to a computed signal that will check if the id is empty (new vs existing movie)
#movie = signal<Movie>(EMPTY_MOVIE);
#isNewMovie = computed(() => !this.id());
  • Remove the ngOnInit function and use an effect to update the #movie signal when the id input changes
effect((onCleanup) => {
  const id = this.id(); // track the id input
  if (id) {
    const sub = this.#movieService.getMovie(id).subscribe((movie) => { // get the movie from the service
      this.movieForm.patchValue(movie);
      this.#movie.set(movie);
    });
    onCleanup(() => sub.unsubscribe()); // cleanup subscription when effect gets disposed
  }
});

DOCUMENTATION

withComponentInputBinding

Signals

Diffs

Clone this wiki locally