AgentSkillsCN

angular

适用于涉及Angular 17+代码的项目开发。

SKILL.md
--- frontmatter
name: angular
description: "Use when working on projects involving Angular 17+ code."

Angular (17+)

References


Code Quality Checks

Check project for existing scripts first. Use npm run <script> if available. Always use ./gradlew, never bare gradle.

bash
# Frontend (check project scripts first; fall back to these)
npm run lint -- --fix               # or: npx ng lint --fix
npm run build                       # or: npx ng build
npm test                            # Jest (or project runner)
npm run e2e                         # Cypress (if present)

# Backend (Spring Boot — always run for embedded projects)
./gradlew spotlessApply             # 1. Format
./gradlew compileJava               # 2. Compile
./gradlew checkstyleMain spotbugsMain pmdMain  # 3. Static analysis
./gradlew test                      # 4. Tests
./gradlew dependencyCheckAnalyze    # 5. Vulnerability scan
./gradlew build                     # 6. Full build (includes Angular via gradle-node-plugin)

Project Structure

All new Angular apps MUST be embedded in Spring Boot. Angular source lives in src/main/webapp/, build output goes to build/resources/main/static (Gradle).

code
project-root/
├── src/
│   ├── main/
│   │   ├── java/com/example/app/
│   │   │   ├── Application.java
│   │   │   ├── domain/
│   │   │   │   ├── model/          # Entities, value objects, enums
│   │   │   │   ├── port/in/        # Inbound ports (use cases)
│   │   │   │   ├── port/out/       # Outbound ports (driven)
│   │   │   │   └── service/        # Domain service implementations
│   │   │   ├── adapter/
│   │   │   │   ├── in/web/         # Controllers, request/response DTOs
│   │   │   │   └── out/persistence/# Repositories
│   │   │   └── config/             # @Configuration classes
│   │   ├── resources/
│   │   │   └── application.yml
│   │   └── webapp/                   # Angular source
│   │       ├── app/
│   │       │   ├── core/             # Singleton services, guards, interceptors
│   │       │   ├── shared/           # Reusable components, directives, pipes
│   │       │   ├── features/
│   │       │   │   └── <feature>/
│   │       │   │       ├── components/
│   │       │   │       ├── services/
│   │       │   │       ├── models/
│   │       │   │       └── <feature>.routes.ts
│   │       │   ├── app.component.ts
│   │       │   ├── app.routes.ts
│   │       │   └── app.config.ts
│   │       ├── index.html
│   │       ├── main.ts
│   │       └── styles.scss
│   └── test/java/com/example/app/
├── angular.json
├── package.json
├── tsconfig.json
├── tsconfig.app.json
├── build.gradle
├── gradlew / gradlew.bat
└── gradle/wrapper/

Testing

Check project for existing test framework first.

  • Unit tests: Jest for new projects. Use @angular-builders/jest or jest-preset-angular.
  • E2E: Cypress for new projects.
  • Component tests: Use Angular TestBed. Prefer shallow tests with stub children.
  • Service tests: Use TestBed.inject(). Mock HTTP with HttpClientTestingModule / provideHttpClientTesting().
  • Every new component, service, pipe, and directive must have a corresponding test.
  • Test naming: should do X when condition Y.
  • Use ComponentFixture and DebugElement for DOM assertions.

See reference/examples.md for Jest setup, TestBed patterns, and Cypress configuration.


Naming Conventions

ItemConventionExample
ComponentsPascalCase + Component suffixUserListComponent
ServicesPascalCase + Service suffixAuthService
DirectivesPascalCase + Directive suffixHighlightDirective
PipesPascalCase + Pipe suffixDateFormatPipe
GuardsPascalCase + Guard suffix or camelCase fnAuthGuard, authGuard
InterceptorsPascalCase + Interceptor suffix or fnTokenInterceptor
Models/InterfacesPascalCase (no I prefix)User, OrderItem
Fileskebab-case + type suffixuser-list.component.ts
Selectorsapp- prefix, kebab-caseapp-user-list
ObservablescamelCase with $ suffixusers$, loading$
SignalscamelCase, NO $ suffixcount, isLoading
Feature folderskebab-caseuser-management/
Route files<feature>.routes.tsuser.routes.ts
ConstantsSCREAMING_SNAKEMAX_RETRIES

Idioms

  • Standalone components by default. No NgModules for new code. Use imports array on @Component.
  • DI style — mixed. inject() for functional contexts (guards, interceptors, resolvers). Check project first for components/services — use whichever pattern is established. New projects: inject().
  • Signals for synchronous state. Use signal(), computed(), effect(). No $ suffix on signals.
  • Observables for async streams. HTTP calls, WebSockets, router events. Use $ suffix.
  • OnPush change detection on all new components.
  • SCSS for new projects. Check project for existing preprocessor.
  • strictTemplates: true mandatory in tsconfig.json for new projects.
  • SPA only for new projects. Do not change existing SSR setups.
  • Typed reactive forms. Use FormControl<T>, FormGroup, FormArray with explicit types.
  • AsyncPipe or toSignal() in templates — never manual .subscribe() for template data.
  • DestroyRef + takeUntilDestroyed() for subscription cleanup.

