<< All versions
Skill v1.0.1
currentAutomated scan100/100gentleman-programming/gentleman-skills/core
1 files
──Details
PublishedSeptember 30, 2026 at 08:24 AM
Content Hashsha256:c0bab2e83af513ff...
Git SHAc8036a378936
Bump Typepatch
──Files
Files (1 file, 4.4 KB)
SKILL.md4.4 KBactive
SKILL.md · 209 lines · 4.4 KB
version: "1.0.1" name: angular-core description: > Angular core patterns: standalone components, signals, inject, control flow, zoneless. Trigger: When creating Angular components, using signals, or setting up zoneless. metadata: author: gentleman-programming version: "1.0"
Standalone Components (REQUIRED)
Components are standalone by default. Do NOT set standalone: true.
typescript
@Component({selector: 'app-user',imports: [CommonModule],changeDetection: ChangeDetectionStrategy.OnPush,template: `...`})export class UserComponent {}
Input/Output Functions (REQUIRED)
typescript
// ✅ ALWAYS: Function-basedreadonly 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 for State (REQUIRED)
typescript
readonly count = signal(0);readonly doubled = computed(() => this.count() * 2);// Updatethis.count.set(5);this.count.update(prev => prev + 1);// Side effectseffect(() => localStorage.setItem('count', this.count().toString()));
NO Lifecycle Hooks (REQUIRED)
Signals replace lifecycle hooks. Do NOT use ngOnInit, ngOnChanges, ngOnDestroy.
typescript
// ❌ NEVER: Lifecycle hooksngOnInit() {this.loadUser();}ngOnChanges(changes: SimpleChanges) {if (changes['userId']) {this.loadUser();}}// ✅ ALWAYS: Signals + effectreadonly userId = input.required<string>();readonly user = signal<User | null>(null);private userEffect = effect(() => {// Runs automatically when userId() changesthis.loadUser(this.userId());});// ✅ For derived data, use computedreadonly displayName = computed(() => this.user()?.name ?? 'Guest');
When to Use What
| Need | Use | |
|---|---|---|
| React to input changes | effect() watching the input signal | |
| Derived/computed state | computed() | |
| Side effects (API calls, localStorage) | effect() | |
| Cleanup on destroy | DestroyRef + inject() |
typescript
// Cleanup exampleprivate readonly destroyRef = inject(DestroyRef);constructor() {const subscription = someObservable$.subscribe();this.destroyRef.onDestroy(() => subscription.unsubscribe());}
inject() Over Constructor (REQUIRED)
typescript
// ✅ ALWAYSprivate readonly http = inject(HttpClient);// ❌ NEVERconstructor(private http: HttpClient) {}
Native Control Flow (REQUIRED)
html
@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 - Only When Needed
Signals are the default. Use RxJS ONLY for complex async operations.
| Use Signals | Use RxJS | |
|---|---|---|
| Component state | Combining multiple streams | |
| Derived values | Debounce/throttle | |
| Simple async (single API call) | Race conditions | |
| Input/Output | WebSockets, real-time | |
| Complex error retry logic |
typescript
// ✅ Simple API call - use signalsreadonly 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 RxJSreadonly searchResults$ = this.searchTerm$.pipe(debounceTime(300),distinctUntilChanged(),switchMap(term => this.http.get<Results>(`/api/search?q=${term}`)));// Convert to signal when needed in templatereadonly searchResults = toSignal(this.searchResults$, { initialValue: [] });
Zoneless Angular (REQUIRED)
Angular is zoneless. Use provideZonelessChangeDetection().
typescript
bootstrapApplication(AppComponent, {providers: [provideZonelessChangeDetection()]});
Remove ZoneJS:
bash
npm uninstall zone.js
Remove from angular.json polyfills: zone.js and zone.js/testing.
Zoneless Requirements
- Use
OnPushchange detection - Use signals for state (auto-notifies Angular)
- Use
AsyncPipefor observables - Use
markForCheck()when needed
Resources
- https://angular.dev/guide/signals
- https://angular.dev/guide/templates/control-flow
- https://angular.dev/guide/zoneless