Skip to content

Project: [Your App Name]

[One-paragraph description of what your application does.]

Tech Stack

  • Angular [version] (standalone components, signals)
  • TypeScript 5.x (strict mode)
  • [UI library: Angular Material / PrimeNG / Tailwind CSS + DaisyUI / etc.]
  • RxJS 7.x (HTTP only; prefer signals for state)
  • [State management: signals / NgRx Store / NgRx SignalStore / etc.]
  • [Backend: REST API at /api, GraphQL, etc.]
  • [Testing: Jest / Vitest / Karma+Jasmine]
  • [Build system: Angular CLI / Nx]

Commands

For Angular CLI projects:

bash
ng serve                    # Dev server at http://localhost:4200
ng build                    # Production build
ng test                     # Run unit tests
ng test --watch=false       # Run unit tests once (CI)
ng lint                     # Lint with ESLint
ng e2e                      # End-to-end tests

For Nx monorepo projects:

bash
nx serve <app-name>         # Dev server
nx build <app-name>         # Production build
nx test <app-name>          # Run unit tests for an app
nx test <lib-name>          # Run unit tests for a library
nx lint <project-name>      # Lint a specific project
nx run-many -t test         # Run tests across all projects
nx run-many -t lint         # Lint all projects
nx affected -t test         # Run tests only for affected projects

Project Structure

Option A: Standard Angular CLI project

src/app/
  core/               # Singleton services, guards, interceptors (app-wide)
  shared/             # Reusable components, directives, pipes
  features/           # Feature modules (lazy-loaded route groups)
    feature-name/
      components/
        pages/        # Container (smart) components
        views/        # Presentational (dumb) components
      services/       # Feature-specific services
      models/         # Interfaces and types
      feature.routes.ts
  app.component.ts
  app.routes.ts
  app.config.ts

Option B: Nx monorepo project

apps/
  <app-name>/
    src/app/
      components/
        pages/          # Container (smart) components
        views/          # Presentational (dumb) components
      config/           # App-level configuration and constants
      types/            # App-level types
      app.component.ts
      app.routes.ts
      app.config.ts
libs/ui/
  component-library/    # Reusable UI components (buttons, inputs, cards)
  data-access/          # State management, API clients, HTTP services
    src/lib/
      api/              # REST API service layer
      clients/          # HTTP and WebSocket clients
      state/            # NgRx feature slices (actions, reducers, selectors, effects)
      config/           # State configuration providers
  shared-ui/            # Shared services, guards, directives, pipes
    src/
      components/       # Shared components
      directives/       # Custom directives
      guards/           # Route guards
      services/         # Shared services (navigation, theme, dialogs)
      pipes/            # Custom pipes
      types/            # Shared types
  features/             # Feature libraries (lazy-loaded)
    feature-name/
      src/lib/
        components/
          pages/        # Container components
          views/        # Presentational components
        feature.routes.ts

Angular Conventions

Components

  • All components are standalone (standalone: true). Do NOT use NgModules.
  • Always use ChangeDetectionStrategy.OnPush.
  • Use input() and output() functions, not @Input() / @Output() decorators.
  • Use computed() for derived state.
  • Use inline templates for components under ~30 lines of HTML; external files otherwise.
  • Component selector prefix: [prefix]- (e.g., app-user-list or ngx-app-user-list).
  • Distinguish between pages (container/smart components that manage state and routing) and views (presentational/dumb components that receive data via inputs and emit via outputs).

Templates

  • Use @if, @for, @switch control flow. Do NOT use *ngIf, *ngFor, [ngSwitch].
  • Always include track expression in @for blocks.
  • Prefer class and style bindings over ngClass / ngStyle.
  • No arrow functions or complex logic in templates.
  • Use the async pipe for observables that are not converted to signals.

State and Reactivity

Option A: Signal-based state (for simpler apps or per-component state)

  • Use signals (signal(), computed(), effect()) for component and service state.
  • Use set() or update() to modify signals. Never use mutate().
  • Keep signals private (private readonly _state = signal(...)) with public readonly selectors.
  • Use RxJS for HTTP calls and event streams; convert to signals at service boundaries where appropriate.

Option B: NgRx Store (for complex apps with shared state)

  • Use createActionGroup() to define feature actions with typed events.
  • Use createReducer() with on() handlers for immutable state transitions.
  • Use createFeatureSelector() and createSelector() for memoized state selection.
  • Use injectable effects with Actions and createEffect() for side effects.
  • Use EntityAdapter for normalized collection state.
  • Dispatch NotificationsActions (or equivalent) for user-facing error handling in effects.
  • Register state with provideState() and provideEffects() in app configuration.

Services

  • Use providedIn: 'root' for app-wide services.
  • Use inject() function for dependency injection. Do NOT use constructor parameter injection.
  • One service per file, one responsibility per service.

Routing

  • Use loadComponent for single-component routes and loadChildren for feature route groups.
  • Each feature folder has its own *.routes.ts file.
  • Use functional route guards (not class-based).

Forms

  • Use reactive forms exclusively (FormBuilder, FormGroup, FormControl).
  • Use fb.nonNullable.group({...}) for type-safe form creation.
  • Do NOT use template-driven forms.

HTTP

  • Use functional interceptors (HttpInterceptorFn).
  • Configure with provideHttpClient(withInterceptors([...])) in app.config.ts.

Images

  • Use NgOptimizedImage for static images (not for base64 inline).

Styling

  • Use Tailwind CSS utility classes for styling. Avoid writing custom CSS when utility classes suffice.
  • Use DaisyUI component classes (e.g., btn, card, badge) for consistent design tokens.
  • Keep component-specific styles minimal; prefer utility composition.

Testing

  • Place test files next to source files (component.spec.ts alongside component.ts).
  • Use TestBed.configureTestingModule with standalone component imports.
  • Mock services with jest.fn() / jest.spyOn() (Jest) or jasmine.createSpyObj (Jasmine).
  • Test signal state transitions (initial -> loading -> loaded/error).
  • For NgRx: test reducers as pure functions, test selectors with mock state, test effects with provideMockActions.

TypeScript Conventions

  • Strict mode enabled (strict: true in tsconfig.json).
  • Use unknown instead of any for ambiguous types.
  • Rely on type inference when types are self-evident.
  • Use interface for data models, not class.
  • Export types and interfaces from a models/ or types/ folder within each feature.

Nx Conventions

  • Respect module boundary constraints defined in ESLint (@nx/enforce-module-boundaries).
  • Libraries use scope tags (e.g., scope:feature, scope:data-access, scope:component-lib) to enforce dependency rules.
  • Do not import directly from library src/ internals; use the public API (index.ts barrel exports).
  • Use nx affected commands for CI to only test/build what changed.

Domain Context

Do NOT

  • Do not create NgModules.
  • Do not use *ngIf, *ngFor, or [ngSwitch].
  • Do not use @Input() / @Output() / @HostBinding() / @HostListener() decorators.
  • Do not use constructor injection.
  • Do not use any type.
  • Do not use template-driven forms.
  • Do not add comments unless the logic is non-obvious.
  • Do not create abstractions for one-time operations.
  • Do not use trackBy with *ngFor (use track in @for blocks instead).
  • Do not import from library internal paths; use barrel exports.

Copyright © MSG Systems Romania AI Labs