Component Communication
The Component Data Highway
In a component-driven architecture, applications are structured as a hierarchical tree of decoupled visual nodes. For the application to function cohesively, these isolated nodes must pass data and share state changes across parent-child boundaries.
To maintain predictable data flows and prevent chaotic state mutations, frameworks enforce a unidirectional data flow pattern: configuration properties flow down from parents to children, while state notification events fire upward from children to parents.
Downstream Data Sharing: @Input()
The @Input() decorator defines an open data entry gate on a child component. It allows a parent component to pass custom configuration profiles, text strings, arrays, or complete state objects down into a nested child instance.
Property Binding Integration: The parent targets the child's declared
@Inputproperty name using standard HTML property binding square brackets ([property]="value").Reactive Tracking: Input properties hook natively into change detection lifecycles. When the parent component modifies the bound data variable, the child component automatically receives the updated value.
TypeScript
// Child Component Logical Controller (job-card.component.ts)
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-job-card',
standalone: true,
template: `
<div class="card">
<h4>{{ jobTitle }}</h4>
<p>Company: {{ businessName }}</p>
</div>
`
})
export class JobCardComponent {
// Explicit entry gates for parent data streams
@Input() jobTitle: string = '';
@Input('company') businessName: string = ''; // Uses an explicit attribute alias
}
HTML
<app-job-card
[jobTitle]="'Full-Stack Developer'"
[company]="'THARUN plc'">
</app-job-card>Upstream Notification Systems: @Output() & EventEmitter
When an action occurs inside a child component—such as a user clicking a specific button, submitting a local form container, or modifying a localized toggle—the child component cannot mutate parent states directly. Instead, it utilizes the @Output() decorator coupled with an instance of the EventEmitter class to emit a structural signal upward.
The Mechanics of EventEmitter
EventEmitteris a framework-specific abstraction built on top of RxJS subject streams. It provides an explicit interface to push events out of a component infrastructure.It uses the
.emit()method to transmit a data payload. The payload can be a primitive type (like a string or number) or a deeply structured custom object.The parent component intercepts the signal using standard event binding parentheses
(outputName)="handleAction($event)". The special$eventkeyword captures the exact data packet transmitted by the.emit()method.
TypeScript
// Child Component Logical Controller (job-card.component.ts)
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-job-card',
standalone: true,
template: `
<div class="card">
<button (click)="triggerApply()">Apply Now</button>
</div>
`
})
export class JobCardComponent {
// Creating an upstream communication channel routing number payloads
@Output() applyClicked = new EventEmitter<number>();
triggerApply() {
const mockJobId = 404;
this.applyClicked.emit(mockJobId); // Firing the event upward with the payload
}
}
TypeScript
// Parent Component Logical Controller (dashboard.component.ts)
import { Component } from '@angular/core';
@Component({
selector: 'app-dashboard',
standalone: true,
template: `
<app-job-card (applyClicked)="processJobApplication($event)"></app-job-card>
`
})
export class DashboardComponent {
processJobApplication(jobId: number) {
console.log(`Parent captured event! Processing application sequence for Job ID: ${jobId}`);
}
}Modern Alternative: Signal-Based Inputs and Outputs
Modern framework engines introduce developer preview primitives that migrate component communication mechanics away from standard decorators and toward fine-grained Signals. This eliminates legacy decorator overhead and unifies reactivity patterns across templates.
TypeScript
// Modern Signal-Based Component Communication Layout
import { Component, input, output } from '@angular/core';
@Component({
selector: 'app-modern-card',
standalone: true,
template: `
<div class="card">
<h4>Role: {{ roleName() }}</h4>
<button (click)="notifyParent()">Select</button>
</div>
`
})
export class ModernCardComponent {
// Read-only Signal property stream (auto-tracks changes without lifecycle checks)
roleName = input.required<string>();
// Clean, decorator-free output emitter instance
selectionChanged = output<string>();
notifyParent() {
this.selectionChanged.emit(this.roleName());
}
}