CtrlK
BlogDocsLog inGet started
Tessl Logo

angular-core

Angular core patterns: standalone components, signals, inject, control flow, zoneless. Trigger: When creating Angular components, using signals, or setting up zoneless.

Install with Tessl CLI

npx tessl i github:Yoizen/dev-ai-workflow --skill angular-core
What are skills?

83

Does it follow best practices?

Validation for skill structure

SKILL.md
Review
Evals

Standalone Components (REQUIRED)

Components are standalone by default. Do NOT set standalone: true.

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

Input/Output Functions (REQUIRED)

// ✅ 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 for State (REQUIRED)

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()));

NO Lifecycle Hooks (REQUIRED)

Signals replace lifecycle hooks. Do NOT use 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');

When to Use What

NeedUse
React to input changeseffect() watching the input signal
Derived/computed statecomputed()
Side effects (API calls, localStorage)effect()
Cleanup on destroyDestroyRef + inject()
// Cleanup example
private readonly destroyRef = inject(DestroyRef);

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

inject() Over Constructor (REQUIRED)

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

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

Native Control Flow (REQUIRED)

@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 SignalsUse RxJS
Component stateCombining multiple streams
Derived valuesDebounce/throttle
Simple async (single API call)Race conditions
Input/OutputWebSockets, real-time
Complex error retry logic
// ✅ 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: [] });

Zoneless Angular (REQUIRED)

Angular is zoneless. Use provideZonelessChangeDetection().

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

Remove ZoneJS:

npm uninstall zone.js

Remove from angular.json polyfills: zone.js and zone.js/testing.

Zoneless Requirements

  • Use OnPush change detection
  • Use signals for state (auto-notifies Angular)
  • Use AsyncPipe for observables
  • Use markForCheck() when needed

Resources

  • https://angular.dev/guide/signals
  • https://angular.dev/guide/templates/control-flow
  • https://angular.dev/guide/zoneless
Repository
Yoizen/dev-ai-workflow
Last updated
Created

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.