From eba15f1b13980c0086f883a88ac12ab10597d238 Mon Sep 17 00:00:00 2001 From: sumeyye Date: Tue, 16 Jun 2026 16:40:02 +0300 Subject: [PATCH] update: some upgrade enhancements for the dev-app --- npm/ng-packs/apps/dev-app/project.json | 2 +- .../apps/dev-app/src/app/app.component.ts | 7 +-- .../dynamic-form-page.component.html | 4 +- .../dynamic-form-page.component.ts | 52 +++++++++---------- .../dev-app/src/app/home/home.component.html | 2 +- .../dev-app/src/app/home/home.component.ts | 7 +-- .../localization-test.component.ts | 23 ++++---- npm/ng-packs/apps/dev-app/tsconfig.json | 2 +- npm/ng-packs/guides/DEVELOPMENT_GUIDE.md | 52 ++++++++++++++++--- 9 files changed, 94 insertions(+), 57 deletions(-) diff --git a/npm/ng-packs/apps/dev-app/project.json b/npm/ng-packs/apps/dev-app/project.json index d8b7d778c7..991b7111a7 100644 --- a/npm/ng-packs/apps/dev-app/project.json +++ b/npm/ng-packs/apps/dev-app/project.json @@ -166,7 +166,7 @@ "defaultConfiguration": "development" }, "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", + "executor": "@angular/build:extract-i18n", "options": { "buildTarget": "dev-app:build" } diff --git a/npm/ng-packs/apps/dev-app/src/app/app.component.ts b/npm/ng-packs/apps/dev-app/src/app/app.component.ts index 57d3ff6695..595f5caed3 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app.component.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app.component.ts @@ -1,12 +1,13 @@ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { LoaderBarComponent } from '@abp/ng.theme.shared'; import { DynamicLayoutComponent } from '@abp/ng.core'; @Component({ + changeDetection: ChangeDetectionStrategy.OnPush, selector: 'app-root', template: ` - - + + `, imports: [LoaderBarComponent, DynamicLayoutComponent], }) diff --git a/npm/ng-packs/apps/dev-app/src/app/dynamic-form-page/dynamic-form-page.component.html b/npm/ng-packs/apps/dev-app/src/app/dynamic-form-page/dynamic-form-page.component.html index c675dca707..d9f91beae5 100644 --- a/npm/ng-packs/apps/dev-app/src/app/dynamic-form-page/dynamic-form-page.component.html +++ b/npm/ng-packs/apps/dev-app/src/app/dynamic-form-page/dynamic-form-page.component.html @@ -20,9 +20,9 @@ All 16 field types + Nested Forms (Group & Array) with full accessibility support
- @if (formFields.length) { + @if (formFields().length) { { - this.formFields = config; - }); - } + submit(formData: any) { + console.log('✅ Form Submitted Successfully!', formData); + console.table(formData); - submit(formData: any) { - console.log('✅ Form Submitted Successfully!', formData); - console.table(formData); - - // Show success message - alert('✅ Form submitted successfully! Check the console for details.'); - - // Reset form after submission - this.dynamicFormComponent().resetForm(); - } + alert('✅ Form submitted successfully! Check the console for details.'); - cancel() { - console.log('❌ Form Cancelled'); - alert('Form cancelled'); - this.dynamicFormComponent().resetForm(); - } + this.dynamicFormComponent()?.resetForm(); + } + + cancel() { + console.log('❌ Form Cancelled'); + alert('Form cancelled'); + this.dynamicFormComponent()?.resetForm(); + } } diff --git a/npm/ng-packs/apps/dev-app/src/app/home/home.component.html b/npm/ng-packs/apps/dev-app/src/app/home/home.component.html index d8b08c06ae..cea534b296 100644 --- a/npm/ng-packs/apps/dev-app/src/app/home/home.component.html +++ b/npm/ng-packs/apps/dev-app/src/app/home/home.component.html @@ -16,7 +16,7 @@

{{ '::LongWelcomeMessage' | abpLocalization }}

@if (!hasLoggedIn) { - {{ 'AbpAccount::Login' | abpLocalization }} diff --git a/npm/ng-packs/apps/dev-app/src/app/home/home.component.ts b/npm/ng-packs/apps/dev-app/src/app/home/home.component.ts index aa25b046d7..00b79e090d 100644 --- a/npm/ng-packs/apps/dev-app/src/app/home/home.component.ts +++ b/npm/ng-packs/apps/dev-app/src/app/home/home.component.ts @@ -1,10 +1,11 @@ import { AuthService, LocalizationPipe } from '@abp/ng.core'; -import { Component, inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core'; import { NgTemplateOutlet } from '@angular/common'; import { ButtonComponent, CardBodyComponent, CardComponent } from '@abp/ng.theme.shared'; import { RouterLink } from '@angular/router'; @Component({ + changeDetection: ChangeDetectionStrategy.OnPush, selector: 'app-home', templateUrl: './home.component.html', imports: [ @@ -18,14 +19,14 @@ import { RouterLink } from '@angular/router'; }) export class HomeComponent { protected readonly authService = inject(AuthService); - loading = false; + readonly loading = signal(false); get hasLoggedIn(): boolean { return this.authService.isAuthenticated; } login() { - this.loading = true; + this.loading.set(true); this.authService.navigateToLogin(); } } diff --git a/npm/ng-packs/apps/dev-app/src/app/localization-test/localization-test.component.ts b/npm/ng-packs/apps/dev-app/src/app/localization-test/localization-test.component.ts index 187f6d1e37..9ce6a4a80c 100644 --- a/npm/ng-packs/apps/dev-app/src/app/localization-test/localization-test.component.ts +++ b/npm/ng-packs/apps/dev-app/src/app/localization-test/localization-test.component.ts @@ -1,13 +1,13 @@ -import { Component, inject, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; import { LocalizationPipe, UILocalizationService, SessionStateService } from '@abp/ng.core'; import { CommonModule } from '@angular/common'; import { CardComponent, CardBodyComponent } from '@abp/ng.theme.shared'; -import { AsyncPipe } from '@angular/common'; @Component({ + changeDetection: ChangeDetectionStrategy.OnPush, selector: 'app-localization-test', - standalone: true, - imports: [CommonModule, LocalizationPipe, CardComponent, CardBodyComponent, AsyncPipe], + imports: [CommonModule, LocalizationPipe, CardComponent, CardBodyComponent], template: `

Hybrid Localization Test

@@ -22,7 +22,7 @@ import { AsyncPipe } from '@angular/common'; -
UI Localization (from /assets/localization/{{ currentLanguage$ | async }}.json)
+
UI Localization (from /assets/localization/{{ currentLanguage() }}.json)

MyProjectName::CustomKey: {{ 'MyProjectName::CustomKey' | abpLocalization }}

MyProjectName::TestMessage: {{ 'MyProjectName::TestMessage' | abpLocalization }}

@@ -39,20 +39,19 @@ import { AsyncPipe } from '@angular/common';
Loaded UI Localizations
-
{{ loadedLocalizations | json }}
+
{{ loadedLocalizations() | json }}
`, }) -export class LocalizationTestComponent implements OnInit { +export class LocalizationTestComponent { private uiLocalizationService = inject(UILocalizationService); private sessionState = inject(SessionStateService); - loadedLocalizations: any = {}; - currentLanguage$ = this.sessionState.getLanguage$(); + readonly loadedLocalizations = signal(this.uiLocalizationService.getLoadedLocalizations()); - ngOnInit() { - this.loadedLocalizations = this.uiLocalizationService.getLoadedLocalizations(); - } + readonly currentLanguage = toSignal(this.sessionState.getLanguage$(), { + initialValue: this.sessionState.getLanguage(), + }); } diff --git a/npm/ng-packs/apps/dev-app/tsconfig.json b/npm/ng-packs/apps/dev-app/tsconfig.json index f02580de5c..df2232aed6 100644 --- a/npm/ng-packs/apps/dev-app/tsconfig.json +++ b/npm/ng-packs/apps/dev-app/tsconfig.json @@ -28,6 +28,6 @@ "angularCompilerOptions": { "strictInjectionParameters": false, "strictInputAccessModifiers": false, - "strictTemplates": false + "strictTemplates": true } } diff --git a/npm/ng-packs/guides/DEVELOPMENT_GUIDE.md b/npm/ng-packs/guides/DEVELOPMENT_GUIDE.md index a44fd2a49d..9bc8e964fb 100644 --- a/npm/ng-packs/guides/DEVELOPMENT_GUIDE.md +++ b/npm/ng-packs/guides/DEVELOPMENT_GUIDE.md @@ -222,12 +222,52 @@ Example HTML template ### Component Best Practices 1. **Single Responsibility**: Each component should have one clear purpose -2. **Dependency Injection**: Use constructor injection for services -3. **Lifecycle Management**: Implement `OnInit` and `OnDestroy` when needed -4. **State Management**: Use reactive forms and observables -5. **Error Handling**: Implement proper error boundaries -6. **Accessibility**: Follow ARIA guidelines -7. **Performance**: Use `OnPush` change detection when possible +2. **Dependency Injection**: Use `inject()` for services +3. **Change detection**: Set `changeDetection: ChangeDetectionStrategy.OnPush` explicitly on every component +4. **Async state**: Use `signal()` / `computed()` / `toSignal()` — avoid mutating plain properties in `.subscribe()` without a CD trigger +5. **List pages**: Use `toSignal(this.list.hookToQuery(...))` and bind `data().items` in templates +6. **Component IO**: Prefer signal `input()` / `output()` / `model()` over `@Input` / `@Output` +7. **Modal bindings**: `[visible]="isModalVisible()" (visibleChange)="isModalVisible.set($event)"` with `[busy]="modalBusy()"` +8. **`markForCheck()`**: Discouraged — use only in directives, CVAs, or dynamic component hosts where signals cannot apply +9. **Error Handling**: Implement proper error boundaries +10. **Accessibility**: Follow ARIA guidelines + +See [upgrade-notes.md](../upgrade-notes.md) for Angular 22 customer migration guidance. + +### Angular 22 component pattern (recommended) + +```typescript +import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { ListService, PagedResultDto } from '@abp/ng.core'; + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'abp-my-list', + providers: [ListService], +}) +export class MyListComponent { + protected readonly list = inject(ListService); + protected readonly service = inject(MyService); + + readonly data = toSignal(this.list.hookToQuery(q => this.service.getList(q)), { + initialValue: { items: [], totalCount: 0 } as PagedResultDto, + }); + + readonly isModalVisible = signal(false); + readonly modalBusy = signal(false); +} +``` + +```html + + + +``` ## Service Layer