See reference/examples.md for standalone component, signal service, and DI patterns.


Design Patterns

  • Facade services — Encapsulate feature state with signals. Expose signal() and computed() values. Mutate via methods.
  • Smart/dumb components — Container components handle logic and inject services. Presentational components use @Input/@Output (or signal inputs) only.
  • Feature-based modules — Group by domain feature, not by technical layer.
  • Lazy-loaded routesloadComponent / loadChildren for feature routes.
  • Interceptors — Functional interceptors (HttpInterceptorFn) for auth tokens, error handling, logging.
  • Guards — Functional guards (CanActivateFn) for route protection.
  • Resolver — Functional resolvers for pre-fetching route data.
  • Adapter pattern — Wrap third-party libraries behind Angular services.

Anti-Patterns

NEVER do these:

  • NgModules for new code — Use standalone components, directives, pipes.
  • Manual .subscribe() in components for template data — Use AsyncPipe or toSignal().
  • Business logic in components — Extract to services.
  • any type — Use proper TypeScript types. Never suppress with @ts-ignore.
  • NgRx/NGXS in new projects — Use signals + facade services. Only use a state library if the project already has one.
  • New barrel files (index.ts) — Direct imports only. Barrel files cause tree-shaking issues. If project already uses them, follow existing convention.
  • Relative imports across features — Use TypeScript path aliases (@app/core, @app/shared).
  • setTimeout/setInterval for Angular state — Use signals, RxJS timers, or afterNextRender.
  • Component inheritance — Use composition (services, directives, content projection).
  • Untyped forms — Always use typed reactive forms.
  • $ suffix on signals — Reserve $ exclusively for Observables.

Error Handling

  • Global error handler via ErrorHandler — catch unhandled exceptions.
  • HTTP errors via functional interceptor — centralized response error handling.
  • Show user-facing errors through a notification service (toast/snackbar).
  • Log errors to a backend logging endpoint in production.
  • Never swallow errors silently in catchError — always log or re-throw.
  • Use retry / retryWhen for transient HTTP failures.

See reference/examples.md for error interceptor and global error handler patterns.


Spring Boot Integration

All new Angular apps are embedded in Spring Boot. Apply the Java skill conventions on the backend side.

Build Integration

  • Use gradle-node-plugin wired into processResources to build Angular as part of the Spring Boot build.
  • Angular build output goes to build/resources/main/static.
  • Single deployable artifact: one Spring Boot JAR serves both API and SPA.

Backend Rules (from Java skill)

  • Hexagonal architecture on the backend.
  • Constructor injection exclusively (Java side).
  • No Lombok — ever.
  • Records for DTOs.
  • Spring Cloud Config Server for externalized config.
  • SpringDoc OpenAPI + Swagger UI mandatory.

SPA Forwarding Controller

A forwarding controller must serve index.html for all SPA routes. Regex excludes /api, /v3, /swagger-ui, /actuator, /public, /error.

CORS

CORS configuration only active under local Spring profile. Production serves both frontend and API from the same origin — no CORS needed.

Externalized Configuration

  • Angular fetches runtime config from /api/config at bootstrap via APP_INITIALIZER.
  • Backend serves config values from Spring Cloud Config Server — single source of truth.
  • Never hardcode environment-specific values (API URLs, feature flags) in Angular source.
  • Use an InjectionToken<AppConfig> to inject config throughout the app.
  • Use @RefreshScope on the config bean so /actuator/refresh reloads values from Config Server.
  • Angular picks up refreshed config on next page load — no polling or push mechanism needed.

API Communication

  • Use Angular HttpClient with a base URL from injected AppConfig.
  • Define TypeScript interfaces/types matching backend DTOs (Records on backend).
  • Use functional interceptors for auth tokens and error handling.

See reference/examples.md for build.gradle config, forwarding controller, and CORS setup.


Components & Change Detection

  • OnPush on all new components. Set changeDetection: ChangeDetectionStrategy.OnPush.
  • Signal inputs (input(), input.required()) over @Input() decorator for new code.
  • Signal outputs (output()) over @Output() + EventEmitter for new code.
  • @if, @for, @switch — Use built-in control flow (Angular 17+) for new code. Existing codebases: follow established template style; don't mass-migrate.
  • @defer — Lazy-load heavy components in templates.
  • Content projection (ng-content, @ContentChild) over component inheritance.
  • afterNextRender / afterRender for DOM manipulation instead of ngAfterViewInit hacks.

