En Angular, la interacción entre componentes es fundamental para construir aplicaciones dinámicas y modulares. Este tema cubre las diferentes formas en que los componentes pueden comunicarse entre sí, incluyendo la comunicación padre-hijo, hijo-padre y entre componentes hermanos.

Conceptos Clave

  1. Input y Output Decorators: Utilizados para la comunicación entre componentes padre e hijo.
  2. ViewChild y ContentChild: Permiten acceder a un componente hijo desde el componente padre.
  3. Servicios Compartidos: Facilitan la comunicación entre componentes que no tienen una relación directa de padre-hijo.
  4. EventEmitter: Utilizado para emitir eventos desde un componente hijo hacia su componente padre.

Comunicación Padre-Hijo

Input Decorator

El decorador @Input permite que un componente hijo reciba datos de su componente padre.

Ejemplo

Componente Padre (parent.component.ts):

import { Component } from '@angular/core';

@Component({
  selector: 'app-parent',
  template: `<app-child [childProperty]="parentProperty"></app-child>`,
})
export class ParentComponent {
  parentProperty = 'Hello from Parent';
}

Componente Hijo (child.component.ts):

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `<p>{{ childProperty }}</p>`,
})
export class ChildComponent {
  @Input() childProperty: string;
}

Output Decorator y EventEmitter

El decorador @Output y la clase EventEmitter permiten que un componente hijo envíe datos a su componente padre.

Ejemplo

Componente Padre (parent.component.ts):

import { Component } from '@angular/core';

@Component({
  selector: 'app-parent',
  template: `<app-child (childEvent)="handleChildEvent($event)"></app-child>`,
})
export class ParentComponent {
  handleChildEvent(event: string) {
    console.log(event);
  }
}

Componente Hijo (child.component.ts):

import { Component, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `<button (click)="sendEvent()">Click me</button>`,
})
export class ChildComponent {
  @Output() childEvent = new EventEmitter<string>();

  sendEvent() {
    this.childEvent.emit('Hello from Child');
  }
}

Comunicación Hijo-Padre

ViewChild Decorator

El decorador @ViewChild permite que un componente padre acceda a una instancia de un componente hijo.

Ejemplo

Componente Padre (parent.component.ts):

import { Component, AfterViewInit, ViewChild } from '@angular/core';
import { ChildComponent } from './child.component';

@Component({
  selector: 'app-parent',
  template: `<app-child></app-child>`,
})
export class ParentComponent implements AfterViewInit {
  @ViewChild(ChildComponent) childComponent: ChildComponent;

  ngAfterViewInit() {
    console.log(this.childComponent.childProperty);
  }
}

Componente Hijo (child.component.ts):

import { Component } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `<p>Child Component</p>`,
})
export class ChildComponent {
  childProperty = 'Hello from Child';
}

Comunicación entre Componentes Hermanos

Servicios Compartidos

Los servicios compartidos permiten la comunicación entre componentes que no tienen una relación directa de padre-hijo.

Ejemplo

Servicio (shared.service.ts):

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class SharedService {
  private messageSource = new Subject<string>();
  currentMessage = this.messageSource.asObservable();

  changeMessage(message: string) {
    this.messageSource.next(message);
  }
}

Componente Hermano 1 (sibling1.component.ts):

import { Component } from '@angular/core';
import { SharedService } from './shared.service';

@Component({
  selector: 'app-sibling1',
  template: `<button (click)="sendMessage()">Send Message</button>`,
})
export class Sibling1Component {
  constructor(private sharedService: SharedService) {}

  sendMessage() {
    this.sharedService.changeMessage('Hello from Sibling 1');
  }
}

Componente Hermano 2 (sibling2.component.ts):

import { Component, OnInit } from '@angular/core';
import { SharedService } from './shared.service';

@Component({
  selector: 'app-sibling2',
  template: `<p>{{ message }}</p>`,
})
export class Sibling2Component implements OnInit {
  message: string;

  constructor(private sharedService: SharedService) {}

  ngOnInit() {
    this.sharedService.currentMessage.subscribe(
      (message) => (this.message = message)
    );
  }
}

Ejercicio Práctico

Ejercicio

  1. Crea un componente padre y un componente hijo.
  2. Utiliza @Input para pasar un mensaje del componente padre al componente hijo.
  3. Utiliza @Output y EventEmitter para enviar un mensaje del componente hijo al componente padre.
  4. Crea dos componentes hermanos y un servicio compartido.
  5. Utiliza el servicio compartido para enviar un mensaje desde el primer componente hermano al segundo componente hermano.

Solución

Componente Padre (parent.component.ts):

import { Component } from '@angular/core';

@Component({
  selector: 'app-parent',
  template: `
    <app-child [childMessage]="parentMessage" (childEvent)="receiveMessage($event)"></app-child>
    <app-sibling1></app-sibling1>
    <app-sibling2></app-sibling2>
  `,
})
export class ParentComponent {
  parentMessage = 'Message from Parent';
  
  receiveMessage(event: string) {
    console.log('Received from child:', event);
  }
}

Componente Hijo (child.component.ts):

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `
    <p>{{ childMessage }}</p>
    <button (click)="sendMessage()">Send to Parent</button>
  `,
})
export class ChildComponent {
  @Input() childMessage: string;
  @Output() childEvent = new EventEmitter<string>();

  sendMessage() {
    this.childEvent.emit('Message from Child');
  }
}

Servicio Compartido (shared.service.ts):

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class SharedService {
  private messageSource = new Subject<string>();
  currentMessage = this.messageSource.asObservable();

  changeMessage(message: string) {
    this.messageSource.next(message);
  }
}

Componente Hermano 1 (sibling1.component.ts):

import { Component } from '@angular/core';
import { SharedService } from './shared.service';

@Component({
  selector: 'app-sibling1',
  template: `<button (click)="sendMessage()">Send to Sibling 2</button>`,
})
export class Sibling1Component {
  constructor(private sharedService: SharedService) {}

  sendMessage() {
    this.sharedService.changeMessage('Message from Sibling 1');
  }
}

Componente Hermano 2 (sibling2.component.ts):

import { Component, OnInit } from '@angular/core';
import { SharedService } from './shared.service';

@Component({
  selector: 'app-sibling2',
  template: `<p>{{ message }}</p>`,
})
export class Sibling2Component implements OnInit {
  message: string;

  constructor(private sharedService: SharedService) {}

  ngOnInit() {
    this.sharedService.currentMessage.subscribe(
      (message) => (this.message = message)
    );
  }
}

Conclusión

La interacción entre componentes en Angular es esencial para construir aplicaciones complejas y modulares. A través de @Input, @Output, EventEmitter, @ViewChild, y servicios compartidos, los componentes pueden comunicarse de manera efectiva, permitiendo una mayor flexibilidad y reutilización del código. Con estos conceptos, estás preparado para manejar la comunicación entre componentes en tus aplicaciones Angular.

Curso de Angular

Módulo 1: Introducción a Angular

Módulo 2: Componentes de Angular

Módulo 3: Enlace de Datos y Directivas

Módulo 4: Servicios e Inyección de Dependencias

Módulo 5: Enrutamiento y Navegación

Módulo 6: Formularios en Angular

Módulo 7: Cliente HTTP y Observables

Módulo 8: Gestión de Estado

Módulo 9: Pruebas en Angular

Módulo 10: Conceptos Avanzados de Angular

Módulo 11: Despliegue y Mejores Prácticas

© Copyright 2024. Todos los derechos reservados