Angular Components

Angular is a component-based framework, meaning that applications are structured as a hierarchy of components. Components encapsulate HTML templates, CSS styles, and TypeScript logic, allowing developers to build modular, reusable, and maintainable user interfaces.

Understanding components is essential for any Angular developer, from beginners creating their first SPA to experts building large-scale enterprise applications. This article explores everything about Angular components—from basic creation to advanced concepts like communication, lifecycle hooks, and best practices.


What is an Angular Component?

A component in Angular is a TypeScript class that:

  1. Contains application logic (methods and properties).
  2. Uses a template to define its view (HTML).
  3. Can include styles for encapsulated CSS.
  4. Is reusable across the application.

Angular components follow the Model-View-Controller (MVC) pattern by combining data (model) and presentation (view) with logic (controller).


Creating an Angular Component

Angular CLI makes component creation simple.

Step 1: Generate a Component

ng generate component header

or shorthand:

ng g c header

This creates the following files:

src/app/header/
 ├─ header.component.ts
 ├─ header.component.html
 ├─ header.component.css
 └─ header.component.spec.ts
  • .ts → Component logic
  • .html → Template
  • .css → Styles
  • .spec.ts → Unit tests

Step 2: Basic Component Example

header.component.ts:

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

@Component({
  selector: 'app-header',
  template: <h1>{{ title }}</h1>,
  styleUrls: ['./header.component.css']
})
export class HeaderComponent {
  title = 'Welcome';
}
  • @Component decorator tells Angular this is a component.
  • selector is the HTML tag used to include this component elsewhere: <app-header></app-header>
  • template contains inline HTML (or use templateUrl for a separate file).
  • styleUrls links component-specific styles.

Component Templates

Templates define the UI of a component. Angular provides data binding to connect the component class with the template.

Types of Data Binding

  1. Interpolation ({{ property }})
    Example:
<p>{{ title }}</p>
  1. Property Binding ([property]="value")
    Example:
<img [src]="imageUrl" alt="Image"/>
  1. Event Binding ((event)="handler()")
    Example:
<button (click)="onClick()">Click Me</button>
  1. Two-Way Binding ([(ngModel)]="property")
    Example:
<input [(ngModel)]="title" placeholder="Edit title"/>
<p>Your title: {{ title }}</p>

Component Styles

Components can have encapsulated CSS, meaning styles do not leak outside the component.

header.component.css:

h1 {
  color: #2c3e50;
  font-family: Arial, sans-serif;
  text-align: center;
}
  • Angular View Encapsulation ensures that these styles only apply to HeaderComponent.
  • Options: Emulated (default), None, ShadowDom.

Component Lifecycle

Angular components go through lifecycle hooks, allowing developers to run code at specific stages.

Key Lifecycle Hooks

HookPurpose
ngOnInit()Called after the component is initialized
ngOnChanges()Called when input properties change
ngDoCheck()Detects and acts on changes manually
ngAfterViewInit()Runs after the component’s view is initialized
ngOnDestroy()Cleanup before the component is destroyed

Example:

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

@Component({
  selector: 'app-header',
  template: &lt;h1&gt;{{ title }}&lt;/h1&gt;
})
export class HeaderComponent implements OnInit, OnDestroy {
  title = 'Welcome';

  ngOnInit() {
console.log('HeaderComponent initialized');
} ngOnDestroy() {
console.log('HeaderComponent destroyed');
} }

Input and Output Properties

Components can receive data from a parent component using @Input() and emit events using @Output().

Parent to Child Communication

child.component.ts:

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

@Component({
  selector: 'app-child',
  template: &lt;p&gt;{{ message }}&lt;/p&gt;
})
export class ChildComponent {
  @Input() message: string = '';
}

parent.component.html:

<app-child [message]="'Hello from Parent'"></app-child>

Child to Parent Communication

child.component.ts:

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

@Component({
  selector: 'app-child',
  template: &lt;button (click)="sendMessage()"&gt;Send&lt;/button&gt;
})
export class ChildComponent {
  @Output() messageEvent = new EventEmitter<string>();

  sendMessage() {
this.messageEvent.emit('Hello Parent!');
} }

parent.component.html:

<app-child (messageEvent)="receiveMessage($event)"></app-child>
<p>{{ parentMessage }}</p>

parent.component.ts:

parentMessage: string = '';

receiveMessage(msg: string) {
  this.parentMessage = msg;
}

Nested Components

Angular allows nesting components to build complex UIs.

Example:

<app-header></app-header>
<app-child></app-child>
<app-footer></app-footer>
  • Each component handles its own logic and view.
  • Promotes modularity and reusability.

Dynamic Components

Angular supports dynamic component creation at runtime using ViewContainerRef and ComponentFactoryResolver.

Example:

import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { ChildComponent } from './child/child.component';

@Component({
  selector: 'app-dynamic',
  template: &lt;ng-container #container&gt;&lt;/ng-container&gt;
})
export class DynamicComponent {
  @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;

  constructor(private resolver: ComponentFactoryResolver) {}

  loadChild() {
const factory = this.resolver.resolveComponentFactory(ChildComponent);
this.container.createComponent(factory);
} }

Component Interaction Patterns

1. Service-Based Communication

Services can store shared state and allow multiple components to access it.

@Injectable({ providedIn: 'root' })
export class DataService {
  data: string = 'Shared Data';
}

Components can inject this service to access or update data.

2. Observable Patterns with RxJS

Use BehaviorSubject to share reactive data:

import { BehaviorSubject } from 'rxjs';

export class DataService {
  private dataSubject = new BehaviorSubject<string>('Initial Data');
  data$ = this.dataSubject.asObservable();

  updateData(value: string) {
this.dataSubject.next(value);
} }

Component Best Practices

  1. Single Responsibility: Each component should have a clear, specific purpose.
  2. Keep Components Small: Avoid large, monolithic components.
  3. Use Inputs and Outputs: Prefer property and event bindings for communication.
  4. Encapsulate Styles: Use component-specific CSS for maintainability.
  5. Leverage Lifecycle Hooks: Manage initialization and cleanup efficiently.
  6. Reuse Components: Build a library of reusable components for your app.

Advanced Component Features

  1. Content Projection with <ng-content>
@Component({
  selector: 'app-card',
  template: `
&lt;div class="card"&gt;
  &lt;ng-content&gt;&lt;/ng-content&gt;
&lt;/div&gt;
` }) export class CardComponent {}

Usage:

<app-card>
  <h2>Title</h2>
  <p>Some content inside the card.</p>
</app-card>
  1. Structural Directives in Components
  • *ngIf, *ngFor to control rendering dynamically.
  1. Host Binding and Host Listener
@HostBinding('class.active') isActive = false;
@HostListener('click') toggle() { this.isActive = !this.isActive; }
  • Dynamically bind classes and listen to DOM events on the host element.

Testing Components

Angular uses Karma and Jasmine for testing components.

Example test:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HeaderComponent } from './header.component';

describe('HeaderComponent', () => {
  let component: HeaderComponent;
  let fixture: ComponentFixture<HeaderComponent>;

  beforeEach(async () => {
await TestBed.configureTestingModule({ declarations: &#91;HeaderComponent] }).compileComponents();
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}); it('should create', () => {
expect(component).toBeTruthy();
}); it('should render title', () => {
const compiled = fixture.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome');
}); });
  • Test creation, property binding, and template rendering.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *