Migration Plan: Angular 15 → Angular 20 (Tanuki Shop)
## Overview This issue tracks the complete migration of the **Tanuki Shop** application from **Angular 15.2** to **Angular 20**. The migration spans 5 major versions (15 → 16 → 17 → 18 → 19 → 20) and requires significant architectural changes, including the shift from NgModules to standalone components, new control flow syntax, signal-based reactivity, and the new build system. ### Current State Analysis | Area | Current | Target | |---|---|---| | Angular | 15.2.x | 20.x | | Angular Material | 15.2.9 | 20.x | | TypeScript | 4.9.4 | 5.8+ | | Zone.js | 0.12.0 | Remove (optional zoneless) | | RxJS | 7.8.x | 7.8+ | | Node.js | Unknown | 20.x / 22.x | | Build system | `@angular-devkit/build-angular:browser` | `@angular/build:application` (esbuild) | | Test runner | Karma + Jasmine | Vitest / Web Test Runner | ### Project Inventory - **11 components**: AppComponent, NavbarComponent, SearchResultComponent, LoginComponent, RegisterComponent, BasketComponent, ProductDetailsComponent, ContactComponent, AboutComponent, PaymentComponent, OrderCompletionComponent - **3 services**: BasketService, ProductService, UserService (all `providedIn: 'root'`) - **2 models**: Product, BasketItem - **1 NgModule** (`AppModule`) with 1 routing module (`AppRoutingModule`) - **13 Angular Material modules** imported individually - **Patterns used**: `BehaviorSubject`, `Observable`, constructor-based DI, `ngOnInit`, template-driven + reactive forms, `styleUrls` (plural) --- ## Phase 1: Preparation & Angular 16 Update > **Goal**: Establish a safe baseline and reach Angular 16. ### 1.1 Pre-migration setup - [ ] Create a dedicated `migration/angular-20` branch - [ ] Ensure CI pipeline is green on the current codebase - [ ] Pin Node.js version to 18.x (minimum for Angular 16+) - [ ] Update `mise.toml` with the correct Node.js version ### 1.2 Update to Angular 16 - [ ] Run `ng update @angular/core@16 @angular/cli@16` - [ ] Run `ng update @angular/material@16 @angular/cdk@16` - [ ] Update TypeScript to `~5.1.0` - [ ] Update `zone.js` to `~0.13.0` - [ ] Resolve any breaking changes flagged by `ng update` - [ ] Verify build and test pass ### 1.3 Angular 16 adoption tasks - [ ] Begin converting components to **standalone** (available as developer preview in 16) - [ ] Replace `styleUrls` with `styleUrl` (singular) across all 11 components - [ ] Update `tsconfig.json`: remove `useDefineForClassFields: false` (no longer needed) --- ## Phase 2: Angular 17 - New Control Flow & Build System > **Goal**: Adopt the new template syntax and esbuild-based builder. ### 2.1 Update to Angular 17 - [ ] Run `ng update @angular/core@17 @angular/cli@17` - [ ] Run `ng update @angular/material@17 @angular/cdk@17` - [ ] Update TypeScript to `~5.2.0` - [ ] Update `zone.js` to `~0.14.0` ### 2.2 Migrate to standalone components - [ ] Run `ng generate @angular/core:standalone` schematic to automate conversion - [ ] Convert all 11 components to `standalone: true` - [ ] Move Material module imports into each component's `imports` array - [ ] Remove `AppModule` (`app.module.ts`) - [ ] Remove `AppRoutingModule` - move routes to `app.routes.ts` - [ ] Update `main.ts` to use `bootstrapApplication()` with `provideRouter()`, `provideAnimationsAsync()` ### 2.3 Migrate to new control flow syntax - [ ] Run `ng generate @angular/core:control-flow` schematic - [ ] Replace `*ngIf` → `@if` / `@else` in all templates - [ ] Replace `*ngFor` → `@for` (with required `track` expression) in all templates - [ ] Replace `[ngSwitch]` → `@switch` if used - [ ] Remove `CommonModule` / `NgIf` / `NgFor` imports (no longer needed with built-in control flow) ### 2.4 Switch to the new build system - [ ] Change `angular.json` builder from `@angular-devkit/build-angular:browser` to `@angular-devkit/build-angular:application` - [ ] Replace `main` with `browser` entry point in build options - [ ] Remove deprecated options: `buildOptimizer`, `vendorChunk`, `namedChunks` - [ ] Update `browserTarget` → `buildTarget` in serve/extract-i18n configs - [ ] Verify build output and dev server work correctly --- ## Phase 3: Angular 18 - Signals & Zoneless Prep > **Goal**: Adopt signal-based reactivity and prepare for zoneless operation. ### 3.1 Update to Angular 18 - [ ] Run `ng update @angular/core@18 @angular/cli@18` - [ ] Run `ng update @angular/material@18 @angular/cdk@18` - [ ] Update TypeScript to `~5.4.0` ### 3.2 Adopt Angular Signals - [ ] Refactor `NavbarComponent`: replace `itemCount` property with `signal()`, replace `isLoggedIn$` / `currentUser$` Observables with `toSignal()` - [ ] Refactor `SearchResultComponent`: convert `products` and `filteredProducts` to `signal()` - [ ] Refactor `BasketComponent`: convert `items` to use `toSignal()` from basket service - [ ] Refactor `ProductDetailsComponent`: convert `product` and `quantity` to `signal()` - [ ] Refactor `LoginComponent`, `RegisterComponent`: convert form fields to `signal()` where appropriate - [ ] Refactor `PaymentComponent`: convert form fields to `signal()` - [ ] Refactor `ContactComponent`: convert form fields to `signal()` - [ ] Consider converting `BasketService` and `ProductService` internal state from `BehaviorSubject` to `signal()` + `computed()` ### 3.3 Adopt new DI patterns - [ ] Replace constructor-based injection with `inject()` function in all 11 components and 3 services - [ ] Replace `ActivatedRoute` snapshot usage with `input()` for route params (e.g., `ProductDetailsComponent`) ### 3.4 Prepare for zoneless - [ ] Add `provideExperimentalZonelessChangeDetection()` to `app.config.ts` (experimental in 18) - [ ] Test all components for proper change detection without Zone.js - [ ] Fix any components that rely on implicit Zone.js change detection --- ## Phase 4: Angular 19 - Stabilization > **Goal**: Adopt stable APIs from Angular 19 and finalize modern patterns. ### 4.1 Update to Angular 19 - [ ] Run `ng update @angular/core@19 @angular/cli@19` - [ ] Run `ng update @angular/material@19 @angular/cdk@19` - [ ] Update TypeScript to `~5.6.0` ### 4.2 Finalize standalone & signals - [ ] `standalone: true` is now the default - remove explicit `standalone: true` from all component decorators - [ ] Ensure all `input()` / `output()` signal-based APIs are used where applicable - [ ] Replace any remaining `@Input()` / `@Output()` decorators with signal-based equivalents - [ ] Adopt `linkedSignal()` for two-way derived state if applicable - [ ] Adopt `resource()` API for async data loading if applicable ### 4.3 Migrate to stable zoneless - [ ] Replace `provideExperimentalZonelessChangeDetection()` with `provideZonelessChangeDetection()` (stable in 19) - [ ] Remove `zone.js` from `package.json` dependencies - [ ] Remove `zone.js` from `polyfills` in `angular.json` ### 4.4 Update test infrastructure - [ ] Migrate from Karma to **Web Test Runner** or **Vitest** - [ ] Remove `karma.conf.js`, `karma` and related devDependencies - [ ] Update `angular.json` test configuration - [ ] Ensure all existing tests pass with the new runner ### 4.5 Angular Material updates - [ ] Migrate to Material 3 (M3) theming if not already done - [ ] Replace `@angular/material/prebuilt-themes/indigo-pink.css` with M3 theme - [ ] Update any deprecated Material component APIs --- ## Phase 5: Angular 20 - Final Migration > **Goal**: Complete the migration to Angular 20. ### 5.1 Update to Angular 20 - [ ] Run `ng update @angular/core@20 @angular/cli@20` - [ ] Run `ng update @angular/material@20 @angular/cdk@20` - [ ] Update TypeScript to `~5.8.0` - [ ] Update Node.js to 20.x or 22.x ### 5.2 Adopt Angular 20 features - [ ] Adopt stabilized APIs introduced in Angular 20 - [ ] Review and adopt any new template syntax or compiler improvements - [ ] Ensure `angular.json` uses `@angular/build:application` builder (final form) - [ ] Review and apply any new performance optimizations (incremental hydration, etc.) ### 5.3 Final cleanup - [ ] Remove all deprecated API usage flagged by the Angular compiler - [ ] Remove `experimentalDecorators` from `tsconfig.json` (no longer needed) - [ ] Remove `downlevelIteration` from `tsconfig.json` - [ ] Update `moduleResolution` from `"node"` to `"bundler"` in `tsconfig.json` - [ ] Clean up any remaining legacy imports - [ ] Update `README.md` with new development instructions --- ## Phase 6: Validation & Rollout ### 6.1 Quality assurance - [ ] Full regression test of all 9 routes/pages - [ ] Verify basket flow: add to cart → update quantity → checkout → payment → order completion - [ ] Verify auth flow: register → login → logout - [ ] Verify search functionality - [ ] Verify Angular Material components render correctly with M3 theme - [ ] Performance audit: compare bundle sizes before/after migration - [ ] Lighthouse audit for performance, accessibility, best practices ### 6.2 CI/CD updates - [ ] Update CI pipeline Node.js version - [ ] Update any Docker images used in CI - [ ] Verify build and deploy pipeline works end-to-end ### 6.3 Documentation - [ ] Update `README.md` with new prerequisites (Node.js, npm versions) - [ ] Document any breaking changes for contributors --- ## Key Risks & Mitigations | Risk | Impact | Mitigation | |---|---|---| | Angular Material breaking changes across 5 versions | High | Migrate Material one version at a time; use `ng update` schematics | | Template syntax migration errors | Medium | Use official `ng generate` schematics for control flow migration | | Zone.js removal breaks change detection | Medium | Test incrementally; keep Zone.js as fallback until fully validated | | Build system migration breaks CI | Medium | Run old and new builders in parallel during transition | | TypeScript version jumps (4.9 → 5.8) | Low | TypeScript upgrades are generally backward-compatible | ## References - [Angular Update Guide](https://angular.dev/update-guide) - [Angular Signals](https://angular.dev/guide/signals) - [Standalone Migration Guide](https://angular.dev/reference/migrations/standalone) - [Control Flow Migration](https://angular.dev/reference/migrations/control-flow) - [Angular Material M3 Theming](https://material.angular.io/guide/theming)
issue