Inputs and Outputs

Understand how Angular components communicate by passing data from parent to child through inputs and sending events from child to parent through outputs.

Lesson content

What are Inputs and Outputs? Inputs and outputs allow Angular components to communicate with each other. An input sends data from a parent component to a child component. An output sends an event or value from a child component to a parent component. Simple Definition Input = Parent → Child Data Output = Child → Parent Event Communication Flow Parent Component ↓ Input Binding ↓ Child Component ↓ User Action ↓ Output Event ↓ Parent Component 1. Input An input defines data that a component accepts from its parent. Modern Angular uses the input() function to create signal-based inputs. Child Component import { Component, input } from '@angular/core'; @Component({ selector: 'app-user-card', template: ` <article> <h2> {{ name() }} </h2> </article> ` }) export class UserCardComponent { name = input(''); } Parent Template <app-user-card name="Ajay" /> Input Flow Parent Value ↓ [name] Binding ↓ Child Input Signal ↓ Child Template Static Input Value Without square brackets, Angular passes a static string. <app-user-card name="Ajay" /> The child receives: 'Ajay' Dynamic Input Value Use property binding when the value comes from a parent expression. Parent Component export class DashboardComponent { selectedName = 'Ajay'; } Parent Template <app-user-card [name]="selectedName" /> Square brackets evaluate the parent expression instead of passing literal text. Reading an Input Signal The input() function returns a read-only input signal. Call the signal to read its current value. name = input(''); <p> {{ name() }} </p> Incorrect {{ name }} Correct {{ name() }} Input with Default Value status = input('inactive'); When the parent does not provide a value, the default value is used. <app-user-card /> Child Value inactive Optional Input without Default Value description = input<string>(); Its type is: InputSignal< string | undefined > Template <p> {{ description() ?? 'No description' }} </p> Required Input Use input.required() when the parent must provide a value. userId = input.required<number>(); Valid Usage <app-user-card [userId]="10" /> Invalid Usage <app-user-card /> Angular reports a build-time template error when a required input is missing. Multiple Inputs import { Component, input } from '@angular/core'; @Component({ selector: 'app-product-card', template: ` <article> <h2> {{ name() }} </h2> <p> Price: {{ price() }} </p> <p> Available: {{ inStock() }} </p> </article> ` }) export class ProductCardComponent { name = input.required<string>(); price = input.required<number>(); inStock = input(true); } Parent Usage <app-product-card [name]="product.name" [price]="product.price" [inStock]="product.inStock" /> Input Type Safety Inputs should use clear TypeScript types. export interface User { id: number; name: string; email: string; } user = input.required<User>(); Template <h2> {{ user().name }} </h2> <p> {{ user().email }} </p> Input Alias An input alias changes the public binding name used by the parent. userName = input('', { alias: 'name' }); Parent Usage <app-user-card name="Ajay" /> Inside the Child {{ userName() }} The public name is name , while the class property remains userName . Input Transform An input transform converts a value before the child component receives it. import { booleanAttribute, Component, input } from '@angular/core'; @Component({ selector: 'app-panel', template: ` <p> Expanded: {{ expanded() }} </p> ` }) export class PanelComponent { expanded = input(false, { transform: booleanAttribute }); } Usage <app-panel expanded /> The value is transformed into the boolean value true . Number Input Transform import { Component, input, numberAttribute } from '@angular/core'; @Component({ selector: 'app-counter', template: ` <p> Count: {{ count() }} </p> ` }) export class CounterComponent { count = input(0, { transform: numberAttribute }); } Usage <app-counter count="10" /> The string attribute value is transformed into the number 10 . Derived Values from Inputs Use computed() to create values derived from input signals. import { Component, computed, input } from '@angular/core'; @Component({ selector: 'app-user-name', template: ` <p> {{ fullName() }} </p> ` }) export class UserNameComponent { firstName = input.required<string>(); lastName = input.required<string>(); fullName = computed(() => `${this.firstName()} ${this.lastName()}` ); } Reacting to Input Changes Use effect() when an input change must trigger a side effect. import { Component, effect, input } from '@angular/core'; @Component({ selector: 'app-user-detail', template: ` <p> User ID: {{ userId() }} </p> ` }) export class UserDetailComponent { userId = input.required<number>(); constructor() { effect(() => { console.log( 'User changed:', this.userId() ); }); } } Use computed signals for derived values and effects only for genuine side effects. Inputs are Read-Only A normal input signal cannot be changed directly by the child. name = input('Ajay'); Invalid this.name.set( 'Rahul' ); The parent owns the input value. When the child needs to request a change, it should emit an output or use a model input for two-way binding. 2. Output An output defines a custom event that a child component can emit to its parent. Modern Angular uses the output() function. Child Component import { Component, output } from '@angular/core'; @Component({ selector: 'app-save-button', template: ` <button (click)="save()" > Save </button> ` }) export class SaveButtonComponent { saved = output<void>(); save() { this.saved.emit(); } } Parent Template <app-save-button (saved)="handleSave()" /> Output Flow User Clicks Child Button ↓ Child Calls emit() ↓ Output Event Fires ↓ Parent Event Binding Runs ↓ Parent Method Executes Output with a Value Child Component import { Component, output } from '@angular/core'; @Component({ selector: 'app-user-card', template: ` <button (click)="selectUser()" > Select </button> ` }) export class UserCardComponent { selected = output<number>(); selectUser() { this.selected.emit(10); } } Parent Template <app-user-card (selected)=" handleSelected($event) " /> Parent Component handleSelected( userId: number ) { console.log( 'Selected user:', userId ); } The $event Value The value emitted by the child is available to the parent through $event . this.selected.emit(10); (selected)=" handleSelected($event) " $event = 10 Output with an Object export interface UserAction { userId: number; action: 'edit' | 'delete'; } action = output<UserAction>(); editUser() { this.action.emit({ userId: 10, action: 'edit' }); } Parent Template <app-user-card (action)=" handleAction($event) " /> Parent Component handleAction( event: UserAction ) { console.log( event.userId, event.action ); } Output Alias An output can expose a different public event name. closed = output<void>({ alias: 'panelClosed' }); Parent Usage <app-panel (panelClosed)=" handleClose() " /> Output Events Do Not Bubble Angular custom output events do not bubble through the DOM like native browser events. Child Output ↓ Direct Parent Listener A grandparent does not automatically receive the event. The parent must handle and optionally emit another event, or shared state can be used. Complete Parent-Child Example Child Component import { Component, input, output } from '@angular/core'; export interface Product { id: number; name: string; } @Component({ selector: 'app-product-card', template: ` <article> <h2> {{ product().name }} </h2> <button (click)="select()" > Select </button> <button (click)="remove()" > Remove </button> </article> ` }) export class ProductCardComponent { product = input.required<Product>(); selected = output<Product>(); deleted = output<number>(); select() { this.selected.emit( this.product() ); } remove() { this.deleted.emit( this.product().id ); } } Parent Component import { Component } from '@angular/core'; @Component({ selector: 'app-product-list', imports: [ ProductCardComponent ], template: ` @for ( product of products; track product.id ) { <app-product-card [product]="product" (selected)=" selectProduct($event) " (deleted)=" deleteProduct($event) " /> } ` }) export class ProductListComponent { products: Product[] = [ { id: 1, name: 'Keyboard' }, { id: 2, name: 'Mouse' } ]; selectProduct( product: Product ) { console.log( 'Selected:', product ); } deleteProduct( productId: number ) { this.products = this.products.filter( product => product.id !== productId ); } } Input and Output Together Parent │ │ [product]="product" ▼ Child │ │ (deleted)="deleteProduct($event)" ▼ Parent Input = Parent sends data Output = Child sends action 3. Two-Way Binding with model() Use model() when the child component must receive and update the same value. Child Component import { Component, model } from '@angular/core'; @Component({ selector: 'app-quantity-selector', template: ` <button (click)="decrease()" > - </button> <span> {{ quantity() }} </span> <button (click)="increase()" > + </button> ` }) export class QuantitySelectorComponent { quantity = model(1); increase() { this.quantity.update( value => value + 1 ); } decrease() { this.quantity.update( value => Math.max( value - 1, 1 ) ); } } Parent Template <app-quantity-selector [(quantity)]="orderQuantity" /> Parent Component orderQuantity = 1; Two-Way Flow Parent Value ↓ Model Input ↓ Child Updates Model ↓ Implicit Change Output ↓ Parent Value Updates Implicit Change Output A model input automatically creates a corresponding output ending in Change . quantity = model(1); Angular automatically creates: quantity quantityChange Two-Way Syntax <app-quantity-selector [(quantity)]="orderQuantity" /> Expanded Syntax <app-quantity-selector [quantity]="orderQuantity" (quantityChange)=" orderQuantity = $event " /> Input and Output vs model() Input and Output model() Explicit one-way data and event flow Supports two-way binding Suitable for actions such as save or delete Suitable for editable values Uses separate input and output names Creates an implicit Change output [user] and (deleted) [(quantity)] Traditional @Input and @Output Existing Angular applications may use decorator-based inputs and outputs. import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'app-user-card', template: ` <h2> {{ name }} </h2> <button (click)="selected.emit(id)" > Select </button> ` }) export class UserCardComponent { @Input({ required: true }) id!: number; @Input() name = ''; @Output() selected = new EventEmitter<number>(); } Parent Usage <app-user-card [id]="user.id" [name]="user.name" (selected)=" selectUser($event) " /> Modern vs Traditional APIs Modern API Traditional API input() @Input() Returns an input signal Uses a normal class property output() @Output() with EventEmitter model() for two-way values Input plus Change output convention Recommended for new code Common in existing applications Programmatic Output Subscription An output can also be subscribed to programmatically when a component is created dynamically. const componentRef = viewContainerRef .createComponent( UserCardComponent ); const subscription = componentRef.instance .selected .subscribe( userId => { console.log( 'Selected:', userId ); } ); Angular automatically cleans up output subscriptions when it destroys the related component. A manually created subscription can also be unsubscribed explicitly when necessary. When to Use Inputs and Outputs Use Inputs When A parent provides display data A child needs configuration A reusable component receives an object A parent controls child state Use Outputs When A child reports a button click A child reports selection A child requests deletion A child submits form data Use model() When The child edits a value The parent and child must remain synchronized Building custom input controls Building sliders, counters, toggles, or selectors Input Mutation Avoid directly mutating an object received through an input. Avoid user = input.required<User>(); rename() { this.user().name = 'Rahul'; } The object belongs to the parent. Prefer Emitting an Event renameRequested = output<string>(); rename() { this.renameRequested.emit( 'Rahul' ); } Parent Updates the State renameUser( name: string ) { this.user = { ...this.user, name }; } Inputs and Change Detection When a parent binding changes, Angular updates the corresponding child input. Parent State Changes ↓ Input Binding Updates ↓ Child Input Signal Changes ↓ Dependent Template and computed Values Update Stable and immutable object updates make component data flow easier to understand. Inputs with OnPush import { ChangeDetectionStrategy, Component, input } from '@angular/core'; @Component({ selector: 'app-user-card', changeDetection: ChangeDetectionStrategy.OnPush, template: ` <h2> {{ user().name }} </h2> ` }) export class UserCardComponent { user = input.required<User>(); } Signal inputs work naturally with OnPush because Angular tracks signal reads in the template. Inputs vs Services Inputs and Outputs Service Direct parent-child communication Communication across unrelated components Clear local data flow Shared state or shared behavior Best for reusable UI components Best for application-level coordination Parent passes data explicitly Components inject a shared dependency Avoid Long Output Chains Passing the same event through many nested components can create output chaining. Child ↓ Parent ↓ Grandparent ↓ Page For deeply nested or unrelated components, consider a shared service or feature state store. Common Mistakes 1. Forgetting to Call an Input Signal Wrong: {{ user }} Correct: {{ user() }} 2. Changing a Normal Input from the Child Normal input signals are read-only. Emit an output or use model() . 3. Forgetting Square Brackets <app-counter count="total" /> This passes the text total . <app-counter [count]="total" /> This passes the value of the parent property. 4. Forgetting Parentheses for an Output Wrong: <app-user-card selected="handleSelected()" /> Correct: <app-user-card (selected)=" handleSelected($event) " /> 5. Using EventEmitter for Inputs Inputs receive values. Outputs emit events. 6. Mutating Parent-Owned Input Objects Emit the requested change and let the parent update its state. 7. Using model() for Every Event Use model() for synchronized editable values, not actions such as save, cancel, or delete. 8. Using Native Event Names for Outputs Avoid output names such as click , change , or input because they can be confused with native DOM events. 9. Assuming Outputs Bubble Angular custom output events do not bubble through the DOM. 10. Using Inputs for Global State Inputs are best for direct component relationships. Use services or state management for broad application state. Best Practices Use input() and output() for new Angular code Use required inputs for mandatory values Provide sensible defaults for optional inputs Use strongly typed input and output values Use aliases only when they improve the public API Use input transforms for simple value normalization Keep normal inputs read-only in the child Use outputs to describe actions or events Use model() only for genuine two-way values Avoid output names that conflict with native events Prefer immutable updates for input objects and arrays Use a shared service for unrelated or deeply nested components Interview Questions What is an input? An input is a component property that receives data from a parent component. What is an output? An output is a custom event that sends a value or notification from a child component to its parent. What is the direction of input communication? Parent → Child What is the direction of output communication? Child → Parent How do you create a required input? user = input.required<User>(); How do you read a signal input? Call it like a function. {{ user().name }} How do you emit an output? selected.emit(value); What is $event? $event contains the value emitted by the child output. Do Angular outputs bubble through the DOM? No. Angular custom outputs do not bubble. What is model()? model() creates a writable model input that supports two-way component binding. What output does model() create? It automatically creates an output using the model name followed by Change . value = model(0); Implicit output: valueChange What is the difference between output() and EventEmitter? output() is the modern initializer API and returns an OutputEmitterRef . Existing code may use @Output() with EventEmitter . Key Point Inputs pass state into a child component, while outputs notify the parent about actions or changes initiated by the child. Easy Formula Input = Parent → Child Output = Child → Parent input() = Read-Only Input Signal input.required() = Mandatory Input output() = Custom Child Event emit() = Send Event Value $event = Emitted Value model() = Input + Change Output + Two-Way Binding Parent State ↓ [input]="value" ↓ Child Component ↓ User Action ↓ output.emit(value) ↓ (output)="handler($event)" ↓ Parent Updates State