Receber por email
← Voltar para o catálogo

web-framework-angular-standalone

Angular 17-19 standalone components, signals, control flow, dependency injection patterns

11estrelas
Atualizado há 7 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add agents-inc/skills

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/web-framework-angular-standalone/svg)](https://www.skillteca.com.br/skills/web-framework-angular-standalone)

Alerta por categoria

Receba novas skills de Design e Frontend toda segunda

1 email curto, só com as skills novas de Design e Frontend. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Angular Standalone Components

Quick Guide: Components are standalone by default in Angular 19. Use signal(), computed(), effect(), linkedSignal() for reactive state. Use input(), output(), model() for component communication. Use @if, @for, @switch, @defer for template control flow. Use inject() for dependency injection. Use resource() for async data fetching.

<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST write standalone components (the default in Angular 19) - only specify standalone: false when intentionally using NgModules)

(You MUST use input(), output(), model() functions instead of @Input(), @Output() decorators)

(You MUST use inject() function for dependency injection, NOT constructor injection)

**(You MUST use @if, @for, @switch control flow blocks, NOT *ngIf, *ngFor, *ngSwitch)**

(You MUST use track expression in ALL @for loops)

(You MUST use linkedSignal() instead of manual signal synchronization for dependent writable state)

</critical_requirements>

Auto-detection: Angular component, standalone component, signal, computed, effect, linkedSignal, resource, rxResource, httpResource, input(), output(), model(), @if, @for, @switch, @defer, inject(), provideRouter, afterRenderEffect

When to use:

  • Building Angular 17-19 components with standalone architecture
  • Implementing reactive state with signals
  • Creating component communication with signal-based inputs/outputs
  • Setting up routing with standalone components
  • Lazy loading components with @defer or loadComponent
  • Fetching async data with resource(), rxResource(), or httpResource()

Key patterns covered:

  • Standalone component architecture (default in Angular 19)
  • Signals for reactive state (signal, computed, effect, linkedSignal)
  • Resource API for async data (resource, rxResource, httpResource) [experimental]
  • Signal-based inputs and outputs (input, output, model)
  • Control flow blocks (@if, @for, @switch, @defer)
  • Dependency injection with inject()
  • Routing with provideRouter and loadComponent
  • DOM effects with afterRenderEffect()

When NOT to use:

  • Legacy Angular projects that must use NgModules (consult migration guides)
  • Simple scripts without Angular framework

Detailed Resources:

  • For core code examples, see [examples/core.md](examples/core.md)
  • For advanced patterns (@defer, DI config, model(), RxJS interop), see [examples/](examples/)
  • For decision frameworks and anti-patterns, see [reference.md](reference.md)

<philosophy>

Philosophy

Angular 17-19 embraces a standalone-first architecture that eliminates NgModule boilerplate. In Angular 19, standalone: true is the default - you only need to specify standalone: false for NgModule components. Signals provide synchronous, fine-grained reactivity for predictable state management. The new control flow syntax (@if, @for, @switch, @defer) is built into templates without imports, offering better type narrowing and smaller bundles. Components should be self-contained, lazy-loadable units that declare their own dependencies.

Angular's Four Pillars (17-19):

  1. Standalone by Default - Components, directives, and pipes are standalone by default in v19
  2. Signal-Based Reactivity - Synchronous, memoized, fine-grained change detection with signal(), computed(), linkedSignal()
  3. Built-In Control Flow - Template syntax that requires no imports and optimizes at build time
  4. Resource API - Experimental async data fetching that integrates with signals (resource(), rxResource(), httpResource() in 19.2)

</philosophy>

<patterns>

Core Patterns

Pattern 1: Standalone Component Structure

All Angular 17-19 components use standalone: true (the default in Angular 19) and declare their own imports.

// user-card.component.ts
import { Component, input, output } from "@angular/core";
import { DatePipe } from "@angular/common";

export type User = {
  id: string;
  name: string;
  email: string;
  createdAt: Date;
};

@Component({
  selector: "app-user-card",
  standalone: true,
  imports: [DatePipe],
  template: `
    <article class="user-card">
      <h2>{{ user().name }}</h2>
      <p>{{ user().email }}</p>
      <time>Joined: {{ user().createdAt | date: "mediumDate" }}</time>
      <button (click)="edit.emit(user())">Edit</button>
    </article>
  `,
})
export class UserCardComponent {
  // Signal-based input (required)
  user = input.required<User>();

  // Signal-based output
  edit = output<User>();
}

Why good: standalone: true eliminates NgModule boilerplate, imports array declares dependencies explicitly for tree-shaking, signal-based input() and output() provide type-safe reactive communication, template is colocated for readability

// BAD - Legacy patterns
@Component({
  selector: "app-user-card",
  template: `...`,
})
export class UserCardComponent {
  @Input() user!: User; // Legacy decorator
  @Output() edit = new EventEmitter<User>(); // Legacy EventEmitter
}

Why bad: @Input decorator lacks signal reactivity, EventEmitter is less type-safe than output(), non-null assertion (!) hides potential undefined errors, no imports array means dependencies aren't explicit

Pattern 2: Signals for Reactive State

Use signal() for writable state, computed() for derived values, and effect() for side effects. Key rules: always use .set() or .update() for mutations (never mutate the value directly), use computed() for derived values (not methods), and reserve effect() for true side effects (logging, analytics, localStorage).

// Writable signal
count = signal(0);

// Computed signal (read-only, memoized, recalculates only when deps change)
doubleCount = computed(() => this.count() * 2);

// Updating signals - always immutable
this.count.set(5); // Replace value
this.count.update((value) => value + 1); // Update from previous

// For arrays/objects: return new references
items = signal<Item[]>([]);
this.items.update((items) => [...items, newItem]); // Spread, don't push

// Effect for side effects only (not derived state)
effect(() => console.log(`Count: ${this.count()}`));

See [examples/core.md](examples/core.md) for a full shopping cart example with signals.

// BAD - Direct mutation doesn't trigger reactivity
this.items().push(newItem);              // signal won't notify consumers
this.items.update(items => { items.push(newItem); return items; }); // same reference, no update

// BAD - Method instead of computed (recalculates every call, not memoized)
getTotal(): number { return this.items().reduce(...); }

Why bad: direct mutation doesn't trigger change detection, returning same reference skips equality check, methods lack memoization that computed() provides

Pattern 3: Signal Inputs and Outputs

Use input(), output(), and model() functions for component communication.

// search-input.component.ts
import { Component, input, output, model, computed } from "@angular/core";

const MIN_SEARCH_LENGTH = 3;

@Component({
  selector: "app-search-input",
  standalone: true,
  template: `
    <div class="search-input">
      <input
        [value]="query()"
        (input)="onInput($event)"
        [placeholder]="placeholder()"
      />
      @if (isValidSearch()) {
        <button (click)="search.emit(query())">Search</button>
      }
      @if (query()) {
        <button (click)="clear()">Clear</button>
      }
    </div>
  `,
})
export class SearchInputComponent {
  // Optional input with default value
  placeholder = input("Search...");

  // Required input
  minLength = input.required<number>();

  // Two-way binding with model()
  que
Compartilhar X LinkedIn