Angular decorators attach metadata to classes, fields, and methods, and they are how Angular knows what a component, input, output, or injectable service should do.
Focus on class metadata, property decorators, and how Angular reads the decorator information before the app runs.
A strong understanding of Angular decorators should include what metadata they register and how that metadata changes the runtime contract.
Angular Decorators @Component @Input @Output should be studied as a practical Angular lesson, not as a label. Start by naming the input, the rule that changes the input, and the result a learner should be able to predict after reading the page.
In the angular > important-decorators page, the notes should connect the definition with a working scenario, a mistake that beginners actually make, and the exact check that proves the fix. That makes the topic useful for coding, debugging, and interview revision.
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.
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. |
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().
The class contains data and behavior. The decorator metadata tells Angular how to use that class in an application.
import { Component } from '@angular/core';
@Component({
selector: 'app-message',
standalone: true,
template: `<p>{{ text }}</p>`
})
export class MessageComponent {
text = 'Angular reads the @Component metadata';
}
@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.
Use @Component() for reusable UI parts such as pages, cards, forms, dialogs, dashboards, layouts, navigation bars, and table rows.
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';
}
@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() 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.
Keep pipes focused on presentation. Heavy calculations, API calls, and state updates should usually live in services, computed signals, or component methods instead.
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>
@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() 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() 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.
@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 } 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>();
}
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() 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.
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.
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>();
}
@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() 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() 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.
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().
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();
}
}
@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 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`;
}
}
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. |
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>();
}
| 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 |
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.
const state = { topic: "Angular Decorators @Component @Input @Output", ready: true };
if (state.ready) {
console.log(state.topic + ": render or run the normal path");
}
const response = null;
const message = response?.message ?? "Angular Decorators @Component @Input @Output: show a clear fallback";
console.log(message);
Memorizing Angular Decorators @Component @Input @Output without the situation where it is useful.
Connect Angular Decorators @Component @Input @Output to a concrete Angular task.
Testing Angular Decorators @Component @Input @Output only with the perfect input.
Include empty, missing, duplicate, incompatible, or failed cases when relevant.
Changing code before reading the visible symptom or error message.
Inspect the output, state, configuration, or stack trace connected to Angular Decorators @Component @Input @Output.
Memorizing Angular Decorators @Component @Input @Output without the situation where it is useful.
Connect Angular Decorators @Component @Input @Output to a concrete Angular task.
Angular decorators use TypeScript decorator syntax, but Angular gives specific meaning to decorators such as <code>@Component()</code>, <code>@Injectable()</code>, <code>@Input()</code>, and <code>@Output()</code>.
<code>@Input()</code> is still supported. For new code, Angular documentation recommends the signal-based <code>input()</code> API, but decorator-based inputs remain common and useful.
<code>@ViewChild()</code> queries elements or components from the component's own template. <code>@ContentChild()</code> queries content projected into the component with <code><ng-content></code>.
Use <code>@Injectable()</code> when Angular needs to put a class into its dependency injection graph. With <code>providedIn: 'root'</code>, Angular creates one application-wide instance and reuses it wherever that service is injected.
Explore 500+ free tutorials across 20+ languages and frameworks.