Instrucciones de origen · Vista previa de solo lectura
name
kramme:connect:modernize-angular
description
Use this Skill when working in the Connect monorepo and needing to modernize legacy Angular components.
disable-model-invocation
false
user-invocable
true
Connect - Modernize Legacy Angular Component
Instructions
When to use this skill:
You're working in the Connect monorepo
You need to refactor legacy Angular components to modern patterns
Component extends legacy FormComponent or BaseComponent
Component uses @Select decorators for state management
Component uses FormNode instead of typed FormGroup
Component doesn't use ChangeDetectionStrategy.OnPush
Component has manual subscription management
Component dispatches actions directly instead of using ComponentStore
Context: Connect's frontend is modernizing Angular components to use NgRx ComponentStore for state management, OnPush change detection, standalone components, and proper TypeScript typing. This provides better type safety, performance, and maintainability.
Guideline Keywords
ALWAYS — Mandatory requirement, exceptions are very rare and must be explicitly approved
— Strong prohibition, exceptions are very rare and must be explicitly approved
NEVER
PREFER — Strong recommendation, exceptions allowed with justification
CAN — Optional, developer's discretion
NOTE — Context, rationale, or clarification
EXAMPLE — Illustrative example
Strictness hierarchy: ALWAYS/NEVER > PREFER > CAN > NOTE/EXAMPLE
Reference Implementation
ALWAYS refer to the Q&A components refactoring as the reference implementation:
libs/connect/cms/qa/feature/src/lib/edit-topic/ - Edit topic component with form management
libs/connect/cms/qa/feature/src/lib/settings-page/ - Settings page with conditional form logic
libs/connect/cms/qa/feature/src/lib/topics-page/ - Topics page with complex state
Migration Process
Phase 1: Assessment
ALWAYS read all component files before starting:
Component TypeScript file
Component template
Component styles (if any)
Related store/state files
ALWAYS identify patterns to migrate:
Legacy base class usage (extends FormComponent, extends BaseComponent)
@Select decorators for state
FormNode usage
Manual subscriptions (subscribe(), takeUntil())
Direct action dispatching
Lifecycle hooks (onInit() vs ngOnInit())
ALWAYS identify business logic:
Form management
State updates
API calls
Conditional field logic
User interactions
Phase 2: Create ComponentStore
ALWAYS create the store file in the same directory as the component (e.g., component-name.store.ts)
ALWAYS define form controls interface separately from state
ALWAYS define forms as class properties, NOT in state
ALWAYS extract initialState as a constant
ALWAYS use readonly for immutability
ALWAYS use ECMAScript #privateFields for encapsulation
ALWAYS use proper type narrowing in effects with filter: (tuple): tuple is [void, DataType] => tuple[1] !== null
ALWAYS use pipe() directly in effects: this.effect<Type>(pipe(...)) not this.effect<Type>((param$) => param$.pipe(...))
EXAMPLE: See resources/examples/component-store.ts for a complete ComponentStore reference implementation.
Phase 3: Refactor Component
ALWAYS add ChangeDetectionStrategy.OnPush
ALWAYS add standalone: true
ALWAYS add ComponentStore to providers array
ALWAYS use inject() for dependency injection
ALWAYS place all inject() calls first in the class as readonly fields
ALWAYS use ECMAScript #privateField syntax for private members
NEVER use the public or private keywords in TypeScript
ALWAYS remove base class extensions
ALWAYS remove @Select decorators
ALWAYS remove manual subscriptions
ALWAYS remove DestroyRef and takeUntilDestroyed (ComponentStore handles cleanup)
EXAMPLE: See resources/examples/component.ts for a complete component reference implementation.
Phase 4: Update Template
ALWAYS use native control flow (@if, @for, @switch) instead of *ngIf, *ngFor, *ngSwitch
ALWAYS use the *ngrxLet directive or ngrxPush pipe to handle Observables
ALWAYS prefer the ngrxPush pipe over async for one-off async bindings
PREFER not using *ngrxLet or ngrxPush multiple times for the same Observable; instead assign it to a template variable using @let
PREFER adding animations for conditional UI
ALWAYS use form bindings with proper type checking
EXAMPLE: See resources/examples/template.html for native control flow and form binding examples.
Phase 5: UX Enhancements
Confirmation Modals
ALWAYS add confirmation modals for destructive actions
ALWAYS use MatDialog to open modals
ALWAYS subscribe to afterClosed() and only proceed if confirmed