Este artículo esta basado en Angular 2+. En el momento de escribirlo, la última versión era Angular 4.
Está pensado para principiantes – si eres un desarrollador de Angular experimentado, probablemente ya conoces estas técnicas.
Actualización: el título de este artículo puede llevar a confusión, porque en realidad no pretendemos que los componentes se comuniquen directamente. Nuestros componentes deberían estar encapsulados y aislados. He elegido este título porque pienso que los desarrolladores que tengan este problema realizarán una búsqueda con estas palabras.
¿Cómo comunicarse entre componentes? Este es el tema con el que he visto a muchos desarrolladores de Angular tener problemas. Mostraremos los tres enfoques más comunes, con ejemplos que encajan en diferentes casos de uso.
Hay otro modo vía ‘redux’ que posiblemente veamos en un nuevo artículo.
Imagina un caso de uso en el que tenemos una barra lateral en la aplicación. La barra puede estar abierta o cerrada. Tenemos el componente side-bar y otro componente (o varios) que puede abrirla/cerrarla o preguntar por su estado.
Describiremos tres maneras de implementar este comportamiento
- Pasar la referencia de un componente a otro
- Comunicación a través de un componente padre
- Comunicación a través de un servicio
Cada uno de estos ejemplos tiene una aplicación de demostración con su código en StackBlitz y repositorio github.
1. Pasando la referencia de un componente a otro
Esta solución debería usarse cuando los componentes tienen dependencia entre ellos. Por ejemplo, un desplegable y el botón para desplegar. Normalmente no puede existir el uno sin el otro.
Crearemos un componente para abrir/cerrar la barra lateral (side-bar-toggle) que tendrá la barra lateral como un input, y al hacer clic en el botón abriremos o cerrarmos el componente side-bar.
Este es el código:
1 2 |
<app-side-bar-toggle [sideBar]="sideBar"></app-side-bar-toggle> <app-side-bar #sideBar></app-side-bar> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Component({ selector: 'app-side-bar-toggle', templateUrl: './side-bar-toggle.component.html', styleUrls: ['./side-bar-toggle.component.css'] }) export class SideBarToggleComponent { @Input() sideBar: SideBarComponent; @HostListener('click') click() { this.sideBar.toggle(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Component({ selector: 'app-side-bar', templateUrl: './side-bar.component.html', styleUrls: ['./side-bar.component.css'] }) export class SideBarComponent { @HostBinding('class.is-open') isOpen = false; toggle() { this.isOpen = !this.isOpen; } } |
Los imports se han omitido en el código TypeScript.
Comunicación a través de un componente padre
Puede utilizarse cuando es sencillo controlar el estado compartido entre componentes a través del componente padre y no queremos crear un nuevo servicio por culpa de una sola variable.
La implementación de este enfoque es casi igual que la anterior, aunque el side-bar-toggle no recibe el componente side-bar. En su lugar, el componente padre tiene la propiedad sideBarIsOpened (barraLateralAbierta), que se pasa al componente side-bar.
1 2 |
<app-side-bar-toggle (toggle)="toggleSideBar()"></app-side-bar-toggle> <app-side-bar [isOpen]="sideBarIsOpened"></app-side-bar> |
1 2 3 4 5 6 7 8 9 10 11 12 |
@Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: [ './app.component.css' ] }) export class AppComponent { sideBarIsOpened = false; toggleSideBar(shouldOpen: boolean) { this.sideBarIsOpened = !this.sideBarIsOpened; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Component({ selector: 'app-side-bar-toggle', templateUrl: './side-bar-toggle.component.html', styleUrls: ['./side-bar-toggle.component.css'] }) export class SideBarToggleComponent { @Output() toggle: EventEmitter<null> = new EventEmitter(); @HostListener('click') click() { this.toggle.emit(); } } |
1 2 3 4 5 6 7 8 9 10 11 |
@Component({ selector: 'app-side-bar', templateUrl: './side-bar.component.html', styleUrls: ['./side-bar.component.css'] }) export class SideBarComponent { @HostBinding('class.is-open') @Input() isOpen = false; } |
Los imports se han omitido en el código TypeScript.
Comunicación a través de un servicio
Finalmente, esta opción debería usarse cuando tenemos un componente que o bien es controlado o bien su estado es consultado desde múltiples instancias.
Ahora tenemos varios lugares en la aplicación que necesitan acceder a nuestro componente side-bar. Vamos a ver como lo hacemos:
Crearemos un servicio side-bar.service.ts, de forma que tendremos:
- side-bar.service.ts
- side-bar.component.ts
- side-bar.component.html
El servicio de la barra lateral tendrá un método ‘toggle’ (conmutar) y un evento ‘change’ (cambio) de modo que a cualquier componente que inyecte este servicio se le podrá notificar que el panel se ha abierto o puede abrirse/cerrarse.
En este ejemplo ni el componente side-bar ni side-bar-toggle tiene parámetros de input, porque se comunican a través de un servicio.
Ahora, el código:
1 2 |
<app-side-bar-toggle></app-side-bar-toggle> <app-side-bar></app-side-bar> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Component({ selector: 'app-side-bar-toggle', templateUrl: './side-bar-toggle.component.html', styleUrls: ['./side-bar-toggle.component.css'] }) export class SideBarToggleComponent { constructor( private sideBarService: SideBarService ) { } @HostListener('click') click() { this.sideBarService.toggle(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Component({ selector: 'app-side-bar', templateUrl: './side-bar.component.html', styleUrls: ['./side-bar.component.css'] }) export class SideBarComponent { @HostBinding('class.is-open') isOpen = false; constructor( private sideBarService: SideBarService ) { } ngOnInit() { this.sideBarService.change.subscribe(isOpen => { this.isOpen = isOpen; }); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Injectable() export class SideBarService { isOpen = false; @Output() change: EventEmitter<boolean> = new EventEmitter(); toggle() { this.isOpen = !this.isOpen; this.change.emit(this.isOpen); } } |
Los imports se han omitido en el código TypeScript.
Te recomiendo este libro: Angular desde 0, un enfoque práctico.
Si tienes otro modo de hacer esto, o tienes problemas con alguno de los ejemplos, puedes indicármelo en los comentarios.
No olvides seguirme en Medium para más artículos de JavaScript/Angular.
Puedes contactar conmigo en Instagram y seguirme en Twitter. Seguiré publicando los artículos más interesantes del mundo JavaScript.
Otros artículos que podrían interesarte (en inglés):
Volviendo a las raíces JavaScript: valor VS referencia
Nota: puedes encontrar el artículo original en https://medium.com/dailyjs/3-ways-to-communicate-between-angular-components-a1e3f3304ecb
El gran libro de Angular: 1