🇺🇸 EN
·7 min read·↗·🇺🇸 EN

Cómo simplificar la comunicación entre componentes en Angular con model() y Signals

Portada principal del artículo: Cómo simplificar la comunicación entre componentes en Angular con model() y Signals

Estuve muy ocupado en septiembre, pero ahora estoy trabajando en la creación de un taller sobre signals y pensando en cómo pueden simplificar nuestro código. Durante un buen tiempo estuve usando @Input() y @Output() para comunicar componentes. Sin embargo, Angular 17 introdujo las input signals para esta tarea, las cuales funcionan muy bien, pero el equipo de Angular también preparó las model signals, una excelente alternativa para lograr comunicación bidireccional entre componentes escribiendo menos código. ¡Vamos a revisarlo!

Note: La función model está en developer preview, pero ya podemos probarla hoy mismo.

Pero quizás te preguntes: ¿para qué necesito otra forma de comunicarme si ya tengo input y output? Es una buena pregunta. Déjame mostrártelo con un ejemplo del mundo real.

Queremos mostrar una lista de películas y calificar cada una. Para facilitar la calificación de las películas, voy a usar Kendo UI for Angular Rating e implementaremos input/output signals para el enlace de datos bidireccional, para luego migrar a model(), que es un enfoque mucho más directo.

¡Vamos a hacerlo!

Configuración del proyecto

Voy a usar la versión más reciente de Angular. La forma más fácil es ejecutando el siguiente comando en la terminal. Acepta las preguntas por defecto respondiendo que sí.

npx -p @angular/cli signal-model-rating-kendo
Need to install the following packages:
@angular/cli@18.2.6
Ok to proceed? (y) y
.....

Luego, entra a la carpeta del proyecto con cd signal-model-rating-kendo y usa los schematics de Kendo UI para instalar Kendo Input Rating.

 
ng add @progress/kendo-angular-inputs
✔ Determining Package Manager
  › Using package manager: npm
✔ Searching for compatible package version
  › Found compatible package version: @progress/kendo-angular-inputs@16.10.0.
✔ Loading package information from registry
✔ Confirming installation
✔ Installing package
UPDATE package.json (1656 bytes)
UPDATE angular.json (3071 bytes)

La calificación usando Input/Output Signals

Primero, vamos a crear un componente personalizado para calificar películas usando el componente kendo-rating de Kendo UI. Enlazaremos la calificación de la película usando input/output signals para mostrar cómo funciona el enlace de datos bidireccional.

ng g c movie-rating
signal-model-rating-kendo % ng g c componentes/movie-rating
CREATE src/app/componentes/movie-rating/movie-rating.component.scss (0 bytes)
CREATE src/app/componentes/movie-rating/movie-rating.component.html (27 bytes)
CREATE src/app/componentes/movie-rating/movie-rating.component.spec.ts (628 bytes)
CREATE src/app/componentes/movie-rating/movie-rating.component.ts (258 bytes)

Usaremos el componente kendo-rating asignando su valor mediante una input signal que recibe el dato, y emitiremos el nuevo puntaje usando un output cuando el usuario actualice la calificación.

Abre el archivo movie-rating.component.ts. Primero, importa RatingComponent desde @progress/kendo-angular-inputs. Luego, declara la propiedad rating como input para recibir el valor y ratingChange como output para emitir el evento.

 
import { Component, input, output } from '@angular/core';
import { RatingComponent } from '@progress/kendo-angular-inputs';
 
@Component({
  selector: 'app-movie-rating',
  standalone: true,
  imports: [RatingComponent],
  templateUrl: './movie-rating.component.html',
  styleUrl: './movie-rating.component.scss',
})
export class MovieRatingComponent {
  rating = input(4);
  ratingChange = output<number>();
 
  onRatingChange(newRating: number) {
    this.ratingChange.emit(newRating);
  }
}

En la plantilla usamos el componente kendo-rating, vinculamos la propiedad value con la signal rating() y escuchamos el evento valueChange para llamar al método onRatingChange. El código final queda así:

<kendo-rating [value]="rating()" (valueChange)="onRatingChange($event)">
</kendo-rating>

Perfecto, ahora es momento de conectar la lista de películas con nuestro componente movie-rating. ¡Vamos a hacerlo!

Comunicar la calificación de las películas

Abre app.component.ts y declara un tipo Movie con las siguientes propiedades:

interface Movie {
  title: string;
  rating: number;
}

Creamos una lista de prueba de películas para usar en el componente:

movies = signal<Movie[]>([
    { title: 'Inception', rating: 4 },
    { title: 'Interstellar', rating: 5 },
    { title: 'The Dark Knight', rating: 5 },
    { title: 'Dunkirk', rating: 3 },
  ]);

El paso más importante es importar MovieRatingComponent en app.component.ts. El código final queda de la siguiente manera:

import { Component, signal } from '@angular/core';
import { RouterOutlet this } from '@angular/router';
import { MovieRatingComponent } from './componentes/movie-rating/movie-rating.component';
 
interface Movie {
  title: string;
  rating: number;
}
 
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet, MovieRatingComponent],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss',
})
export class AppComponent {
  movies = signal<Movie[]>([
    { title: 'Inception', rating: 4 },
 
    { title: 'Interstellar', rating: 5 },
 
    { title: 'The Dark Knight', rating: 5 },
 
    { title: 'Dunkirk', rating: 3 },
  ]);
}

