mcpField
A
angular-core

angular-core

github.com/gentleman-programming開発661227 回インストール31 週間前に更新
リポジトリ

angular-core について

スタンドアロンコンポーネント、シグナル、関数形式の入出力、ゾーンレス変更検知に関するAngularスキルです。Angularのパターンを示し、必要な場合に限ってRxJSを使う方針を説明します。

主な機能

  1. 01スタンドアロンコンポーネントの規約
  2. 02関数形式の入力と出力
  3. 03シグナルによる状態と副作用の管理
  4. 04ライフサイクルフックの回避
  5. 05inject()による依存性注入
  6. 06ゾーンレス変更検知

活用シーン

  1. 01スタンドアロンAngularコンポーネントの構築
  2. 02シグナルによるコンポーネント状態の管理
  3. 03effectによるライフサイクルフックの置き換え
  4. 04ゾーンレスAngularアプリの設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/gentleman-programming/gentleman-skills --skill angular-core

インストール後、Claude Code で/angular-coreを入力して読み込まれたか確認します。

詳細

スタンドアロンコンポーネント(必須)

コンポーネントはデフォルトでスタンドアロンです。standalone: true を設定しないでください。

@Component({
  selector: 'app-user',
  imports: [CommonModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `...`
})
export class UserComponent {}

入出力関数(必須)

// ✅ ALWAYS: Function-based
readonly user = input.required<User>();
readonly disabled = input(false);
readonly selected = output<User>();
readonly checked = model(false);  // Two-way binding

// ❌ NEVER: Decorators
@Input() user: User;
@Output() selected = new EventEmitter<User>();

状態管理には Signals を使用(必須)

readonly count = signal(0);
readonly doubled = computed(() => this.count() * 2);

// Update
this.count.set(5);
this.count.update(prev => prev + 1);

// Side effects
effect(() => localStorage.setItem('count', this.count().toString()));

ライフサイクルフックは使用しない(必須)

Signals はライフサイクルフックに置き換わります。ngOnInit、ngOnChanges、ngOnDestroy は使用しないでください。

// ❌ NEVER: Lifecycle hooks
ngOnInit() {
  this.loadUser();
}

ngOnChanges(changes: SimpleChanges) {
  if (changes['userId']) {
    this.loadUser();
  }
}

// ✅ ALWAYS: Signals + effect
readonly userId = input.required<string>();
readonly user = signal<User | null>(null);

private userEffect = effect(() => {
  // Runs automatically when userId() changes
  this.loadUser(this.userId());
});

// ✅ For derived data, use computed
readonly displayName = computed(() => this.user()?.name ?? 'Guest');
用途に応じた使い分け
必要な処理使用するもの
入力の変更への反応入力シグナルを監視する effect()
派生状態/計算状態computed()
副作用(API 呼び出し、localStorage)effect()
破棄時のクリーンアップDestroyRef + inject()
// Cleanup example
private readonly destroyRef = inject(DestroyRef);

constructor() {
  const subscription = someObservable$.subscribe();
  this.destroyRef.onDestroy(() => subscription.unsubscribe());
}

コンストラクターより inject()(必須)

// ✅ ALWAYS
private readonly http = inject(HttpClient);

// ❌ NEVER
constructor(private http: HttpClient) {}

ネイティブ制御フロー(必須)

@if (loading()) {
  <spinner />
} @else {
  @for (item of items(); track item.id) {
    <item-card [data]="item" />
  } @empty {
    <p>No items</p>
  }
}

@switch (status()) {
  @case ('active') { <span>Active</span> }
  @default { <span>Unknown</span> }
}

RxJS — 必要な場合にのみ使用

Signals がデフォルトです。複雑な非同期処理に限り、RxJS を使用してください。

Signals を使用RxJS を使用
コンポーネントの状態複数のストリームの結合
派生値デバウンス/スロットリング
単純な非同期処理(単一の API 呼び出し)競合状態
入出力WebSocket、リアルタイム処理
複雑なエラー再試行ロジック
// ✅ Simple API call - use signals
readonly user = signal<User | null>(null);
readonly loading = signal(false);

async loadUser(id: string) {
  this.loading.set(true);
  this.user.set(await firstValueFrom(this.http.get<User>(`/api/users/${id}`)));
  this.loading.set(false);
}

// ✅ Complex stream - use RxJS
readonly searchResults$ = this.searchTerm$.pipe(
  debounceTime(300),
  distinctUntilChanged(),
  switchMap(term => this.http.get<Results>(`/api/search?q=${term}`))
);

// Convert to signal when needed in template
readonly searchResults = toSignal(this.searchResults$, { initialValue: [] });

ゾーンレス Angular(必須)

Angular はゾーンレスです。provideZonelessChangeDetection() を使用してください。

bootstrapApplication(AppComponent, {
  providers: [provideZonelessChangeDetection()]
});

ZoneJS を削除します:

npm uninstall zone.js

angular.json の polyfills から zone.js と zone.js/testing を削除します。

ゾーンレスの要件
  • OnPush 変更検出を使用する
  • 状態には Signals を使用する(Angular に自動通知されます)
  • オブザーバブルには AsyncPipe を使用する
  • 必要に応じて markForCheck() を使用する

リソース

コメント