Isaac.

Angular Component Communication

Implement patterns for component communication in Angular.

By EMEPublished: February 20, 2025
angularcomponentscommunicationparent childservices

A Simple Analogy

Component communication is like a phone system. Data flows through channels: parents to children, siblings through services.


Why Communication?

  • Reusable: Components work together
  • Encapsulation: Each component has responsibility
  • Testing: Easier to test isolated components
  • Scalability: Build complex UIs from simple parts
  • Maintainability: Changes isolated to components

Parent to Child (@Input)

// Child component
@Component({
  selector: 'app-card',
  template: `<div>{{ title }}</div>`
})
export class CardComponent {
  @Input() title: string;
}

// Parent component
@Component({
  selector: 'app-parent',
  template: `<app-card [title]="'Hello World'"></app-card>`
})
export class ParentComponent {}

Child to Parent (@Output)

// Child component
@Component({
  selector: 'app-button-group',
  template: `<button (click)="onSelect('save')">Save</button>`
})
export class ButtonGroupComponent {
  @Output() selected = new EventEmitter<string>();
  
  onSelect(action: string) {
    this.selected.emit(action);
  }
}

// Parent component
@Component({
  selector: 'app-dialog',
  template: `<app-button-group (selected)="handleAction($event)"></app-button-group>`
})
export class DialogComponent {
  handleAction(action: string) {
    console.log('Action:', action);
  }
}

Sibling Communication (Service)

// Shared service
@Injectable({ providedIn: 'root' })
export class MessageService {
  private messageSubject = new Subject<string>();
  public message$ = this.messageSubject.asObservable();
  
  sendMessage(msg: string) {
    this.messageSubject.next(msg);
  }
}

// Component A (sender)
@Component({
  selector: 'app-sender'
})
export class SenderComponent {
  constructor(private msgService: MessageService) {}
  
  send() {
    this.msgService.sendMessage('Hello sibling');
  }
}

// Component B (receiver)
@Component({
  selector: 'app-receiver'
})
export class ReceiverComponent implements OnInit {
  constructor(private msgService: MessageService) {}
  
  ngOnInit() {
    this.msgService.message$.subscribe(msg => {
      console.log('Received:', msg);
    });
  }
}

ViewChild Access

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

Best Practices

  1. One-way binding: Keep data flow simple
  2. Services: For complex shared state
  3. RxJS: Use observables for multiple updates
  4. Immutability: Don't mutate parent data
  5. Testing: Mock services for unit tests

Related Concepts

  • Change detection
  • Zone management
  • State management (NgRx)
  • Dependency injection

Summary

Communicate between Angular components using @Input/@Output for parent-child, services with RxJS for siblings.