Angular Decorators — @Component, @Input, @Output
Angular Important Decorators
Decorators are TypeScript features that Angular uses to attach metadata to classes, properties, methods, and constructor parameters. A decorator starts with @ and is placed immediately before the thing it describes. In Angular, decorators tell the framework what a class is, how it should be compiled, how it participates in dependency injection, and how it connects to templates.
For example, @Component() tells Angular that a class controls a UI view. @Injectable() tells Angular that a class can participate in dependency injection. @Input() allows a parent component to pass data into a child component. @HostListener() connects a class method to a DOM event on the host element.
Decorator Categories
Angular decorators become much easier to understand when you group them by the kind of code they decorate.
| Category | Decorators | Main Purpose |
|---|---|---|
| Class decorators | @Component, @Directive, @Pipe, @Injectable, @NgModule | Tell Angular what role a class plays. |
| Property decorators | @Input, @Output, @HostBinding, @ViewChild, @ViewChildren, @ContentChild, @ContentChildren | Configure data flow, host bindings, and template queries. |
| Method decorators | @HostListener | Run a method when a host event happens. |
| Parameter decorators | @Inject, @Optional, @Self, @SkipSelf, @Host | Control dependency injection lookup behavior. |
How Angular Uses Decorator Metadata
Angular does not use decorators as decoration only. The Angular compiler reads decorator metadata and uses it to generate efficient code for templates, dependency injection, change detection, queries, and component rendering. This is why a plain TypeScript class is not automatically an Angular component. Angular needs the metadata from @Component().
import { Component } from '@angular/core';
@Component({
selector: 'app-message',
standalone: true,
template: `<p>{{ text }}</p>`
})
export class MessageComponent {
text = 'Angular reads the @Component metadata';
}
The class contains data and behavior. The decorator metadata tells Angular how to use that class in an application.
@Component()
@Component() is the most important Angular decorator. It declares a class as a component and provides metadata such as selector, template, styleUrl, imports, providers, and change detection settings. A component always has a template because it owns a section of the UI.
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'app-profile-card',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<article class="profile-card">
<h2>{{ name }}</h2>
<p>{{ role }}</p>
</article>
`
})
export class ProfileCardComponent {
name = 'Aarav';
role = 'Frontend Developer';
}
Use @Component() for reusable UI parts such as pages, cards, forms, dialogs, dashboards, layouts, navigation bars, and table rows.
@Directive()
@Directive() declares a class as a directive. A directive adds behavior to an existing element. Components are technically directives with templates, but a plain directive does not own its own template. Directives are perfect for reusable DOM behavior such as autofocus, permissions, validation styling, keyboard shortcuts, and hover effects.
import { AfterViewInit, Directive, ElementRef, inject } from '@angular/core';
@Directive({
selector: '[appAutofocus]',
standalone: true
})
export class AutofocusDirective implements AfterViewInit {
private elementRef = inject(ElementRef<HTMLInputElement>);
ngAfterViewInit() {
this.elementRef.nativeElement.focus();
}
}
<input appAutofocus placeholder="Search users" />
@Pipe()
@Pipe() declares a class as a pipe. Pipes transform values for display in templates. Angular includes built-in pipes such as date, currency, uppercase, and json. Custom pipes are useful for repeated display formatting.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'fileSize',
standalone: true
})
export class FileSizePipe implements PipeTransform {
transform(bytes: number): string {
if (bytes < 1024) {
return `${bytes} B`;
}
return `${(bytes / 1024).toFixed(1)} KB`;
}
}
<p>Attachment size: {{ attachmentSize | fileSize }}</p>
Keep pipes focused on presentation. Heavy calculations, API calls, and state updates should usually live in services, computed signals, or component methods instead.
@Injectable()
@Injectable() marks a class as available to Angular's dependency injection system. Services commonly use this decorator for API communication, shared state, authentication, logging, caching, and business logic. The most common configuration is providedIn: 'root', which registers a singleton service for the whole application.
import { Injectable, signal } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class UserService {
private readonly _users = signal(['Asha', 'Rohan', 'Meera']);
readonly users = this._users.asReadonly();
addUser(name: string) {
this._users.update(users => [...users, name]);
}
}
import { Component, inject } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-user-list',
standalone: true,
template: `
@for (user of userService.users(); track user) {
<p>{{ user }}</p>
}
`
})
export class UserListComponent {
readonly userService = inject(UserService);
}
@NgModule()
@NgModule() defines a module that groups components, directives, pipes, providers, and imported modules. Modern Angular applications often use standalone components and route-level configuration instead of NgModules, but @NgModule() remains important because many existing Angular applications still use module-based architecture.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AdminDashboardComponent } from './admin-dashboard.component';
@NgModule({
declarations: [AdminDashboardComponent],
imports: [CommonModule],
exports: [AdminDashboardComponent]
})
export class AdminModule {}
@Input()
@Input() exposes a component or directive property so a parent template can bind to it. Inputs are how data flows into a child component. Modern Angular recommends the signal-based input() function for new projects, but decorator-based @Input() remains fully supported and is still common in existing codebases.
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
template: `
<h3>{{ name }}</h3>
<p>{{ role }}</p>
`
})
export class UserCardComponent {
@Input({ required: true }) name = '';
@Input() role = 'Member';
}
<app-user-card
[name]="currentUser.name"
[role]="currentUser.role" />
import { Component, input } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
template: `<h3>{{ name() }}</h3>`
})
export class UserCardComponent {
name = input.required<string>();
}
@Input() can also define aliases and transforms. Use aliases carefully because they create a different public template name from the internal class property name.
import { Component, Input, booleanAttribute } from '@angular/core';
@Component({
selector: 'app-alert',
standalone: true,
template: `<p [class.important]="important">{{ message }}</p>`
})
export class AlertComponent {
@Input({ alias: 'alertMessage' }) message = '';
@Input({ transform: booleanAttribute }) important = false;
}
@Output()
@Output() exposes a custom event from a child component to its parent. It is commonly used for button clicks, row selection, form submission, closing modals, and notifying a parent that something changed. Modern Angular also supports the signal-friendly output() function.
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-confirm-button',
standalone: true,
template: `<button (click)="confirmed.emit()">Confirm</button>`
})
export class ConfirmButtonComponent {
@Output() confirmed = new EventEmitter<void>();
}
<app-confirm-button (confirmed)="saveChanges()" />
import { Component, output } from '@angular/core';
@Component({
selector: 'app-confirm-button',
standalone: true,
template: `<button (click)="confirmed.emit()">Confirm</button>`
})
export class ConfirmButtonComponent {
confirmed = output<void>();
}
Avoid output names that collide with native DOM events such as click, change, or input. Use names that describe the business event, such as saved, closed, selected, or submitted.
@HostBinding()
@HostBinding() binds a directive or component property to the host element. It can bind classes, styles, attributes, and DOM properties. This decorator is especially common in attribute directives.
import { Directive, HostBinding, Input } from '@angular/core';
@Directive({
selector: '[appStatus]',
standalone: true
})
export class StatusDirective {
@Input() appStatus: 'success' | 'warning' | 'danger' = 'success';
@HostBinding('class.is-success')
get isSuccess() {
return this.appStatus === 'success';
}
@HostBinding('attr.aria-live')
ariaLive = 'polite';
}
<p [appStatus]="'warning'">Your password will expire soon.</p>
@HostListener()
@HostListener() listens for an event on the host element and calls a method when that event occurs. It can also listen to global targets such as window, document, and body. If the handler returns false, Angular prevents the default browser behavior for that event.
import { Directive, EventEmitter, HostListener, Output } from '@angular/core';
@Directive({
selector: '[appEscapeClose]',
standalone: true
})
export class EscapeCloseDirective {
@Output() escapePressed = new EventEmitter<void>();
@HostListener('document:keydown.escape', ['$event'])
onEscape(event: KeyboardEvent) {
event.stopPropagation();
this.escapePressed.emit();
}
}
@ViewChild() and @ViewChildren()
@ViewChild() and @ViewChildren() query elements, directives, or child components from the component's own template. Use @ViewChild() for the first matching item and @ViewChildren() for a list of matches.
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-search-panel',
standalone: true,
template: `
<input #searchInput placeholder="Search" />
<button (click)="focusSearch()">Focus</button>
`
})
export class SearchPanelComponent implements AfterViewInit {
@ViewChild('searchInput') searchInput?: ElementRef<HTMLInputElement>;
ngAfterViewInit() {
console.log('Input is available after the view initializes');
}
focusSearch() {
this.searchInput?.nativeElement.focus();
}
}
import { Component, ElementRef, viewChild } from '@angular/core';
@Component({
selector: 'app-search-panel',
standalone: true,
template: `<input #searchInput placeholder="Search" />`
})
export class SearchPanelComponent {
searchInput = viewChild<ElementRef<HTMLInputElement>>('searchInput');
focusSearch() {
this.searchInput()?.nativeElement.focus();
}
}
Prefer normal template binding for most UI updates. Use view queries when you truly need access to a child component instance, directive instance, or DOM element API such as focus().
@ContentChild() and @ContentChildren()
@ContentChild() and @ContentChildren() query content projected into a component with <ng-content>. They are useful when building reusable wrappers such as tabs, accordions, cards, form fields, and layout components.
import { AfterContentInit, Component, ContentChild, Directive } from '@angular/core';
@Directive({
selector: '[appPanelTitle]',
standalone: true
})
export class PanelTitleDirective {}
@Component({
selector: 'app-panel',
standalone: true,
imports: [PanelTitleDirective],
template: `
<section class="panel">
<ng-content />
</section>
`
})
export class PanelComponent implements AfterContentInit {
@ContentChild(PanelTitleDirective) title?: PanelTitleDirective;
ngAfterContentInit() {
console.log('Projected title directive:', this.title);
}
}
<app-panel>
<h2 appPanelTitle>Account Settings</h2>
<p>Projected panel body content</p>
</app-panel>
Parameter Decorators for Dependency Injection
Parameter decorators change how Angular resolves dependencies in constructors. They are less common in new code that uses the inject() function, but they are still important in many Angular projects.
| Decorator | Meaning | Common Use |
|---|---|---|
@Inject() | Use a specific injection token. | Inject values that are not classes, such as config objects. |
@Optional() | Allow the dependency to be missing. | Reusable components/directives with optional parent services. |
@Self() | Search only the current injector. | Require a dependency from the local element/provider. |
@SkipSelf() | Start searching from the parent injector. | Override or extend parent services. |
@Host() | Limit lookup at the host boundary. | Directive/component relationships. |
import { InjectionToken } from '@angular/core';
export interface AppConfig {
apiUrl: string;
}
export const APP_CONFIG = new InjectionToken<AppConfig>('APP_CONFIG');
import { Inject, Injectable, Optional } from '@angular/core';
import { APP_CONFIG, AppConfig } from './tokens';
@Injectable()
export class ApiService {
constructor(
@Inject(APP_CONFIG) private config: AppConfig,
@Optional() private logger?: Console
) {}
get usersUrl() {
return `${this.config.apiUrl}/users`;
}
}
Decorator APIs vs Signal-Based APIs
Modern Angular includes function-based APIs that reduce the need for some traditional property decorators in new code. These APIs are compiler-aware and usually integrate better with signals. The decorator APIs are still valid and widely used, so professional Angular developers should understand both styles.
| Decorator API | Modern Function API | Use For |
|---|---|---|
@Input() | input(), input.required() | Receiving values from a parent. |
@Output() | output() | Emitting custom component events. |
| Two-way input plus output | model() | Creating two-way bindable component values. |
@ViewChild(), @ViewChildren() | viewChild(), viewChildren() | Querying a component's own template. |
@ContentChild(), @ContentChildren() | contentChild(), contentChildren() | Querying projected content. |
Real-World Example: Decorators Working Together
In real applications, decorators are rarely isolated. A reusable component may use @Component(), @Input(), @Output(), and a service marked with @Injectable(). A directive may combine @Directive(), @Input(), @HostBinding(), and @HostListener().
import { Component, EventEmitter, Input, Output } from '@angular/core';
export interface Task {
id: number;
title: string;
completed: boolean;
}
@Component({
selector: 'app-task-card',
standalone: true,
template: `
<article [class.done]="task.completed">
<h3>{{ task.title }}</h3>
<button (click)="toggle.emit(task.id)">
{{ task.completed ? 'Reopen' : 'Complete' }}
</button>
</article>
`
})
export class TaskCardComponent {
@Input({ required: true }) task!: Task;
@Output() toggle = new EventEmitter<number>();
}
When to Use Which Decorator
| Need | Use |
|---|---|
| Create a UI block with its own template | @Component() |
| Add reusable behavior to an existing element | @Directive() |
| Format a value in a template | @Pipe() |
| Create an injectable service | @Injectable() |
| Receive data from a parent | @Input() or input() |
| Emit an event to a parent | @Output() or output() |
| Bind class, style, property, or attribute on the host | @HostBinding() |
| Listen to a host or global event | @HostListener() |
| Access a child element/component from your own template | @ViewChild(), @ViewChildren(), or function query APIs |
| Access projected content | @ContentChild(), @ContentChildren(), or function query APIs |
Conclusion
Angular decorators are the connection point between plain TypeScript classes and the Angular framework. They define components, directives, pipes, services, modules, data inputs, custom outputs, host behavior, template queries, content queries, and special dependency injection rules.
For beginners, the best path is to master @Component(), @Input(), @Output(), and @Injectable() first. Then learn directives with @HostBinding() and @HostListener(), followed by query decorators and dependency injection decorators. Once you understand what each decorator tells Angular, the framework becomes much easier to reason about.
@Input() data: User;
@Input({ required: true }) data!: User;
@Output() click = new EventEmitter<void>();
@Output() saved = new EventEmitter<void>();
@ViewChild() for every UI change
Use bindings first; query only when you need an instance or DOM API.
Put API calls in a pipe transform()
Put API calls in a service marked with @Injectable().
- Angular decorators provide metadata that the compiler and runtime use to understand your code.
-
@Component(),@Directive(),@Pipe(),@Injectable(), and@NgModule()are class decorators. -
@Input()receives data from a parent;@Output()sends custom events back to a parent. -
@HostBinding()and@HostListener()connect directive/component code to the host element. -
@ViewChild()queries your component view, while@ContentChild()queries projected content. -
Modern Angular also offers
input(),output(),model(),viewChild(), andcontentChild()for signal-friendly component authoring.
Frequently Asked Questions
Level Up Your Angular Skills
Master Angular with these hand-picked resources