Understanding the @Input and @Output Decorators
Have you ever built a component and then found yourself struggling to make it talk to other parts of your application? Perhaps you need to send data down to a child component, or maybe you want the child to notify the parent when something happens, like a user clicking a button. Component communication is often the sticking point when developers start working with reusable components. This guide addresses exactly that challenge, focusing on how Angular's @Input and @Output decorators enable smooth data flow and interaction between components.
What Exactly Do the @Input and @Output Decorators Do?
To understand these decorators, it helps to start with the concept of decorators in Angular generally. At their core, decorators are functions that attach metadata to classes, their properties, or their methods. They wrap the target and alter its behavior—this is how Angular knows to treat certain elements in specific ways.
It's worth distinguishing between two main types: class decorators and class field decorators. Class decorators operate at the class level, applying their metadata to the entire class definition. Class field decorators, on the other hand, are applied to individual properties or fields within a class.
Consider a class decorator: the @Component() decorator. Placed directly above a class declaration, it provides Angular with the configuration needed to render and manage that class as a component.
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
}
Field decorators, in contrast, fine-tune the behavior of a specific property. The @Input and @Output decorators are prime examples of this category, and they form the foundation of parent-to-child and child-to-parent interaction.
With that foundation laid, let's zoom in on the specifics of these two frequently used decorators.
Using @Input for Parent-to-Child Data Flow
The @Input decorator's primary role is to allow data to pass from a parent component into a child component. When you place @Input on a property in a child component, you signal to Angular that this property is meant to receive a value from the parent. This opens up a direct channel of communication: the parent binds data to that property, and the child can then use it throughout its own template or logic.
Let's turn this theory into practice with a classic scenario. Imagine you have two components:
Parent Component
Child Component
The parent wants to teach its child to count. Specifically, the parent will track how many times a button is clicked and pass that number down so the child can render it. The parent's template demonstrates how to bind that data:
Inside the parent-component.html File
<button (click)="teachChildNumbersCounting()">Ask Child</button>
<app-child [clickCounts]="clicks"></app-child>
In this markup, the parent binds its clicks value to the child's clickCounts property. The child then tracks updates to this bound value internally and adjusts its own count. From the child's template, it uses interpolation to display the count.
Curious about the logic that drives this in the parent class? The following code shows how it all ties together:
The Logic Behind the parent-component.ts File
export class Component1 {
clicks = 0;
teachChildNumbersCounting() {
this.clicks++;
}
}
The parent component declares the clicks property, initializing it to 0. Then it defines the teachChildNumbersCounting() method, which is referenced in parent-component.html. Each time the button is clicked, this function increments the clicks value by 1, and the updated value flows down to the child component.
But the parent can only pass data down if the child is ready to receive it. In the child-component.ts file, the crucial step is applying the @Input decorator to the clickCounts property. That single decorator transforms a regular property into a gateway for incoming data. It's at this moment that the connection between parent and child is truly forged, with @Input handling the heavy lifting.
Here's how that looks in the child component's TypeScript file:
The Setup in child-component.ts
@Component({
....
}}
export class childComponent {
@Input() clickCounts: number;
}
With this code in place, the child component is fully equipped to receive and display data. In its own view, it can simply use interpolation with clickCounts to show the count to the user.
Here's the child component's template code:
Displaying Data in child-component.html
<div>
<p>No of clicks from parent's Component - {{clickCounts}}</p>
</div>
Managing Child-to-Parent Data Flow with @Output
The @Output decorator serves the opposite purpose—it lets a child component push events upward to its parent. When you apply this decorator to a property or method inside a child component, that property becomes an event emitter. Triggering that emitter sends data or event notifications to the parent, enabling upward communication between the two layers of the component tree.
Let's revisit the earlier scenario but flip the direction of data flow. Previously, the child was able to show the parent's button click count. Now, imagine the parent needs to display how many times a button inside the child has been clicked.
To set this up, the child component requires some adjustments. Here's what the child's template looks like:
Child Component Template
<div>
<p>No of clicks from parent's Component - {{clickCounts}}</p>
<button (click)='countClicks()'>Click to count from child</button>
</div>
The corresponding logic in the child's TypeScript file is shown next:
Child Component Logic
export class Component2 {
@Output() childToPrentCountClick: EventEmitter<number> = new EventEmitter<number>();
@Input() clickCounts: number;
countClicks() {
this.childToPrentCountClick.emit();
}
}
The event emitter in that child component is named childToPrentCount. It is wired up to react whenever the button is clicked. However, for this to actually work, the parent component must also be modified. The necessary changes are visible in the code below:
Parent Component Template
<button (click)="teachChildNumbersCounting()">Ask Child</button>
<p>No of clicks from child's Component - {{clicks}}</p>
<app-child
[clickCounts]="clicks"
childToPrentCountClick="teachChildNumbersCounting()"
></app-child>
In the parent's template, the function teachChildNumbersCounting() is bound to the childToParentCountClick event—an emitter decorated with @Output in the child. As a result, the button remains in the child component, but all the click handling logic stays in the parent. The child doesn't need to know what happens when its button is pressed.
Wrapping Up
Throughout this discussion, we've seen that @Input moves data from a parent down to a child, while @Output sends events from a child back up to its parent. Using these two decorators in combination creates a robust channel for components to interact and share information within an Angular application.