Reactivity & State

  • Signals for component/service state. signal() for writable state, computed() for derived values.
  • Facade services — One per feature. Encapsulate state as private signals, expose public read-only signals and computed values.
  • effect() — Use sparingly. Prefer computed(). Effects are for side effects (logging, analytics, localStorage sync).
  • toSignal() / toObservable() — Bridge between signals and RxJS when needed.
  • No NgRx/NGXS for new projects. Signals + facade services cover most state needs.

See reference/examples.md for facade service pattern with signals.


Routing

  • Standalone route configs. Use Routes arrays in <feature>.routes.ts files.
  • Lazy loading. loadComponent for single routes, loadChildren for feature route groups.
  • Functional guards and resolvers. Use CanActivateFn, ResolveFn with inject().
  • Route params via signals. Use input() with withComponentInputBinding() in app.config.ts.
  • Nested routes for feature layouts with <router-outlet>.

See reference/examples.md for route configuration and lazy loading patterns.


Forms

  • Typed reactive forms only. FormControl<string>, FormGroup, FormArray.
  • No template-driven forms in new code.
  • Custom validators as pure functions returning ValidatorFn.
  • Error display — Reusable error message component/directive.
  • Form submission — Disable submit button while pending. Show validation on blur or submit.

Documentation

  • All public services, components, and interfaces must have JSDoc/TSDoc.
  • Document @Input() / input() and @Output() / output() with descriptions.
  • README in each feature folder for complex features.
  • API contracts documented via SpringDoc OpenAPI on the backend.

Dependencies

  • Check package.json before adding any dependency.
  • UI library: Check project first. Prefer Angular Material if none present.
  • Linting: @angular-eslint only. No separate Prettier.
  • State: Signals + services. No NgRx/NGXS unless already in project.
  • HTTP: Angular HttpClient only. No axios.
  • Run npm audit after adding new dependencies.
  • Keep Angular, Angular CLI, and Angular Material versions aligned.

Performance Considerations

  • OnPush change detection on all components.
  • Lazy loading — Routes, @defer blocks, dynamic imports.
  • trackBy in @for loops — Use track expression to minimize DOM churn.
  • Pure pipes over method calls in templates.
  • runOutsideAngular for non-UI event listeners (scroll, resize).
  • Preloading strategiesPreloadAllModules or custom strategies for lazy routes.
  • Bundle analysisnpx ng build --stats-json + webpack-bundle-analyzer.
  • Image optimization — Use NgOptimizedImage directive.

Troubleshooting

ErrorCauseFix
NG0100: ExpressionChangedAfterItHasBeenCheckedValue changed between change detection cyclesUse signals, or move logic to afterNextRender
NG0200: Circular dependency in DIService A injects B, B injects ARefactor with events or mediator service
NG0300: Multiple components match selectorDuplicate selectorsUse unique app- prefixed selectors
NG0303: Can't bind to 'X' since it isn't a known propertyMissing import in standalone componentAdd component/directive to imports array
NullInjectorError: No provider for XService not providedAdd to providers in component or app.config.ts
NG05104: Signal is read-onlyTrying to .set() on computed()Use signal() for writable state
404 on page refresh (SPA)Server doesn't forward SPA routesAdd forwarding controller (see Spring Boot Integration)
Build out of memoryLarge app, insufficient heapnode --max_old_space_size=8192 node_modules/.bin/ng build
CORS errors in devMissing CORS configEnable CORS under local Spring profile only

Design Principles

  • SOLID — Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion.
  • DRY — Don't repeat yourself, but don't extract prematurely.
  • KISS — Straightforward solutions over clever abstractions.
  • YAGNI — Don't build for hypothetical requirements.
  • Composition over inheritance — Services, directives, content projection.
  • Separation of concerns — Components handle UI, services handle logic and state.
  • Unidirectional data flow — Data flows down via inputs, events flow up via outputs.
  • Single source of truth — One facade service owns each piece of state.

Checklist Before Completion

  • Lint passes: npm run lint (or npx ng lint)
  • Build succeeds: npm run build (or npx ng build)
  • All tests pass: npm test
  • E2E passes (if present): npm run e2e
  • Backend checks pass: ./gradlew compileJava, ./gradlew test
  • Full Spring Boot build: ./gradlew build
  • strictTemplates: true in tsconfig.json
  • All components use OnPush change detection
  • All components are standalone (no NgModules)
  • Built-in control flow (@if, @for) used — no structural directives
  • Signals for synchronous state, Observables for async streams
  • No manual .subscribe() for template data
  • No any types
  • No Lombok on backend
  • Facade services used for feature state (no NgRx/NGXS)
  • TypeScript interfaces match backend DTOs
  • Config externalized via /api/config + APP_INITIALIZER (no hardcoded env values)
  • No secrets in source or logs
  • npm audit clean
  • Backend endpoints have SpringDoc OpenAPI annotations