Skip to main content

angular-signals

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

소스 정보

저장소
TheBushidoCollective/han
최근 소스 활동
2026년 4월 28일 03:37
감지된 SKILL.md 언어
영어
스타
200
포크
24

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
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);
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기