Angular Component

Advanced Angular component development focusing on automated UI patterns and modular system integration

Angular Component is a community skill for building reusable Angular components, covering standalone components, input/output bindings, content projection, template syntax, lifecycle hooks, and component design patterns for Angular applications.

What Is This?

Overview

Angular Component provides patterns for creating well-designed Angular components. It covers standalone components that declare their own imports without NgModule boilerplate, input and output bindings with signal-based inputs and typed event emitters, content projection using ng-content with select attributes for flexible component composition, template syntax with control flow blocks, template variables, and pipes, and lifecycle management with hooks for initialization, change detection, and cleanup. The skill enables developers to build composable, reusable UI components that follow Angular best practices.

Who Should Use This

This skill serves Angular developers building component libraries and design systems, teams creating reusable UI components for multi-application sharing, and engineers adopting standalone components and signal-based inputs.

Why Use It?

Problems It Solves

NgModule-based components require excessive boilerplate for declarations and imports. Traditional @Input decorators lack required input enforcement and transform capabilities. Content projection patterns are underused leaving components rigid and difficult to customize. Change detection issues cause performance problems in component trees with frequent updates.

Core Highlights

Standalone components eliminate NgModule dependency for simpler project structure. Signal inputs provide reactive, type-safe property binding with required and default options. Content projection slots enable flexible component composition with ng-content. OnPush change detection reduces rendering work for stable component trees.

How to Use It?

Basic Usage

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

@Component({
  selector: 'app-card',
  standalone: true,
  template: `
    <div class="card">
      <header>
        <h3>{{ title() }}</h3>
        @if (subtitle()) {
          <p>{{ subtitle() }}</p>
        }
      </header>
      <div class="body">
        <ng-content />
      </div>
      <footer>
        <ng-content
          select="[actions]" />
      </footer>
    </div>
  `,
  changeDetection:
    ChangeDetectionStrategy
      .OnPush,
})
export class CardComponent {
  title = input.required<
    string>();
  subtitle = input<string>();
  closed = output<void>();
}

// Usage in parent template
// <app-card [title]="'Hello'">
//   <p>Body content</p>
//   <div actions>
//     <button>OK</button>
//   </div>
// </app-card>

Real-World Examples

import { Component, input,
  computed, signal }
  from '@angular/core';

@Component({
  selector: 'app-data-table',
  standalone: true,
  imports: [NgFor],
  template: `
    <table>
      <thead>
        <tr>
          @for (col of columns();
              track col.key) {
            <th (click)="
                sortBy(col.key)">
              {{ col.label }}
            </th>
          }
        </tr>
      </thead>
      <tbody>
        @for (row of sorted();
            track row.id) {
          <tr>
            @for (col
                of columns();
                track col.key) {
              <td>{{ row[
                col.key] }}</td>
            }
          </tr>
        }
      </tbody>
    </table>
  `,
})
export class DataTableComponent {
  columns = input.required<
    { key: string;
      label: string }[]>();
  data = input.required<
    Record<string, any>[]>();

  sortKey = signal('');
  sortDir = signal<
    'asc' | 'desc'>('asc');

  sorted = computed(() => {
    const key =
      this.sortKey();
    if (!key) return
      this.data();
    const dir =
      this.sortDir();
    return [...this.data()]
      .sort((a, b) => {
        const cmp = String(
          a[key]).localeCompare(
            String(b[key]));
        return dir === 'asc'
          ? cmp : -cmp;
      });
  });

  sortBy(key: string) {
    if (this.sortKey()
        === key) {
      this.sortDir.update(
        (d) => d === 'asc'
          ? 'desc' : 'asc');
    } else {
      this.sortKey.set(key);
      this.sortDir.set('asc');
    }
  }
}

Advanced Tips

Use model() for two-way binding with signal-based inputs that simplify parent-child synchronization. Implement host bindings with the host property in the component decorator for cleaner host element styling. Create structural directives with TemplateRef injection for advanced content projection patterns.

When to Use It?

Use Cases

Build a data table component with sortable columns using signal inputs and computed properties. Create a card component with named content projection slots for header, body, and actions. Implement a form field wrapper with validation display and required input enforcement.

Related Topics

Angular standalone components, signal inputs, content projection, change detection, and component lifecycle.

Important Notes

Requirements

Angular 17 or later for signal inputs and new control flow syntax. TypeScript 5.0 or later for decorator metadata support. Angular CLI for component scaffolding and testing.

Usage Recommendations

Do: use OnPush change detection for all components to optimize rendering performance. Prefer signal inputs over decorator-based @Input for new components. Use content projection for customizable component regions.

Don't: use ngOnChanges for reacting to input changes when signal effects provide a cleaner alternative. Create components with more than five inputs without considering a configuration object. Mutate input arrays or objects directly when OnPush expects immutable reference changes.

Limitations

Signal inputs require Angular 17 limiting adoption for projects on older versions. New control flow syntax (@if, @for) needs template migration from structural directives. Content projection cannot conditionally render projected content based on slot emptiness without workarounds.