Back to blog
Mar 07, 2025
10 min read

Angular Decorator @Component

The @Component decorator in Angular is used to mark a class as an Angular component and provide configuration metadata that determines how the component should be processed, instantiated, and used at runtime. It plays a crucial role in building Angular applications by defining the building blocks of the user interface.

Example of using the @Component decorator can be found in a project that implements a dashboard with multiple widgets. Each widget can be a separate Angular component defined using the @Component decorator.

For instance, consider a WeatherWidgetComponent that displays the current weather information for a given location:

import { Component, Input } from '@angular/core';
import { WeatherService } from '../weather.service';
import { Weather } from '../weather.model';

@Component({
  selector: 'app-weather-widget',
  template: `
    <div *ngIf="weather$ | async as weather">
      <h3>{{ weather.location }}</h3>
      <p>{{ weather.temperature }}°C</p>
      <p>{{ weather.description }}</p>
    </div>
  `,
  styles: [`
    div {
      border: 1px solid #ccc;
      padding: 10px;
      margin-bottom: 10px;
    }
  `]
})
export class WeatherWidgetComponent {
  @Input() location: string;
  weather$ = this.weatherService.getWeather(this.location);

  constructor(private weatherService: WeatherService) {}
}

In this example, the @Component decorator is used to define the WeatherWidgetComponent with a custom HTML tag (app-weather-widget), an inline template, and CSS styles. The component takes a location input and uses an injected WeatherService to fetch the weather information.

In Angular, change detection is the process of detecting and handling changes in the component’s data and view bindings. When a component is instantiated, Angular automatically creates a change detector for it. This change detector is responsible for tracking and propagating changes in the component’s bindings to ensure that the view remains up-to-date with the underlying data.

The change detection process can be configured using the ChangeDetectionStrategy enum, which provides two primary strategies: ChangeDetectionStrategy.OnPush and ChangeDetectionStrategy.Default. These strategies determine how and when the change detector checks for changes in the component’s bindings.

  1. ChangeDetectionStrategy.OnPush:

When using the ChangeDetectionStrategy.OnPush strategy, the change detector is set to check for changes only when the component’s input properties change. This strategy is also known as “on-demand” change detection. It is more performant than the default strategy because it reduces the number of unnecessary change detection cycles.

In other words, when a component is marked with ChangeDetectionStrategy.OnPush, Angular checks the input properties for changes only once when the component is created. After that, Angular will only check the input properties again when an event is triggered, such as an input property being updated, or when a user interaction occurs (e.g., a button click or form submission).

To use the ChangeDetectionStrategy.OnPush strategy, you can set the changeDetection property in the @Component decorator. You can optimize application performance by using the ChangeDetectionStrategy.OnPush strategy when creating components. This strategy allows Angular to skip checking for changes when the input properties don’t change, resulting in fewer change detection cycles and better performance.

Here’s an example of using ChangeDetectionStrategy.OnPush for the WeatherWidgetComponent:

import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
import { WeatherService } from '../weather.service';
import { Weather } from '../weather.model';

