Ad

Decorators in Angular

In Angular, decorators are a core part of how the framework works. Whether you're building components, services, directives, or pipes—you're using decorators all the time. But what are they exactly, and how do they work?


What Is a Decorator?

A decorator is a special kind of function in TypeScript that can be attached to a class, method, property, or parameter. When used, it adds metadata or behavior to the thing it's decorating. In Angular, decorators help the framework understand how to handle your code—for example, how to render a component, how to inject a service, or how to handle events.

All Angular decorators start with the @ symbol and are imported from Angular’s core libraries like @angular/core.


Common Angular Decorators

@Component

This marks a class as an Angular component and tells Angular how to use it in the DOM.

tsimport { Component } from '@angular/core';

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
})
export class UserComponent {
  name = 'John';
}

What it does: Angular knows this class is a component, and it should be rendered wherever <app-user> is used in HTML.


@NgModule

This defines a module, which is a group of related components, directives, pipes, and services.

tsimport { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  bootstrap: [AppComponent],
})
export class AppModule {}


@Injectable

Used to mark a class as available for dependency injection (DI).

tsimport { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  login() {
    // logic here
  }
}

Note: providedIn: 'root' makes the service available application-wide and tree-shakable.


@Input and @Output

Used in components to allow data to be passed between parent and child components.

tsimport { Input, Output, EventEmitter, Component } from '@angular/core';

@Component({
  selector: 'app-user-card',
  template: '<div>{{ name }}</div>',
})
export class UserCardComponent {
  @Input() name: string = '';
  @Output() selected = new EventEmitter<string>();

  selectUser() {
    this.selected.emit(this.name);
  }
}


@HostListener and @HostBinding

These decorators are used in directives or components to listen to events and bind to host element properties.

tsimport { Directive, HostListener, HostBinding } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  @HostBinding('style.backgroundColor') bg = '';

  @HostListener('mouseenter') onMouseEnter() {
    this.bg = 'yellow';
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.bg = '';
  }
}

This directive highlights the element when hovered.


Custom Decorators in Angular

Angular gives you powerful built-in decorators, but you can also create your own. Custom decorators are useful for logging, validation, caching, role-based control, and more. They are just TypeScript functions with a special signature.

Example 1: Method Logging Decorator

tsexport function LogMethod() {
  return function (target: any, key: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value;

    descriptor.value = function (...args: any[]) {
      console.log(\`[Log] \${key} called with:\`, args);
      return original.apply(this, args);
    };

    return descriptor;
  };
}

Usage:

tsexport class UserService {
  @LogMethod()
  getUser(id: number) {
    return { id, name: 'Test User' };
  }
}

Every time getUser is called, a log message is printed with the method name and arguments.


Example 2: Required Property Decorator

This custom property decorator throws an error if a required property is set to null or undefined.

tsexport function Required(target: any, propertyKey: string) {
  let value: any;

  const getter = () => value;
  const setter = (newVal: any) => {
    if (newVal === null || newVal === undefined) {
      throw new Error(\`Property "\${propertyKey}" is required.\`);
    }
    value = newVal;
  };

  Object.defineProperty(target, propertyKey, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true,
  });
}

Usage:

tsexport class Product {
  @Required
  name: string;

  constructor(name: string) {
    this.name = name;
  }
}

If someone creates a Product with null or undefined as the name, an error will be thrown immediately.


Key Takeaways

  • Decorators are a core part of Angular’s architecture. They add metadata that tells Angular how to treat your classes and properties.
  • They are powered by TypeScript’s experimental decorator feature.
  • You can create custom decorators to reuse logic like logging or validation.
  • Use decorators to keep your code cleaner, more declarative, and easier to maintain.