@Input and @Output โ Parent Child Data Flow
Components are isolated. @Input sends data down from parent to child. @Output sends events up from child to parent. This two-way flow is the foundation of Angular component communication.
Angular components are isolated โ they don't share state by default. To build meaningful UIs, components need to communicate.
"Parent = baap, Child = beta โ baap se beta ko data do, beta se baap ko event do."
The fundamental rule:
- Data flows DOWN via
@Inputโ parent gives data to child - Events flow UP via
@Outputโ child notifies parent - THIS IS UNIDIRECTIONAL DATA FLOW โ Angular's design for predictability
<!-- Parent template -->
<app-child
[data]="parentData" <!-- @Input โ data goes DOWN -->
(childEvent)="handle()"> <!-- @Output โ event comes UP -->
</app-child>
Why this design? Unidirectional data flow makes debugging easy โ you always know where data comes from. If something changes, it changed in the parent or an event was emitted from the child. No two-way binding spaghetti.
@Input decorator marks a class property as receiving data from parent.
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-biryani-card',
template: `
<div class="card">
<h3>{{ biryaniName }}</h3>
<p>Price: โน{{ price }}</p>
</div>
`
})
export class BiryaniCardComponent {
@Input() biryaniName = ''; // Optional with default
@Input({ required: true }) price!: number; // Must be provided!
@Input({ alias: 'img' }) imageUrl = ''; // Parent uses [img]
@Input({ transform: booleanAttribute }) isActive = false;
}
"Input = courier from parent โ parcel milta hai child ko."
Parent template usage:
<app-biryani-card
[biryaniName]="'Hyderabadi Biryani'"
[price]="500"
[img]="'/images/biryani.jpg'"
[isActive]="true">
</app-biryani-card>
@Input options:
required: true(Angular 17+) โ compile error if parent doesn't providealias: 'img'โ parent uses [img], child uses this.imageUrltransform: booleanAttributeโ auto-converts string/empty to booleantransform: numberAttributeโ auto-converts string to number
@Output decorator creates an event that the child can emit to notify the parent.
import { Component, Input, Output, EventEmitter } from '@angular/core';
export interface Order {
name: string;
quantity: number;
price: number;
}
@Component({...})
export class BiryaniCardComponent {
@Input() biryaniName = '';
@Input() price = 0;
// @Output โ ALWAYS use EventEmitter
@Output() orderPlaced = new EventEmitter<Order>();
placeOrder() {
this.orderPlaced.emit({
name: this.biryaniName,
quantity: 1,
price: this.price
});
}
}
"Output = callback from child โ child bolta hai 'ye hua', parent sunta hai."
Parent template:
<app-biryani-card
[biryaniName]="selectedBiryani"
[price]="selectedPrice"
(orderPlaced)="onOrder($event)">
</app-biryani-card>
Parent component:
onOrder(order: Order) {
console.log('๐ฆ Order received:', order);
this.orderHistory.push(order);
}
Important rules for @Output:
- ALWAYS use
EventEmitterโ never plain functions or Subjects EventEmitteris Angular-specific โ works with template event binding- It extends Subject, so you can also use it with RxJS operators
Angular 17+ introduced transform functions that automatically convert input values.
booleanAttribute: converts presence/truthy to boolean
// Component
@Input({ transform: booleanAttribute }) isActive = false;
// Parent โ all of these set isActive to true:
<app-card isActive> <!-- empty string โ true -->
<app-card [isActive]="'true'"> <!-- "true" โ true -->
<app-card [isActive]="''"> <!-- empty โ true -->
// These set isActive to false:
<app-card> <!-- not provided โ false -->
<app-card [isActive]="false"> <!-- false โ false -->
<app-card [isActive]="'false'"> <!-- "false" โ false (converts!) -->
numberAttribute: converts string to number
// Component
@Input({ transform: numberAttribute }) count = 0;
// Parent
<app-counter [count]="5"> // 5 (number)
<app-counter count="5"> // "5" (string) โ 5 (number) automatically!
Custom transform functions:
// Custom transformer
function toUpperCase(value: string): string {
return value?.toUpperCase() ?? '';
}
@Input({ transform: toUpperCase }) name = '';
// Parent: [name]="'biryani'" โ child gets 'BIRYANI'
"Transform = automatic converter โ string se number, empty se boolean, custom bhi banao."
Two-way binding combines @Input and @Output with a naming convention.
// CHILD COMPONENT
@Component({...})
export class BiryaniInputComponent {
@Input() name = ''; // Property
@Output() nameChange = new EventEmitter<string>(); // MUST be "name" + "Change"
updateName(newName: string) {
this.name = newName;
this.nameChange.emit(newName); // Notify parent
}
}
// PARENT TEMPLATE
<app-biryani-input [(name)]="selectedBiryani"></app-biryani-input>
// This is SYNTAX SUGAR for:
<app-biryani-input
[name]="selectedBiryani"
(nameChange)="selectedBiryani = $event">
</app-biryani-input>
"Two-way = Input + Output with naming convention โ Angular magic."
The naming convention is strict:
- If @Input is
name, @Output MUST benameChange - If @Input is
biryaniName, @Output MUST bebiryaniNameChange - Without this exact naming,
[(name)]won't work!
Modern alternative: As covered in Stage 10, model() signal does the same thing in 1 line instead of 3 properties + update method. But understanding @Input/@Output two-way is important for reading legacy code.
Key Takeaways
- โ Data flows DOWN (@Input), Events flow UP (@Output) โ unidirectional
- โ @Input({ required: true }) โ compile error if parent misses it
- โ @Input({ transform: booleanAttribute }) โ auto-converts to boolean
- โ @Output โ ALWAYS use EventEmitter, never plain functions
- โ Two-way binding: [(name)] works when @Output = nameChange
- โ Treat @Input as READ-ONLY in child โ use @Output to notify parent
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login