Ng22 reactivity
Skill PavanAnguluri/angular22-agent-skills/skills/ng22-reactivity
Angular 22 Agent Skills
npx -y skills add PavanAnguluri/angular22-agent-skills --skill ng22-reactivityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Enforces Angular 22 fine-grained reactivity using Signals, Signal inputs, and Model primitives.
SKILL.md
3.6 KB, as published. Nobody here has run it
Angular 22 Signals & Reactivity Discipline
You are an expert Angular 22 architect. Treat signals as the default state primitive and keep reactive dependencies explicit, synchronous, and easy to reason about.
Core Rules
- Use
signal()for writable local state and expose readonly views withasReadonly()when consumers should not mutate the state. - Use
computed()for every synchronous derivation that the template or another signal depends on. - Declare component inputs with
input()orinput.required()instead of@Input(). - Use
model()for two-way state that must sync between parent and child. - Use
effect()only for side effects that talk to non-reactive APIs; prefercomputed()when the output is still just data. - Read signals before
awaitboundaries. Reactive tracking is synchronous, so async gaps break dependency capture.
Signal Shapes
Writable state
import { signal } from '@angular/core';
const count = signal(0);
count.set(1);
count.update((value) => value + 1);
Readonly exposure
import { Injectable, signal } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class CounterState {
private readonly _count = signal(0);
readonly count = this._count.asReadonly();
increment(): void {
this._count.update((value) => value + 1);
}
}
Dynamic derivation
import { computed, signal } from '@angular/core';
const showCount = signal(false);
const count = signal(0);
const message = computed(() => {
if (showCount()) {
return `The count is ${count()}`;
}
return 'Nothing to see here.';
});
Component Example
import { Component, computed, input, model, signal } from '@angular/core';
@Component({
standalone: true,
selector: 'app-user-profile',
template: `
<section>
<h3>{{ fullName() }}</h3>
<p>{{ badgeText() }}</p>
<input [value]="role()" (input)="role.set($any($event.target).value)" />
<button type="button" (click)="isExpanded.update((value) => !value)">
Toggle details
</button>
</section>
`,
})
export class UserProfileComponent {
firstName = input.required<string>();
lastName = input.required<string>();
role = model<string>('Developer');
isExpanded = signal(false);
fullName = computed(() => `${this.firstName()} ${this.lastName()}`);
badgeText = computed(() => (this.isExpanded() ? 'Expanded' : 'Collapsed'));
}
Effects and Interop
Use effect() when the code needs to update a non-reactive API such as storage, title, analytics, or a logging service. Use untracked() when an incidental signal read should not become a dependency.
import { effect, untracked } from '@angular/core';
effect(() => {
const user = currentUser();
untracked(() => loggingService.log(`User changed: ${user}`));
});
Only use toSignal() or toObservable() from @angular/core/rxjs-interop when bridging asynchronous streams such as HTTP pipelines, WebSocket feeds, or form value streams.
Anti-Patterns
- Do not store derived data in writable state if it can be expressed as
computed(). - Do not mutate nested objects in place and expect the UI to react reliably.
- Do not use signals as a dumping ground for unrelated state.
- Do not call methods from templates when the result can be cached as a signal.
Review Checklist
- Every input is signal-based.
- Every derived value is a computed signal.
- Side effects are isolated and intentional.
- The template reads data, it does not calculate it.