En app.component.html usamos @for para iterar sobre la lista de películas, mostrar el título, la calificación y el componente movie-rating para permitir que el usuario vote.

Al usar el enlace bidireccional con [(rating)], podemos sincronizar el valor cada vez que se emite:

@for (movie of movies(); track movie) {
  <h3>{{ movie.title }}</h3>
 
  <app-movie-rating [(rating)]="movie.rating"></app-movie-rating>
 
  <p>Current rating: {{ movie.rating }} stars</p>
}

Guarda los cambios y ejecuta ng serve para que nuestra aplicación muestre la lista de películas y sus calificaciones. Abre http://localhost:4200 en tu navegador y verás la lista funcionando con el sistema de votos.

Implementamos con éxito el enlace bidireccional usando input/output signals, pero esto requiere manejar manualmente la entrada y emitir la salida.

¿Y si te dijera que podemos hacerlo con menos código usando model()? 😎

Migrando a model()

Antes de comenzar, ¿por qué querrías cambiar a model()? La ventaja es que model() crea automáticamente tanto un input como un output, facilitando el manejo del enlace bidireccional sin tener que emitir eventos a mano. Con un pequeño cambio en MovieRatingComponent para usar model() en vez de input y output por separado, logramos el mismo resultado sin necesidad de signals de tipo output.

Vamos a aplicarlo. Primero importa model desde @angular/core:

import { Component, model } from '@angular/core';
import { RatingComponent } from '@progress/kendo-angular-inputs';

Cambia el input por model. Al ser una signal modificable (writable signal), podemos actualizarla directamente usando el método update:

import { Component, model } from '@angular/core';
import { RatingComponent } from '@progress/kendo-angular-inputs';
 
@Component({
  selector: 'app-movie-rating',
  standalone: true,
  imports: [RatingComponent],
  templateUrl: './movie-rating.component.html',
  styleUrl: './movie-rating.component.scss',
})
export class MovieRatingComponent {
  rating = model(4);
 
  onRatingChange(newRating: number) {
    this.rating.update(() => newRating);
  }
}

Esto emite automáticamente el valor cuando la signal cambia. Ya no necesitamos un output para la comunicación bidireccional, porque model crea una salida por nosotros agregando la palabra Change al final del nombre. En nuestro caso, genera ratingChange.

<app-movie-rating [(rating)]="movie.rating" (ratingChange)="onRatingChange($event)"></app-movie-rating>

Abre http://localhost:4200 en tu navegador para ver la lista de películas con sus calificaciones. ¡Funciona igual pero con menos código!

¡UN MOMENTO! ¿Quieres aprender más sobre Angular Signals?

Te recomiendo mucho el libro Angular Signals Masterclass eBook de Kevin Kreuzer. Es una forma excelente de aprender todo sobre Signals en un solo lugar, con ejemplos claros que te guían paso a paso.

Descarga una vista previa gratuita para revisar parte del material. Y cuando quieras avanzar más, puedes adquirir la versión completa para tener la guía más completa sobre Signals.

Ahora ya conoces mi recomendación para aprender sobre Signals. 😎 ¡Continuemos!

Bien, pero ¿por qué existen input y model si no son lo mismo?

Puedes usar tanto input() como model() para comunicar componentes, pero la gran ventaja de model() es que crea automáticamente un input y un output para nuestra propiedad.

Antes de elegir uno, ten en cuenta los siguientes puntos:

  • model(): Crea de forma automática tanto un input como un output para una propiedad, permitiendo enlace bidireccional con mucho menos código. Es una signal modificable (writable signal), lo que significa que puede actualizarse y emite los cambios a quienes la consumen sin necesidad de gestionar eventos manualmente.

  • Input/Output Signals: Requieren manejar manualmente los valores de entrada y emitir eventos de salida para lograr el enlace bidireccional.

Recuerda: La función model() simplifica la comunicación, pero no soporta funciones de transformación, las cuales pueden ser necesarias en escenarios más complejos. Para esos casos, usar input() y output() por separado sigue siendo una mejor alternativa.

Resumen

Aprendimos a implementar el enlace bidireccional de datos usando input/output signals en Angular y luego vimos cómo la nueva función model() simplifica este proceso con mucho menos código.

Comenzamos construyendo un componente para calificar películas usando input/output signals, donde manejamos manualmente el enlace de datos y la emisión de eventos. Aunque este enfoque funciona bien, requiere más código repetitivo para gestionar la comunicación entre componentes. Luego pasamos al enfoque con model(), el cual simplifica el código al encargarse automáticamente tanto del enlace de entrada como del de salida.

¡Happy coding!

Artículos Relacionados

Compartir este artículo

Si esta guía te resultó útil, compártela con tu equipo o con otros desarrolladores.


Software real. Lecciones reales.

Comparto las lecciones que aprendí por las malas, para que puedas evitarlas o estar preparado cuando ocurran.

User avatar
User avatar
User avatar
User avatar
+13K

Únete a 13,800+ desarrolladores y lectores.

Cero spam. Cancela tu suscripción en cualquier momento.