Angular 22+ Migration: $ARGUMENTS
SkillDev toolsMigrate a component or directive to Angular 22+ signal-based patterns
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Angular 22+ Migration: $ARGUMENTS skill
What this skill tells your AI
The instructions your AI receives, as published by sap/fundamental-ngx in .claude/skills/migrate/SKILL.md and read by ahel’s review.
If $ARGUMENTS is empty, ask the user for a component path or folder before proceeding.
Phase 1: Analyze
Read all files in the target path. For each component, directive, or service, scan for migration items below. If a folder is given, process all .ts and .html files within it.
What to migrate
Inputs / Outputs / Models:
@Input()→input()orinput.required()@Input()with setter →input()+linkedSignal()(preferred) oreffect()(only for DOM side effects)@Input()+@Output()pair (e.g.value/valueChange) →model()@Output()→output()- Boolean inputs: add
{ transform: booleanAttribute } - Number inputs: add
{ transform: numberAttribute }
Host bindings:
@HostBinding('class.x')→host: { '[class.x]': 'expr()' }@HostBinding('attr.x')→host: { '[attr.x]': 'expr()' }@HostBinding('style.x')→host: { '[style.x]': 'expr()' }@HostListener('event')→host: { '(event)': 'handler($event)' }
Queries:
@ViewChild()→viewChild()orviewChild.required()@ViewChildren()→viewChildren()@ContentChild()→contentChild()@ContentChildren()→contentChildren()
Template syntax:
*ngIf→@if*ngFor→@for(requirestrackexpression)*ngSwitch/*ngSwitchCase→@switch/@case
State management:
BehaviorSubjectused for local component state →signal()BehaviorSubject+combineLatest→computed()Subject<void>used only to trigger side effects →effect()effect()that only callssignal.set()for derived state →computed()orlinkedSignal- Redundant
markForCheck()after signal updates → remove - Object/array mutation in place +
signal.set()with same ref → immutable update withsignal.update()
CSS class building:
CssClassBuilder+@applyCssClass→computed()returning class string +host: { '[class]': '_cssClass()' }
Cleanup patterns:
- Custom
DestroyedService→DestroyRef+takeUntilDestroyed() ngOnDestroyonly fordestroy$.next()→ remove entirely after migration
Decorator cleanup:
- Remove
standalone: true(default since Angular 19) - Remove
allowSignalWritesoption ineffect()(the option no longer exists)
Imports:
- Deprecated
*Moduleclasses → individual component/directive imports
Member ordering (ESLint enforced):
- Decorated properties (any remaining
@Input,@Output,@ViewChild) - Signal inputs/outputs (
input(),output(),model()) - Public → Protected → Private fields
- Constructor
- Public → Protected → Private methods
What NOT to migrate
Do NOT convert these — they should stay as-is:
- RxJS for async operations: HTTP calls, WebSocket streams, timers, interval-based logic → keep as Observable
- BehaviorSubject with complex operators:
switchMap,debounceTime,distinctUntilChanged,combineLatestwith async sources → keep as RxJS - Plain properties with no reactive consumer: If nothing in template, host binding, computed, or effect reads it → keep as plain property, do not wrap in
signal() - Signals for internal bookkeeping: One-time flags, cached DOM measurements, counters with no reactive consumer → use plain property
Migration is optional for existing code
The migration targets are recommendations, not mandatory fixes. Present all findings but let the user decide scope. Existing @Input() / @Output() decorators are valid — only migrate them if the user wants to or if the code is being substantially reworked.
Phase 2: Present Plan
Output a structured migration plan:
## Migration Plan: [component name]
### Files affected
- path/to/component.ts
- path/to/component.html
- path/to/component.spec.ts
### Inputs / Outputs
| Current | Migration | Notes |
|---------|-----------|-------|
| `@Input() label: string` | `readonly label = input<string>('')` | |
| `@Input() disabled: boolean` | `readonly disabled = input(false, { transform: booleanAttribute })` | |
| `@Input() value` + `@Output() valueChange` | `readonly value = model<T>()` | Two-way binding |
### Host Bindings
| Current | Migration |
|---------|-----------|
| `@HostBinding('class.is-open')` | `host: { '[class.is-open]': 'isOpen()' }` |
### Template Changes
| Current | Migration |
|---------|-----------|
| `*ngIf="condition"` | `@if (condition) { ... }` |
| `*ngFor="let item of items"` | `@for (item of items; track item.id) { ... }` |
### State Management
| Current | Migration | Reason |
|---------|-----------|--------|
| `BehaviorSubject<boolean>` | `signal(false)` | Local state, no async consumers |
### Skipped (intentionally not migrated)
| Item | Reason |
|------|--------|
| `httpData$` Observable | Async HTTP stream |
### Test Updates
- [ ] Replace `component.input = value` with `fixture.componentRef.setInput('input', value)`
- [ ] Update module imports to individual components
- [ ] Verify existing tests still pass after migration
### Docs Updates
- [ ] Update examples if public API changed (input names, usage patterns)
- [ ] Ensure examples use individual imports, not deprecated modules
### Breaking Changes
- [ ] List any renamed/removed inputs or outputs
- [ ] Draft BREAKING CHANGE footer if needed
Stop here and wait for approval before proceeding.
Phase 3: Execute
After approval:
- Apply all migrations from the approved plan
- Format code:
yarn format - Run build:
nx run <library>:build - Run lint:
nx run <library>:lint - Run tests:
nx run <library>:test --testfile=<spec-file> --skip-nx-cache - Report results and any issues
Signals
- GitHub stars
- 294
- Forks
- 147
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
migrate-sap- Source
- github.com/sap/fundamental-ngx