Chapter 11.1โ˜• 15 min read

@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.

01Why 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.

02@Input โ€” Parent to Child

@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 provide
  • alias: 'img' โ€” parent uses [img], child uses this.imageUrl
  • transform: booleanAttribute โ€” auto-converts string/empty to boolean
  • transform: numberAttribute โ€” auto-converts string to number
03@Output โ€” Child to Parent

@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
  • EventEmitter is Angular-specific โ€” works with template event binding
  • It extends Subject, so you can also use it with RxJS operators
04Input with Transform (Angular 17+)

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."

05Two-Way Binding with @Input + @Output

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 be nameChange
  • If @Input is biryaniName, @Output MUST be biryaniNameChange
  • 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
Course Search
Search across all chapters & stages
๐Ÿ“–

Search the course

Type any topic โ€” branching, stash, rebase, hooks โ€” and jump straight to that chapter.

merge branchesgit stashundo commitrebase