angular-20-standalone-component
Angular 20の最新パターンで、リアクティブな状態管理やフォーム処理、サービス連携が必要なUIコンポーネントを、簡潔なコードで効率的に作成・保守できる土台を提供するSkill。
📜 元の英語説明(参考)
Create Angular 20 standalone components using modern patterns: Signals for state management, input()/output() functions (not decorators), @if/@for/@switch control flow (not *ngIf/*ngFor), inject() dependency injection (not constructor), and OnPush change detection. Use this skill when scaffolding new UI components that need reactive state, form handling, or integration with services following the three-layer architecture.
🇯🇵 日本人クリエイター向け解説
Angular 20の最新パターンで、リアクティブな状態管理やフォーム処理、サービス連携が必要なUIコンポーネントを、簡潔なコードで効率的に作成・保守できる土台を提供するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o angular-20-standalone-component.zip https://jpskill.com/download/16770.zip && unzip -o angular-20-standalone-component.zip && rm angular-20-standalone-component.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/16770.zip -OutFile "$d\angular-20-standalone-component.zip"; Expand-Archive "$d\angular-20-standalone-component.zip" -DestinationPath $d -Force; ri "$d\angular-20-standalone-component.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
angular-20-standalone-component.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
angular-20-standalone-componentフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
Angular 20 スタンドアロンコンポーネントスキル
このスキルは、最新のパターンとプロジェクト標準に従って Angular 20 コンポーネントを作成するのに役立ちます。
コア原則
最新の Angular 20 パターン
- スタンドアロンコンポーネント: 100% スタンドアロン、NgModule は不要
- Signals:
signal()、computed()、effect()を状態に使用 - 新しい構文:
input()、output()、@if、@for、@switch - inject(): 関数ベースの依存性注入
- OnPush: パフォーマンス向上のための変更検知戦略
アーキテクチャ統合
- プレゼンテーション層: コンポーネントは UI のみを処理
- サービス統合: ビジネスロジックのためにサービスを注入
- 直接リポジトリなし: リポジトリを直接注入しない
- イベント駆動: モジュール間の通信には EventBus を使用
コンポーネントテンプレート
import { Component, signal, computed, effect, input, output, inject, ChangeDetectionStrategy } from '@angular/core';
import { SHARED_IMPORTS } from '@shared';
import { YourService } from '@core/services/your.service';
@Component({
selector: 'app-your-component',
standalone: true,
imports: [SHARED_IMPORTS],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="component-container">
@if (loading()) {
<nz-spin nzSimple />
} @else if (hasError()) {
<nz-alert
nzType="error"
[nzMessage]="errorMessage()!"
nzShowIcon
/>
} @else {
<div class="content">
@for (item of items(); track item.id) {
<app-item-card
[item]="item"
(itemChange)="handleItemChange($event)"
/>
} @empty {
<nz-empty nzNotFoundContent="No items found" />
}
</div>
}
</div>
`,
styles: [`
.component-container {
padding: 24px;
}
.content {
display: grid;
gap: 16px;
}
`]
})
export class YourComponent {
// ✅ inject() でサービスを注入
private yourService = inject(YourService);
// ✅ プロパティには input() を使用 (@Input() ではない)
blueprintId = input.required<string>();
readonly = input(false);
// ✅ イベントには output() を使用 (@Output() ではない)
itemChange = output<Item>();
// ✅ 可変状態には signal() を使用
loading = signal(false);
error = signal<string | null>(null);
items = signal<Item[]>([]);
// ✅ 派生状態には computed() を使用
hasError = computed(() => this.error() !== null);
errorMessage = computed(() => this.error());
totalItems = computed(() => this.items().length);
// ✅ 副作用には effect() を使用
constructor() {
effect(() => {
const id = this.blueprintId();
console.log('Blueprint ID changed:', id);
this.loadItems(id);
});
}
ngOnInit(): void {
this.loadItems(this.blueprintId());
}
async loadItems(blueprintId: string): Promise<void> {
this.loading.set(true);
this.error.set(null);
try {
const items = await this.yourService.getItems(blueprintId);
this.items.set(items);
} catch (err) {
this.error.set(err instanceof Error ? err.message : 'Unknown error');
} finally {
this.loading.set(false);
}
}
handleItemChange(item: Item): void {
// ローカル状態を更新
this.items.update(items =>
items.map(i => i.id === item.id ? item : i)
);
// 親に発行
this.itemChange.emit(item);
}
}
主要なパターン
1. Signal による状態管理
// 書き込み可能な signals
private _items = signal<Item[]>([]);
// 読み取り専用のパブリックアクセス
items = this._items.asReadonly();
// computed による派生状態
filteredItems = computed(() =>
this._items().filter(item => item.status === 'active')
);
// signals の更新
this._items.set([...]); // 置換
this._items.update(items => [...items, newItem]); // 変換
2. 制御フロー構文
// ✅ 正しい: 新しい @if 構文
@if (condition()) {
<div>Content</div>
} @else if (otherCondition()) {
<div>Other</div>
} @else {
<div>Default</div>
}
// ✅ 正しい: 新しい @for 構文 (track 付き)
@for (item of items(); track item.id) {
<div>{{ item.name }}</div>
} @empty {
<p>No items</p>
}
// ✅ 正しい: 新しい @switch 構文
@switch (status()) {
@case ('active') { <span class="badge-success">Active</span> }
@case ('inactive') { <span class="badge-danger">Inactive</span> }
@default { <span class="badge-default">Unknown</span> }
}
// ❌ 間違い: 古い構文 (禁止)
<div *ngIf="condition">...</div>
<div *ngFor="let item of items">...</div>
<div [ngSwitch]="status">...</div>
3. Input/Output 関数
// ✅ 正しい: input()/output() 関数を使用
task = input.required<Task>();
readonly = input(false);
taskChange = output<Task>();
// ❌ 間違い: デコレーター (禁止)
@Input() task!: Task;
@Output() taskChange = new EventEmitter<Task>();
4. 依存性注入
// ✅ 正しい: inject() を使用
private taskService = inject(TaskService);
private router = inject(Router);
private destroyRef = inject(DestroyRef);
// ❌ 間違い: コンストラクタ注入 (禁止)
constructor(
private taskService: TaskService,
private router: Router
) {}
5. サブスクリプション
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
// ✅ 正しい: takeUntilDestroyed による自動クリーンアップ
private destroyRef = inject(DestroyRef);
ngOnInit(): void {
this.service.data$
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(data => this.items.set(data));
}
// ❌ 間違い: クリーンアップなしの手動サブスクリプション
ngOnInit(): void {
this.service.data$.subscribe(data => this.items.set(data));
}
コンポーネントの種類
スマートコンポーネント (コンテナ)
@Component({
selector: 'app-task-list',
standalone: true,
imports: [SHARED_IMPORTS, TaskItemComponent],
template: `
@for (task of tasks(); track task.id) {
<app-task-item
[task]="task"
(taskChange)="updateTask($event)"
/>
}
`
})
(原文はここで切り詰められています) 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Angular 20 Standalone Component Skill
This skill helps create Angular 20 components following modern patterns and project standards.
Core Principles
Modern Angular 20 Patterns
- Standalone Components: 100% standalone, zero NgModules
- Signals: Use
signal(),computed(),effect()for state - New Syntax:
input(),output(),@if,@for,@switch - inject(): Function-based dependency injection
- OnPush: Change detection strategy for performance
Architecture Integration
- Presentation Layer: Components handle UI only
- Service Integration: Inject services for business logic
- No Direct Repository: Never inject repositories directly
- Event-Driven: Use EventBus for cross-module communication
Component Template
import { Component, signal, computed, effect, input, output, inject, ChangeDetectionStrategy } from '@angular/core';
import { SHARED_IMPORTS } from '@shared';
import { YourService } from '@core/services/your.service';
@Component({
selector: 'app-your-component',
standalone: true,
imports: [SHARED_IMPORTS],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="component-container">
@if (loading()) {
<nz-spin nzSimple />
} @else if (hasError()) {
<nz-alert
nzType="error"
[nzMessage]="errorMessage()!"
nzShowIcon
/>
} @else {
<div class="content">
@for (item of items(); track item.id) {
<app-item-card
[item]="item"
(itemChange)="handleItemChange($event)"
/>
} @empty {
<nz-empty nzNotFoundContent="No items found" />
}
</div>
}
</div>
`,
styles: [`
.component-container {
padding: 24px;
}
.content {
display: grid;
gap: 16px;
}
`]
})
export class YourComponent {
// ✅ Inject services with inject()
private yourService = inject(YourService);
// ✅ Use input() for properties (NOT @Input())
blueprintId = input.required<string>();
readonly = input(false);
// ✅ Use output() for events (NOT @Output())
itemChange = output<Item>();
// ✅ Use signal() for mutable state
loading = signal(false);
error = signal<string | null>(null);
items = signal<Item[]>([]);
// ✅ Use computed() for derived state
hasError = computed(() => this.error() !== null);
errorMessage = computed(() => this.error());
totalItems = computed(() => this.items().length);
// ✅ Use effect() for side effects
constructor() {
effect(() => {
const id = this.blueprintId();
console.log('Blueprint ID changed:', id);
this.loadItems(id);
});
}
ngOnInit(): void {
this.loadItems(this.blueprintId());
}
async loadItems(blueprintId: string): Promise<void> {
this.loading.set(true);
this.error.set(null);
try {
const items = await this.yourService.getItems(blueprintId);
this.items.set(items);
} catch (err) {
this.error.set(err instanceof Error ? err.message : 'Unknown error');
} finally {
this.loading.set(false);
}
}
handleItemChange(item: Item): void {
// Update local state
this.items.update(items =>
items.map(i => i.id === item.id ? item : i)
);
// Emit to parent
this.itemChange.emit(item);
}
}
Key Patterns
1. Signal State Management
// Writable signals
private _items = signal<Item[]>([]);
// Read-only public access
items = this._items.asReadonly();
// Computed derived state
filteredItems = computed(() =>
this._items().filter(item => item.status === 'active')
);
// Update signals
this._items.set([...]); // Replace
this._items.update(items => [...items, newItem]); // Transform
2. Control Flow Syntax
// ✅ CORRECT: New @if syntax
@if (condition()) {
<div>Content</div>
} @else if (otherCondition()) {
<div>Other</div>
} @else {
<div>Default</div>
}
// ✅ CORRECT: New @for syntax with track
@for (item of items(); track item.id) {
<div>{{ item.name }}</div>
} @empty {
<p>No items</p>
}
// ✅ CORRECT: New @switch syntax
@switch (status()) {
@case ('active') { <span class="badge-success">Active</span> }
@case ('inactive') { <span class="badge-danger">Inactive</span> }
@default { <span class="badge-default">Unknown</span> }
}
// ❌ WRONG: Old syntax (forbidden)
<div *ngIf="condition">...</div>
<div *ngFor="let item of items">...</div>
<div [ngSwitch]="status">...</div>
3. Input/Output Functions
// ✅ CORRECT: Use input()/output() functions
task = input.required<Task>();
readonly = input(false);
taskChange = output<Task>();
// ❌ WRONG: Decorators (forbidden)
@Input() task!: Task;
@Output() taskChange = new EventEmitter<Task>();
4. Dependency Injection
// ✅ CORRECT: Use inject()
private taskService = inject(TaskService);
private router = inject(Router);
private destroyRef = inject(DestroyRef);
// ❌ WRONG: Constructor injection (forbidden)
constructor(
private taskService: TaskService,
private router: Router
) {}
5. Subscriptions
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
// ✅ CORRECT: Auto-cleanup with takeUntilDestroyed
private destroyRef = inject(DestroyRef);
ngOnInit(): void {
this.service.data$
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(data => this.items.set(data));
}
// ❌ WRONG: Manual subscriptions without cleanup
ngOnInit(): void {
this.service.data$.subscribe(data => this.items.set(data));
}
Component Types
Smart Component (Container)
@Component({
selector: 'app-task-list',
standalone: true,
imports: [SHARED_IMPORTS, TaskItemComponent],
template: `
@for (task of tasks(); track task.id) {
<app-task-item
[task]="task"
(taskChange)="updateTask($event)"
/>
}
`
})
export class TaskListComponent {
private taskService = inject(TaskService);
tasks = signal<Task[]>([]);
ngOnInit(): void {
this.loadTasks();
}
async loadTasks(): Promise<void> {
const tasks = await this.taskService.getTasks();
this.tasks.set(tasks);
}
async updateTask(task: Task): Promise<void> {
await this.taskService.updateTask(task.id, task);
this.tasks.update(tasks =>
tasks.map(t => t.id === task.id ? task : t)
);
}
}
Presentational Component (Pure)
@Component({
selector: 'app-task-item',
standalone: true,
imports: [SHARED_IMPORTS],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<nz-card>
<h3>{{ task().title }}</h3>
<p>{{ task().description }}</p>
<button nz-button (click)="handleComplete()">
Complete
</button>
</nz-card>
`
})
export class TaskItemComponent {
task = input.required<Task>();
taskChange = output<Task>();
handleComplete(): void {
const updated = { ...this.task(), status: 'completed' };
this.taskChange.emit(updated);
}
}
Form Handling
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-task-form',
standalone: true,
imports: [SHARED_IMPORTS, ReactiveFormsModule],
template: `
<form nz-form [formGroup]="form" (ngSubmit)="handleSubmit()">
<nz-form-item>
<nz-form-label nzRequired>Title</nz-form-label>
<nz-form-control nzErrorTip="Please enter task title">
<input nz-input formControlName="title" />
</nz-form-control>
</nz-form-item>
<button nz-button nzType="primary" [disabled]="!form.valid">
Submit
</button>
</form>
`
})
export class TaskFormComponent {
private fb = inject(FormBuilder);
form = this.fb.group({
title: ['', [Validators.required, Validators.maxLength(200)]],
description: [''],
status: ['pending']
});
taskSubmit = output<Partial<Task>>();
handleSubmit(): void {
if (this.form.valid) {
this.taskSubmit.emit(this.form.value);
this.form.reset();
}
}
}
Checklist
When creating a component:
- [ ] Standalone component with imports
- [ ] Uses signal() for state
- [ ] Uses computed() for derived state
- [ ] Uses input()/output() functions
- [ ] Uses @if/@for/@switch syntax
- [ ] Uses inject() for dependencies
- [ ] OnPush change detection
- [ ] No business logic in component
- [ ] Proper error handling
- [ ] Loading states
- [ ] Empty states
- [ ] TypeScript strict typing