@Component({
  selector: 'app-weather-widget',
  template: `
    <div *ngIf="weather$ | async as weather">
      <h3>{{ weather.location }}</h3>
      <p>{{ weather.temperature }}°C</p>
      <p>{{ weather.description }}</p>
    </div>
  `,
  styles: [`
    div {
      border: 1px solid #ccc;
      padding: 10px;
      margin-bottom: 10px;
    }
  `],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class WeatherWidgetComponent {
  @Input() location: string;
  weather$ = this.weatherService.getWeather(this.location);

  constructor(private weatherService: WeatherService) {}
}
  1. ChangeDetectionStrategy.Default:

When using the ChangeDetectionStrategy.Default strategy, the change detector checks for changes in the component’s bindings after every asynchronous event, such as a timer or an XHR request. This strategy is also known as “check-always” change detection.

In other words, with the ChangeDetectionStrategy.Default strategy, Angular checks the component’s bindings after every asynchronous event, even if the component’s input properties have not changed. This strategy is less performant than ChangeDetectionStrategy.OnPush because it can result in unnecessary change detection cycles.

In Angular, viewProviders are used to define a set of injectable objects that are visible to the component’s view DOM children. By configuring viewProviders in a component, you can provide a specific instance of a service that will be shared among the component and its view children.

When Angular creates a component, it also creates a hierarchy of injectors. The component’s injector is responsible for creating instances of the component’s dependencies, and it is also responsible for creating injectors for the component’s child components.

The viewProviders property allows you to define a set of providers that are visible only to the component’s view children. When Angular creates an instance of a child component, it will first look for a provider in the child component’s providers array. If it does not find a provider there, Angular will continue to look for a provider in the parent component’s viewProviders array.

Here’s an example of using viewProviders to provide a specific instance of a service to a component and its view children:

import { Component, ViewChildren, QueryList, ViewChild, AfterViewInit, ElementRef } from '@angular/core';
import { SomeService } from './some.service';

@Component({
  selector: 'app-parent',
  template: `
    <app-child></app-child>
    <app-child></app-child>
  `,
  styleUrls: ['./parent.component.css'],
  providers: [],
  viewProviders: [
    { provide: SomeService, useClass: SomeServiceInstance }
  ]
})
export class ParentComponent implements AfterViewInit {
  constructor() {}

  ngAfterViewInit() {
    // You can access the shared instance of SomeService here
  }
}

@Component({
  selector: 'app-child',
  template: `
    <div #childElement></div>
  `,
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements AfterViewInit {
  @ViewChildren('childElement') childElements: QueryList<ElementRef>;
  @ViewChild('childElement', { static: false }) childElement: ElementRef;

  constructor(private someService: SomeService) {}

  ngAfterViewInit() {
    // You can access the shared instance of SomeService here
  }
}

In this example, the ParentComponent has two child components, ChildComponent. The ParentComponent defines a viewProviders array that includes a provider for SomeService with a specific instance of the service, SomeServiceInstance.

When Angular creates an instance of the ChildComponent, it will first look for a provider for SomeService in the ChildComponent’s providers array. If it does not find one there, Angular will continue to look for a provider in the ParentComponent’s viewProviders array. Since the ParentComponent has defined a provider for SomeService, Angular will use the instance of SomeServiceInstance to create an instance of SomeService that will be shared among the ChildComponent instances.

By using viewProviders, you can ensure that a specific instance of a service is shared among the component and its view children. This can be useful when you want to share state or behavior between components without making the service available to the entire application.

To optimize system performance with viewProviders is to use them to provide a shared service to a group of components that are related to each other. By doing so, you can reduce the number of instances of the service that are created and shared among components, which can improve the overall performance of the application.

Here’s an example of using viewProviders to provide a shared service to a group of components:

import { Component, ViewChildren, QueryList, ViewChild, AfterViewInit, ElementRef } from '@angular/core';
import { SomeSharedService } from './some-shared.service';

@Component({
  selector: 'app-parent',
  template: `
    <app-child1></app-child1>
    <app-child2></app-child2>
    <app-child3></app-child3>
  `,
  styleUrls: ['./parent.component.css'],
  viewProviders: [
    SomeSharedService
  ]
})
export class ParentComponent {
  constructor() {}
}

@Component({
  selector: 'app-child1',
  template: `
    <div>Child 1</div>
  `,
  styleUrls: ['./child1.component.css']
})
export class Child1Component {
  constructor(private someSharedService: SomeSharedService) {}
}

@Component({
  selector: 'app-child2',
  template: `
    <div>Child 2</div>
  `,
  styleUrls: ['./child2.component.css']
})
export class Child2Component {
  constructor(private someSharedService: SomeSharedService) {}
}

@Component({
  selector: 'app-child3',
  template: `
    <div>Child 3</div>
  `,
  styleUrls: ['./child3.component.css']
})
export class Child3Component {
  constructor(private someSharedService: SomeSharedService) {}
}

In this example, the ParentComponent defines a viewProviders array that includes a provider for SomeSharedService. This means that the SomeSharedService will be shared among the Child1Component, Child2Component, and Child3Component instances.

When Angular creates an instance of the Child1Component, Child2Component, or Child3Component, it will first look for a provider for SomeSharedService in the component’s providers array. If it does not find one there, Angular will continue to look for a provider in the ParentComponent’s viewProviders array. Since the ParentComponent has defined a provider for SomeSharedService, Angular will use the same instance of SomeSharedService to create instances of SomeSharedService that will be shared among the Child1Component, Child2Component, and Child3Component instances.

By using viewProviders to provide a shared service to a group of components, you can reduce the number of instances of the service that are created and shared among components, which can improve the overall performance of the application. This can be especially important in large-scale applications where performance is a critical factor.

In Angular, encapsulation refers to the way in which component styles are scoped to a specific component and its view hierarchy. Encapsulation is important in large-scale applications because it helps to avoid naming collisions and makes it easier to maintain and manage styles.

The ViewEncapsulation enum in Angular provides three different encapsulation policies that can be applied to a component:

  1. ViewEncapsulation.Emulated: This is the default encapsulation policy in Angular. It emulates the behavior of native Shadow DOM by applying a modified version of the component’s styles to the component’s view hierarchy. This ensures that the component’s styles are not leaked to the global scope and do not interfere with styles in other components. However, it does not provide the same level of encapsulation as native Shadow DOM.
  2. ViewEncapsulation.None: This policy applies the component’s styles globally without any encapsulation. This means that the component’s styles are applied to the entire document and can interfere with styles in other components. This policy is not recommended for large-scale applications because it can lead to naming collisions and make it difficult to maintain and manage styles.
  3. ViewEncapsulation.ShadowDom: This policy uses the browser’s native Shadow DOM API to encapsulate styles. Shadow DOM is a native web standard that provides a way to encapsulate styles and markup within a component’s view hierarchy. This policy provides the highest level of encapsulation and ensures that the component’s styles are completely isolated from styles in other components. However, it is not supported by all browsers and can lead to performance issues in some cases.

The ViewEncapsulation policy can be set in the @Component decorator using the encapsulation property. For example:

import { Component, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'app-example',
  template: `
    <div class="example">Example Component</div>
  `,
  styleUrls: ['./example.component.css'],
  encapsulation: ViewEncapsulation.Emulated
})
export class ExampleComponent {}

In this example, the ExampleComponent is using the default ViewEncapsulation.Emulated policy. This means that the component’s styles will be emulated to provide a level of encapsulation that is similar to native Shadow DOM.

The ViewEncapsulation policy can be useful in large-scale applications where there are many components with complex styles. By using encapsulation, you can ensure that the styles for each component are isolated and do not interfere with each other. This can help to reduce naming collisions and make it easier to maintain and manage styles.

For example, in a large-scale application with many components that have complex styles, you might use ViewEncapsulation.ShadowDom to provide the highest level of encapsulation. However, this policy is not supported by all browsers and can lead to performance issues in some cases.

On the other hand, if you have a component that has no styles or styleUrls, you might use ViewEncapsulation.None to avoid the overhead of encapsulation. This can improve the performance of the component, but it can also lead to naming collisions and make it difficult to maintain and manage styles.

In general, the ViewEncapsulation policy should be chosen based on the specific needs of the application and the component. By understanding the different encapsulation policies and how they affect component styles.