Skip to main content

angular-signals

Use when building Angular 16+ applications requiring fine-grained reactive state management and zone-less change detection.

Informations de source

Dépôt
TheBushidoCollective/han
Dernière activité de la source
28 avril 2026 à 03:37
Langue détectée de SKILL.md
anglais
Étoiles
198
Forks
21

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
angular-signals
user-invocable
false
description
Use when building Angular 16+ applications requiring fine-grained reactive state management and zone-less change detection.
allowed-tools
["Bash","Read"]
# Angular Signals Master Angular Signals for building reactive applications with fine-grained reactivity and improved performance. ## Signal Basics ### Creating and Using Signals ```typescript import { Component, signal, computed, effect } from '@angular/core'; @Component({ selector: 'app-counter', standalone: true, template: ` <div> <p>Count: {{ count() }}</p> <p>Double: {{ doubleCount() }}</p> <button (click)="increment()">+</button> <button (click)="decrement()">-</button> <button (click)="reset()">Reset</button> </div> ` }) export class CounterComponent { // Writable signal count = signal(0); // Computed signal doubleCount = computed(() => this.count() * 2); constructor() { // Effect runs when count changes effect(() => { console.log(`Count is: ${this.count()}`); }); } increment() { this.count.update(value => value + 1); } decrement() { this.count.update(value => value - 1); } reset() { this.count.set(0); } } ``` ### Signal Methods ```typescript import { signal } from '@angular/core'; // Create signal const count = signal(0); // set - replace value count.set(5); // update - transform current value count.update(value => value + 1); // mutate - modify object (experimental) const user = signal({ name: 'John', age: 30 }); user.mutate(value => { value.age = 31; // Mutate in place }); // Read value const current = count(); // Call as function ``` ## Computed Signals ### Basic Computed ```typescript import { signal, computed } from '@angular/core'; const firstName = signal('John'); const lastName = signal('Doe'); // Computed signal const fullName = computed(() => { return `${firstName()} ${lastName()}`; }); console.log(fullName()); // John Doe firstName.set('Jane'); console.log(fullName()); // Jane Doe (automatically updates) ``` ### Complex Computed ```typescript interface Product { id: number; name: string; price: number; quantity: number; } @Component({ selector: 'app-cart' }) export class CartComponent { items = signal<Product[]>([]); // Computed: total items itemCount = computed(() => { return this.items().reduce((sum, item) => sum + item.quantity, 0); }); // Computed: subtotal subtotal = computed(() => { return this.items().reduce((sum, item) => sum + (item.price * item.quantity), 0 ); }); // Computed: tax tax = computed(() => this.subtotal() * 0.08); // Computed: total total = computed(() => this.subtotal() + this.tax()); // Computed: formatted total formattedTotal = computed(() => { return `$${this.total().toFixed(2)}`; }); } ``` ### Chained Computed ```typescript const count = signal(1); const doubled = computed(() => count() * 2); const quadrupled = computed(() => doubled() * 2); const formatted = computed(() => `Count: ${quadrupled()}`); console.log(formatted()); // Count: 4 count.set(2); console.log(formatted()); // Count: 8 ``` ## Effects ### Basic Effects ```typescript import { Component, signal, effect } from '@angular/core'; @Component({ selector: 'app-logger' }) export class LoggerComponent { count = signal(0); constructor() { // Effect runs when count changes effect(() => { console.log(`Count changed to: ${this.count()}`); }); } increment() { this.count.update(v => v + 1); // Triggers effect } } ``` ### Effect Cleanup ```typescript import { effect } from '@angular/core'; const count = signal(0); effect((onCleanup) => { const timer = setInterval(() => { console.log(count()); }, 1000); // Cleanup function onCleanup(() => { clearInterval(timer); }); }); ``` ### Conditional Effects ```typescript import { effect, signal } from '@angular/core'; const enabled = signal(true); const count = signal(0); effect(() => { // Only run if enabled if (!enabled()) return; console.log(`Count: ${count()}`); }); ``` ## Signal Inputs ### Component Inputs as Signals ```typescript import { Component, input, computed } from '@angular/core'; @Component({ selector: 'app-user-profile', standalone: true, template: ` <div> <h2>{{ displayName() }}</h2> <p>Age: {{ age() }}</p> <p>Is adult: {{ isAdult() }}</p> </div> ` }) export class UserProfileComponent { // Signal inputs (Angular 17.1+) firstName = input.required<string>(); lastName = input.required<string>(); age = input(0); // Optional with default // Computed from inputs displayName = computed(() => `${this.firstName()} ${this.lastName()}` ); isAdult = computed(() => this.age() >= 18); } // Usage <app-user-profile [firstName]="'John'" [lastName]="'Doe'" [age]="30" /> ``` ### Transform Input Signals ```typescript import { Component, input } from '@angular/core'; @Component({ selector: 'app-formatted-text' }) export class FormattedTextComponent { // Transform input text = input('', { transform: (value: string) => value.toUpperCase() }); // Alias input label = input('', { alias: 'labelText' }); } // Usage <app-formatted-text [text]="'hello'" [labelText]="'Name'" /> ``` ## Signal Outputs ### Component Outputs as Signals ```typescript import { Component, output } from '@angular/core'; @Component({ selector: 'app-button', template: ` <button (click)="handleClick()"> {{ label() }} </button> ` }) export class ButtonComponent { label = input('Click me'); // Signal output (Angular 17.1+) clicked = output<void>(); valueChanged = output<number>(); private clickCount = signal(0); handleClick() { this.clickCount.update(v => v + 1); this.clicked.emit(); this.valueChanged.emit(this.clickCount()); } } // Usage <app-button (clicked)="onClicked()" (valueChanged)="onValueChanged($event)" /> ``` ## Signal Queries ### ViewChild with Signals ```typescript import { Component, viewChild, ElementRef, afterNextRender } from '@angular/core'; @Component({ selector: 'app-input-focus', template: ` <input #inputElement type="text" /> <button (click)="focusInput()">Focus</button> ` }) export class InputFocusComponent { // Signal-based viewChild inputElement = viewChild<ElementRef>('inputElement'); constructor() { afterNextRender(() => { // Access element after render const element = this.inputElement()?.nativeElement; if (element) { element.focus(); } }); } focusInput() { this.inputElement()?.nativeElement.focus(); } } ``` ### ViewChildren with Signals ```typescript import { Component, viewChildren, ElementRef } from '@angular/core'; @Component({ selector: 'app-list', standalone: true, template: ` @for (item of items(); track item) { <div #item>{{ item }}</div> } <p>Item count: {{ itemElements().length }}</p> ` }) export class ListComponent { items = signal(['A', 'B', 'C']); // Signal-based viewChildren itemElements = viewChildren<ElementRef>('item'); logItemCount() { console.log(`Count: ${this.itemElements().length}`); } } ``` ### ContentChild with Signals ```typescript import { Component, contentChild, Directive } from '@angular/core'; @Directive({ selector: '[appHeader]' }) export class HeaderDirective {} @Component({ selector: 'app-card', standalone: true, template: ` <div class="card"> <ng-content select="[appHeader]" /> <ng-content /> @if (hasHeader()) { <p>Has custom header</p> } </div> ` }) export class CardComponent { // Signal-based contentChild header = contentChild(HeaderDirective); hasHeader = computed(() => !!this.header()); } // Usage <app-card> <h2 appHeader>Title</h2> <p>Content</p> </app-card> ``` ## Signals vs Observables ### When to Use Signals ```typescript // Use signals for synchronous state @Component({ selector: 'app-counter' }) export class CounterComponent { count = signal(0); // Signal for synchronous state increment() { this.count.update(v => v + 1); } } ``` ### When to Use Observables ```typescript // Use observables for async operations @Component({ selector: 'app-user-list' }) export class UserListComponent { private http = inject(HttpClient);
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub