Skip to content
Back to skills

Kramme Connect Modernize Angular

ASecurity

Use this Skill when working in the Connect monorepo and needing to modernize legacy Angular components.

  • 57 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added May 31, 2026
developmenttypescriptgoangularnoderefactoringapifrontendperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 31, 2026

npx -y skills add diegosouzapw/awesome-omni-skill --skill kramme-connect-modernize-angular --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Kramme Connect Modernize Angular?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Kramme Connect Modernize Angular
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/diegosouzapw-kramme-connect-modernize-angular/badge)](https://www.skillsdirectory.com/skills/diegosouzapw-kramme-connect-modernize-angular)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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
- **NEVER** — Strong prohibition, exceptions are very rare and must be explicitly approved
- **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

**EXAMPLE:**

```typescript
deleteItem(): void {
  this.#dialog
    .open(ConfirmDeleteModalComponent, {
      data: { itemName: this.data$.value?.name },
    })
    .afterClosed()
    .subscribe((confirmed) => {
      if (confirmed) {
        this.#componentStore.deleteItem();
      }
    });
}
```

##### User Feedback

- **ALWAYS** use `successMessage` in ApiAction definitions (not manual toasts in stores)
- **ALWAYS** use `CoSnackService` only for local operations (cancel, info messages)
- **NEVER** show success before API call completes

**EXAMPLE - Success Messages:**

```typescript
// ❌ WRONG - shows before API completes
readonly saveChanges = this.effect<void>(
  pipe(
    tap(() => {
      this.#store.dispatch(updateAction.start(this.form.getRawValue()));
      this.#snacks.success('Saved!'); // ← BAD
    })
  )
);

// ✅ CORRECT - shows only on actual success
export const updateAction = new ApiAction<State, Input, Output>(
  'Entity',
  'Update',
  'Feature',
  {
    showErrors: true,
    successMessage: 'Saved!', // ← GOOD
  }
);
```

##### Copy to Clipboard

- **PREFER** adding copy-to-clipboard buttons for IDs

**EXAMPLE:**

```html
<button
  matSuffix
  mat-icon-button
  matTooltip="Copy to clipboard"
  [cdkCopyToClipboard]="form.controls.id.value"
  (cdkCopyToClipboardCopied)="onIdCopied($event)"
>
  <fa-icon [icon]="copyIcon" />
</button>
```

---

#### Phase 6: Verification

- **ALWAYS** run lint: `corepack yarn nx lint <library-name>`
- **ALWAYS** check for:
  - No manual subscriptions in components
  - All effects use `pipe()` directly
  - Forms have proper type annotations
  - No `any` types
  - Proper change detection strategy
- **ALWAYS** test:
  - Form initialization
  - Save/cancel flows
  - Conditional field logic
  - Error handling
  - User feedback

---

### Common Patterns

See `resources/patterns.md` for detailed examples of conditional field disabling and nonNullable form controls.

---

### Critical Rules

#### Forms

- **NEVER** store forms in ComponentStore state
  - **ALWAYS** define forms as class properties in the store
- **ALWAYS** add `nonNullable: true` to form controls
- **ALWAYS** use typed `FormGroup` and `FormControl` (not `FormNode`)
- **ALWAYS** define form controls interface

#### Effects

- **ALWAYS** use `pipe()` directly: `this.effect<Type>(pipe(...))`
- **NEVER** use arrow functions: `this.effect<Type>((param$) => param$.pipe(...))`
- **ALWAYS** use proper type narrowing with filter

#### Subscriptions

- **NEVER** use manual subscriptions in components
  - **NOTE**: ComponentStore handles cleanup automatically
- **NEVER** use `DestroyRef` and `takeUntilDestroyed` for ComponentStore subscriptions
- **ALWAYS** wire observables directly to updaters/effects

#### User Feedback

- **NEVER** show success toasts before API calls complete
- **ALWAYS** use `successMessage` in ApiAction definitions
- **ALWAYS** use `CoSnackService` only for local operations

#### TypeScript

- **NEVER** use `any` types
  - **ALWAYS** use `unknown` when type is uncertain
- **ALWAYS** use ECMAScript `#privateField` syntax for encapsulation
- **NEVER** use the `public` or `private` keywords in TypeScript class members

#### State Management

- **NEVER** use `ComponentStore.get()`
  - **ALWAYS** read state via selectors
- **NEVER** keep empty effects

---

### Migration Checklist

See `resources/checklist.md` for the full phase-by-phase migration checklist.

---

### Additional Best Practices from AGENTS.md

- **ALWAYS** check AGENTS.md for the latest definite best practices

#### Angular Components

- **ALWAYS** set `changeDetection: ChangeDetectionStrategy.OnPush` in `@Component` decorator for new components
- **ALWAYS** use separate HTML files (do NOT use inline templates)
- **ALWAYS** place all `inject()` calls first in the class as readonly fields
- **ALWAYS** place `@Input` and `@Output` properties second in the class
- **ALWAYS** use `class` bindings instead of `ngClass`
- **ALWAYS** use `style` bindings instead of `ngStyle`
- **ALWAYS** use pipes for data transformation in templates, not methods in the component class

#### UI and Styling

- **PREFER** Angular Material/CDK for complex, interactive UI
- **NEVER** override internal APIs in Angular Material components
- **ALWAYS** use Tailwind for layout, spacing, and simple styling
- **ALWAYS** use `tw-` prefix (enforced in `libs/co/ui-tailwind-preset/tailwind.config.js`)
- **ALWAYS** define repeated patterns in CSS layer using `@apply` directive

#### FontAwesome Icons

- **ALWAYS** use FontAwesome icons via the `@fortawesome/angular-fontawesome` package
- **ALWAYS** use `<fa-icon>` component, not `<i>` tags with CSS classes
- **ALWAYS** import from `@fortawesome/pro-*-svg-icons` (not free packages)
- **ALWAYS** store icons as readonly component properties; prefer regular style by default

---

### Before Submitting Code Review

- **ALWAYS** ensure all affected tests pass locally
- **ALWAYS** run formatting: `yarn run format` (from `Connect/ng-app-monolith`)
- **ALWAYS** run linting: `yarn exec nx affected --targets=lint,test --skip-nx-cache`
- **ALWAYS** verify no linting errors are present
- **ALWAYS** ensure code follows established patterns as outlined in AGENTS.md

---

### Reference Files

**ALWAYS** refer to these files for complete examples:

- `libs/connect/cms/qa/feature/src/lib/edit-topic/cms-qa-edit-topic.store.ts`
- `libs/connect/cms/qa/feature/src/lib/settings-page/cms-qa-settings-page.store.ts`
- `libs/connect/cms/qa/feature/src/lib/topics-page/cms-qa-topics-page.store.ts`
- `AGENTS.md` - Angular Development Patterns section

### Examples

See the Instructions section.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.