Skip to main content

angular-signals

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

Quellinformationen

Repository
TheBushidoCollective/han
Letzte Quellaktivität
28. April 2026 um 03:37
Erkannte Sprache von SKILL.md
Englisch
Sterne
198
Forks
22

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
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);
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen