Master component communication patterns in Angular using Input, Output, ViewChild, ContentChild, and services.
Master component communication patterns in Angular using Input, Output, ViewChild, ContentChild, and services.
Learn about @Input - Parent to Child.
1// Child Component
2import { Component, Input } from '@angular/core';
3
4@Component({
5 selector: 'app-user-card',
6 template: \`
7 <div class="card">
8 <h3>{{ user.name }}</h3>
9 <p>{{ user.email }}</p>
10 </div>
11 \`
12})
13export class UserCardComponent {
14 @Input() user!: { name: string; email: string };
15 @Input({ required: true }) userId!: number;
16 @Input({ alias: 'userName' }) name?: string;
17}
18
19// Parent Component
20@Component({
21 selector: 'app-parent',
22 template: \`
23 <app-user-card
24 [user]="currentUser"
25 [userId]="123"
26 [userName]="'John'">
27 </app-user-card>
28 \`
29})
30export class ParentComponent {
31 currentUser = { name: 'John Doe', email: '[email protected]' };
32}Learn about @Output - Child to Parent.
1// Child Component
2import { Component, Output, EventEmitter } from '@angular/core';
3
4@Component({
5 selector: 'app-counter',
6 template: \`
7 <div>
8 <p>Count: {{ count }}</p>
9 <button (click)="increment()">+</button>
10 <button (click)="decrement()">-</button>
11 </div>
12 \`
13})
14export class CounterComponent {
15 @Output() countChanged = new EventEmitter<number>();
16 @Output('reset') onReset = new EventEmitter<void>();
17 count = 0;
18
19 increment() {
20 this.count++;
21 this.countChanged.emit(this.count);
22 }
23
24 decrement() {
25 this.count--;
26 this.countChanged.emit(this.count);
27 }
28}
29
30// Parent Component
31@Component({
32 selector: 'app-parent',
33 template: \`
34 <app-counter
35 (countChanged)="handleCountChange($event)"
36 (reset)="handleReset()">
37 </app-counter>
38 <p>Parent knows count is: {{ parentCount }}</p>
39 \`
40})
41export class ParentComponent {
42 parentCount = 0;
43
44 handleCountChange(newCount: number) {
45 this.parentCount = newCount;
46 }
47
48 handleReset() {
49 console.log('Counter reset');
50 }
51}Learn about @ViewChild and @ViewChildren.
1import { Component, ViewChild, ViewChildren, QueryList, AfterViewInit } from '@angular/core';
2
3@Component({
4 selector: 'app-parent',
5 template: \`
6 <input #searchInput type="text">
7 <app-child></app-child>
8 <app-child></app-child>
9 <app-child></app-child>
10 \`
11})
12export class ParentComponent implements AfterViewInit {
13 // Access element reference
14 @ViewChild('searchInput') searchInput!: ElementRef<HTMLInputElement>;
15
16 // Access child component
17 @ViewChild(ChildComponent) child!: ChildComponent;
18
19 // Access multiple children
20 @ViewChildren(ChildComponent) children!: QueryList<ChildComponent>;
21
22 ngAfterViewInit() {
23 // Access DOM element
24 this.searchInput.nativeElement.focus();
25
26 // Call child method
27 this.child.doSomething();
28
29 // Iterate through children
30 this.children.forEach(child => {
31 child.doSomething();
32 });
33
34 // Listen to changes
35 this.children.changes.subscribe(children => {
36 console.log('Children changed:', children.length);
37 });
38 }
39}Learn about Service-Based Communication.
1import { Injectable, signal } from '@angular/core';
2import { Subject, Observable } from 'rxjs';
3
4@Injectable({ providedIn: 'root' })
5export class SharedDataService {
6 // Using signals (Angular 16+)
7 private dataSignal = signal<string>('');
8 readonly data = this.dataSignal.asReadonly();
9
10 updateData(value: string) {
11 this.dataSignal.set(value);
12 }
13
14 // Using Subject
15 private messageSubject = new Subject<string>();
16 message$ = this.messageSubject.asObservable();
17
18 sendMessage(message: string) {
19 this.messageSubject.next(message);
20 }
21}
22
23// Component A - Sender
24@Component({
25 selector: 'app-sender',
26 template: \`
27 <button (click)="send()">Send Data</button>
28 \`
29})
30export class SenderComponent {
31 private sharedData = inject(SharedDataService);
32
33 send() {
34 this.sharedData.updateData('Hello from Sender');
35 this.sharedData.sendMessage('Message from Sender');
36 }
37}
38
39// Component B - Receiver
40@Component({
41 selector: 'app-receiver',
42 template: \`
43 <p>Received: {{ sharedData.data() }}</p>
44 <p>Message: {{ message }}</p>
45 \`
46})
47export class ReceiverComponent implements OnInit {
48 private sharedData = inject(SharedDataService);
49 message = '';
50
51 ngOnInit() {
52 this.sharedData.message$.subscribe(msg => {
53 this.message = msg;
54 });
55 }
56}Learn about Template Reference Variables.
1@Component({
2 template: \`
3 <!-- Access input value directly -->
4 <input #emailInput type="email">
5 <button (click)="log(emailInput.value)">Log</button>
6
7 <!-- Access child component methods -->
8 <app-counter #counter></app-counter>
9 <button (click)="counter.reset()">Reset Counter</button>
10
11 <!-- Pass reference to another component -->
12 <app-display [counter]="counter"></app-display>
13 \`
14})
15export class ExampleComponent {
16 log(value: string) {
17 console.log('Email:', value);
18 }
19}Learn about Content Projection with @ContentChild.
1import { Component, ContentChild } from '@angular/core';
2
3// Card Component
4@Component({
5 selector: 'app-card',
6 template: \`
7 <div class="card">
8 <div class="header">
9 <ng-content select="[card-header]"></ng-content>
10 </div>
11 <div class="body">
12 <ng-content></ng-content>
13 </div>
14 <div class="footer">
15 <ng-content select="[card-footer]"></ng-content>
16 </div>
17 </div>
18 \`
19})
20export class CardComponent {
21 @ContentChild('headerRef') header!: ElementRef;
22
23 ngAfterContentInit() {
24 console.log('Header element:', this.header);
25 }
26}
27
28// Usage
29@Component({
30 template: \`
31 <app-card>
32 <h2 #headerRef card-header>Title</h2>
33 <p>Card content goes here</p>
34 <button card-footer>Action</button>
35 </app-card>
36 \`
37})
38export class ParentComponent {}Use @Input/@Output for direct parent-child communication
Use services for communication between unrelated components
Prefer signals over Subject/BehaviorSubject when possible
Use @ViewChild only after ngAfterViewInit lifecycle hook
Mark required inputs with @Input(
Always complete/unsubscribe from observables
Use OnPush change detection for better performance
Document component APIs with JSDoc comments
Keep component coupling loose for better maintainability
@Input() enables data flow from parent to child components
@Output() with EventEmitter enables child-to-parent communication
@ViewChild provides access to child components and DOM elements
Services with dependency injection enable global state management
Signals provide reactive state management with automatic change detection
Template reference variables enable direct component/element access in templates
@ContentChild accesses projected content in ng-content slots
RxJS Subjects enable event-driven architecture patterns
Choose the right communication pattern based on component relationship and complexity
Your progress is saved in this browser, on this device only.