diff --git a/npm/ng-packs/.gitignore b/npm/ng-packs/.gitignore index 84b48a798cc..e991e4a0ddb 100644 --- a/npm/ng-packs/.gitignore +++ b/npm/ng-packs/.gitignore @@ -59,4 +59,5 @@ Thumbs.db .nx/ -vitest.config.*.timestamp* \ No newline at end of file +vitest.config.*.timestamp* +.claude/worktrees \ No newline at end of file diff --git a/npm/ng-packs/.prettierignore b/npm/ng-packs/.prettierignore index feffcae59f3..b57d35e3b86 100644 --- a/npm/ng-packs/.prettierignore +++ b/npm/ng-packs/.prettierignore @@ -11,3 +11,5 @@ /.nx/cache /.nx/workspace-data .angular + +.nx/self-healing \ No newline at end of file diff --git a/npm/ng-packs/apps/dev-app/project.json b/npm/ng-packs/apps/dev-app/project.json index c397d8d449d..991b7111a73 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" } @@ -179,7 +179,7 @@ "outputs": ["{workspaceRoot}/coverage/apps/dev-app"], "options": { "passWithNoTests": true, - "reportsDirectory": "../../coverage/apps/dev-app", + "reportsDirectory": "{projectRoot}/../../coverage/apps/dev-app", "silent": false } }, 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 57d3ff66956..595f5caed32 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/app.config.ts b/npm/ng-packs/apps/dev-app/src/app/app.config.ts index 6b8bcf2c054..23a35413394 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app.config.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app.config.ts @@ -4,7 +4,7 @@ import { provideClientHydration, withEventReplay, withHttpTransferCacheOptions, - withIncrementalHydration, + withNoIncrementalHydration, } from '@angular/platform-browser'; import { appRoutes } from './app.routes'; @@ -20,7 +20,6 @@ import { provideIdentityConfig } from '@abp/ng.identity/config'; import { provideTenantManagementConfig } from '@abp/ng.tenant-management/config'; import { provideFeatureManagementConfig } from '@abp/ng.feature-management'; import { provideThemeBasicConfig } from '@abp/ng.theme.basic'; -import { provideAnimations } from '@angular/platform-browser/animations'; export const appConfig: ApplicationConfig = { providers: [ @@ -46,12 +45,11 @@ export const appConfig: ApplicationConfig = { provideFeatureManagementConfig(), provideZoneChangeDetection({ eventCoalescing: true }), provideThemeBasicConfig(), - provideAnimations(), provideRouter(appRoutes), provideClientHydration( withEventReplay(), withHttpTransferCacheOptions({}), - withIncrementalHydration(), + withNoIncrementalHydration(), ), ], }; 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 c675dca707f..d9f91beae5d 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 d8b08c06aec..cea534b296f 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 @@

{{ '::Welcome' | abpLocalization }}

{{ '::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 aa25b046d7c..00b79e090de 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 187f6d1e37c..9ce6a4a80cc 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 f02580de5c5..df2232aed67 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 a44fd2a49d3..9bc8e964fb2 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 diff --git a/npm/ng-packs/migrations.json b/npm/ng-packs/migrations.json index 3615f35e50f..d4d67927bf1 100644 --- a/npm/ng-packs/migrations.json +++ b/npm/ng-packs/migrations.json @@ -1,118 +1,130 @@ { "migrations": [ { - "version": "22.0.0-beta.1", - "description": "Updates release version config based on the breaking changes in Nx v22", - "implementation": "./src/migrations/update-22-0-0/release-version-config-changes", + "cli": "nx", + "version": "22.6.0-beta.10", + "description": "Adds .claude/worktrees to .gitignore", + "implementation": "./dist/src/migrations/update-22-6-0/add-claude-worktrees-to-git-ignore", "package": "nx", - "name": "22-0-0-release-version-config-changes" + "name": "22-6-1-add-claude-worktrees-to-git-ignore" }, { - "version": "22.0.0-beta.2", - "description": "Consolidates releaseTag* options into nested releaseTag object structure", - "implementation": "./src/migrations/update-22-0-0/consolidate-release-tag-config", + "cli": "nx", + "version": "22.7.0-beta.0", + "description": "Adds .nx/polygraph to .gitignore", + "implementation": "./dist/src/migrations/update-22-7-0/add-polygraph-to-git-ignore", "package": "nx", - "name": "22-0-0-consolidate-release-tag-config" + "name": "22-7-0-add-polygraph-to-git-ignore" }, { "cli": "nx", - "version": "22.1.0-beta.5", - "description": "Updates the nx wrapper.", - "implementation": "./src/migrations/update-22-1-0/update-nx-wrapper", + "version": "22.6.0-rc.0", + "description": "Adds .claude/settings.local.json to .gitignore", + "implementation": "./dist/src/migrations/update-17-3-0/update-nxw", "package": "nx", - "name": "22-1-0-update-nx-wrapper" + "name": "22-6-0-add-claude-settings-local-to-git-ignore" }, { - "version": "21.5.0-beta.2", - "description": "Migrate the legacy 'development' custom condition to a workspace-unique custom condition name.", - "factory": "./src/migrations/update-21-5-0/migrate-development-custom-condition", - "package": "@nx/js", - "name": "migrate-development-custom-condition" + "cli": "nx", + "version": "22.7.0-beta.0", + "description": "Adds .nx/self-healing to .gitignore", + "implementation": "./dist/src/migrations/update-22-2-0/add-self-healing-to-gitignore", + "package": "nx", + "name": "22-7-0-add-self-healing-to-gitignore" }, { - "version": "22.0.0-beta.0", - "description": "Remove the deprecated `external` and `externalBuildTargets` options from the `@nx/js:swc` and `@nx/js:tsc` executors.", - "factory": "./src/migrations/update-22-0-0/remove-external-options-from-js-executors", - "package": "@nx/js", - "name": "remove-external-options-from-js-executors" + "version": "22.3.2-beta.0", + "requires": { + "@angular/build": ">=21.0.0" + }, + "description": "Create AI Instructions to help migrate users workspaces past breaking changes for Vitest 4.", + "implementation": "./src/migrations/update-22-1-0/create-ai-instructions-for-vitest-4", + "package": "@nx/vitest", + "name": "update-22-3-2" }, { - "version": "22.1.0-rc.1", - "description": "Removes redundant TypeScript project references from project's tsconfig.json files when runtime tsconfig files (e.g., tsconfig.lib.json, tsconfig.app.json) exist.", - "factory": "./src/migrations/update-22-1-0/remove-redundant-ts-project-references", - "package": "@nx/js", - "name": "remove-redundant-ts-project-references" + "version": "22.6.0-beta.11", + "description": "Prefix reportsDirectory with {projectRoot} to maintain correct resolution after workspace-root-relative behavior change.", + "implementation": "./src/migrations/update-22-6-0/prefix-reports-directory-with-project-root", + "package": "@nx/vitest", + "name": "update-22-6-0-prefix-reports-directory" }, { - "version": "21.3.0-beta.3", - "description": "Rename the CLI option `testPathPattern` to `testPathPatterns`.", - "implementation": "./src/migrations/update-21-3-0/rename-test-path-pattern", - "package": "@nx/jest", - "name": "rename-test-path-pattern" + "version": "22.7.0-beta.12", + "description": "Add missing inputs to @nx/eslint:lint executor target defaults", + "implementation": "./src/migrations/update-21-6-0/update-executor-lint-inputs", + "package": "@nx/eslint", + "name": "update-executor-lint-inputs" }, { - "version": "22.2.0-beta.2", - "description": "Convert jest.config.ts files from ESM to CJS syntax (export default -> module.exports, import -> require) for projects using CommonJS resolution to ensure correct loading under Node.js type-stripping.", - "implementation": "./src/migrations/update-22-2-0/convert-jest-config-to-cjs", + "version": "22.3.2-beta.0", + "requires": { + "jest": ">=30.0.0" + }, + "description": "Replace removed matcher aliases in Jest v30 with their corresponding matcher", + "implementation": "./src/migrations/update-21-3-0/replace-removed-matcher-aliases", "package": "@nx/jest", - "name": "convert-jest-config-to-cjs" + "name": "replace-removed-matcher-aliases-v22-3" }, { - "cli": "nx", - "version": "21.3.0-beta.4", - "requires": { "@angular/core": ">=20.1.0" }, - "description": "Update the @angular/cli package version to ~20.1.0.", - "factory": "./src/migrations/update-21-3-0/update-angular-cli", + "version": "22.3.0-beta.0", + "requires": { + "@angular/core": ">=21.0.0" + }, + "description": "Updates webpack-based SSR configuration to use preserve module format and bundler module resolution.", + "factory": "./src/migrations/update-22-3-0/update-ssr-webpack-config", "package": "@nx/angular", - "name": "update-angular-cli-version-20-1-0" + "name": "update-ssr-webpack-config-22-2-0" }, { - "version": "21.5.0-beta.0", - "description": "Set the 'tsConfig' option to build and test targets to help with Angular migration issues.", - "factory": "./src/migrations/update-21-5-0/set-tsconfig-option", + "version": "22.3.0-beta.0", + "requires": { + "@angular/core": ">=21.0.0-rc.3" + }, + "description": "Update 'module' to 'preserve' and 'moduleResolution' to 'bundler' in TypeScript configurations for Angular projects.", + "factory": "./src/migrations/update-22-3-0/update-module-resolution", "package": "@nx/angular", - "name": "set-tsconfig-option" + "name": "update-module-resolution-22-2-0" }, { - "cli": "nx", - "version": "21.5.0-beta.2", - "requires": { "@angular/core": ">=20.2.0" }, - "description": "Update the @angular/cli package version to ~20.2.0.", - "factory": "./src/migrations/update-21-5-0/update-angular-cli", + "version": "22.3.0-beta.0", + "requires": { + "@angular/core": ">=21.0.0" + }, + "description": "Updates the 'lib' property in tsconfig files to use 'es2022' or a more modern version.", + "factory": "./src/migrations/update-22-3-0/update-typescript-lib", "package": "@nx/angular", - "name": "update-angular-cli-version-20-2-0" + "name": "update-typescript-lib-22-2-0" }, { - "version": "21.5.0-beta.2", - "requires": { "@angular/core": ">=20.2.0" }, - "description": "Remove any Karma configuration files that only contain the default content. The default configuration is automatically available without a specific project configurationfile.", - "factory": "./src/migrations/update-21-5-0/remove-default-karma-configuration-files", + "version": "22.3.0-beta.0", + "requires": { + "@angular/core": ">=21.0.0" + }, + "description": "Update 'vitest' unit test runner option to 'vitest-analog' in generator defaults.", + "factory": "./src/migrations/update-22-3-0/update-unit-test-runner-option", "package": "@nx/angular", - "name": "remove-default-karma-configuration-files" + "name": "update-unit-test-runner-option" }, { - "cli": "nx", - "version": "21.6.1-beta.2", - "requires": { "@angular/core": ">=20.3.0" }, - "description": "Update the @angular/cli package version to ~20.3.0.", - "factory": "./src/migrations/update-21-6-1/update-angular-cli", + "version": "22.3.0-beta.3", + "requires": { + "@angular/core": ">=21.0.0" + }, + "description": "Set 'isolatedModules' to 'true' in TypeScript test configurations for Angular projects.", + "factory": "./src/migrations/update-22-3-0/set-isolated-modules", "package": "@nx/angular", - "name": "update-angular-cli-version-20-3-0" + "name": "set-isolated-modules-22-3-0" }, { - "version": "20.2.0", - "description": "Replaces usages of the deprecated Router.getCurrentNavigation method with the Router.currentNavigation signal", - "factory": "./bundles/router-current-navigation.cjs#migrate", - "optional": true, - "package": "@angular/core", - "name": "router-current-navigation" - }, - { - "version": "20.3.0", - "description": "Adds `BootstrapContext` to `bootstrapApplication` calls in `main.server.ts` to support server rendering.", - "factory": "./bundles/add-bootstrap-context-to-server-main.cjs#migrate", - "package": "@angular/core", - "name": "add-bootstrap-context-to-server-main" + "version": "22.3.0-beta.3", + "requires": { + "@angular/core": ">=21.0.0" + }, + "description": "Replace 'jest-preset-angular/setup-jest' imports with the new 'setupZoneTestEnv' function.", + "factory": "./src/migrations/update-22-3-0/update-jest-preset-angular-setup", + "package": "@nx/angular", + "name": "update-jest-preset-angular-setup" } ] } diff --git a/npm/ng-packs/nx.json b/npm/ng-packs/nx.json index a573c5c59a7..f87bf30ba80 100644 --- a/npm/ng-packs/nx.json +++ b/npm/ng-packs/nx.json @@ -111,7 +111,12 @@ } }, "@nx/eslint:lint": { - "inputs": ["default", "{workspaceRoot}/.eslintrc.json"], + "inputs": [ + "default", + "^default", + "{workspaceRoot}/.eslintrc.json", + "{workspaceRoot}/tools/eslint-rules/**/*" + ], "cache": true }, "@angular/build:application": { @@ -135,5 +140,6 @@ "!{projectRoot}/.eslintrc.json" ] }, - "parallel": 3 + "parallel": 3, + "analytics": false } diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index 85a9e981899..f27e905f843 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -32,7 +32,7 @@ "dep-graph": "nx dep-graph", "help": "nx help", "build:schematics": "cd scripts && yarn && yarn build:schematics && cd ..", - "dev:schematics": "tsc -p packages/schematics/tsconfig.json -w", + "dev:schematics": "tsc -p packages/schematics/tsconfig.lib.json -w", "mock:schematics": "cd scripts/mock-schematic && yarn && yarn start", "debug:schematics": "./node_modules/.bin/nx g ./packages/schematics/src/collection.json:proxy-add --module identity --apiName __default --source __default --target __default --url https://localhost:44305 --serviceType application --entryPoint __default ", "debug:schematics-dist": "./node_modules/.bin/ng g ./dist/packages/schematics/collection.json:proxy-add --module __default --apiName __default --source __default --target __default --url http://localhost:4300 --service-type application --entryPoint __default", @@ -77,27 +77,27 @@ "@ng-bootstrap/ng-bootstrap": "~20.0.0", "@ngneat/spectator": "~19.6.2", "@ngx-validate/core": "^0.2.0", - "@nx/angular": "~22.2.0", - "@nx/cypress": "~22.2.0", - "@nx/devkit": "~22.2.0", - "@nx/eslint": "~22.2.0", - "@nx/eslint-plugin": "~22.2.0", - "@nx/jest": "~22.2.0", - "@nx/js": "22.2.7", - "@nx/plugin": "~22.2.0", - "@nx/vite": "22.2.7", - "@nx/vitest": "22.2.7", - "@nx/web": "~22.2.0", - "@nx/workspace": "~22.2.0", + "@nx/angular": "~22.7.5", + "@nx/cypress": "~22.7.5", + "@nx/devkit": "~22.7.5", + "@nx/eslint": "~22.7.5", + "@nx/eslint-plugin": "~22.7.5", + "@nx/jest": "~22.7.5", + "@nx/js": "~22.7.5", + "@nx/plugin": "~22.7.5", + "@nx/vite": "~22.7.5", + "@nx/vitest": "~22.7.5", + "@nx/web": "~22.7.5", + "@nx/workspace": "~22.7.5", "@popperjs/core": "~2.11.0", - "@schematics/angular": "~21.2.0", - "@swc-node/register": "1.9.2", - "@swc/cli": "0.6.0", - "@swc/core": "~1.5.7", - "@swc/helpers": "~0.5.11", + "@schematics/angular": "~22.0.0", + "@swc-node/register": "~1.11.1", + "@swc/cli": "~0.7.10", + "@swc/core": "~1.15.8", + "@swc/helpers": "~0.5.23", "@swimlane/ngx-datatable": "~22.0.0", "@types/express": "~5.0.0", - "@types/jest": "29.5.14", + "@types/jest": "~30.0.0", "@types/node": "20.19.9", "@typescript-eslint/eslint-plugin": "7.16.0", "@typescript-eslint/parser": "7.16.0", @@ -117,10 +117,10 @@ "eslint-plugin-cypress": "^3.5.0", "express": "~5.1.0", "got": "^11.0.0", - "jest": "^29.0.0", + "jest": "~30.4.2", "jest-canvas-mock": "^2.0.0", - "jest-environment-jsdom": "^29.0.0", - "jest-preset-angular": "14.6.0", + "jest-environment-jsdom": "~30.4.1", + "jest-preset-angular": "~16.0.0", "jsdom": "~22.1.0", "jsonc-eslint-parser": "^2.0.0", "jsonc-parser": "^2.0.0", @@ -128,9 +128,9 @@ "just-compare": "^2.0.0", "lerna": "^4.0.0", "lint-staged": "^13.0.0", - "ng-packagr": "~21.2.0", + "ng-packagr": "~22.0.0", "ng-zorro-antd": "~21.1.0", - "nx": "~22.2.0", + "nx": "~22.7.5", "postcss": "^8.0.0", "postcss-import": "14.1.0", "postcss-preset-env": "7.5.0", @@ -140,15 +140,16 @@ "@toast-ui/editor": "~3.0.0", "rxjs": "~7.8.0", "should-quote": "^1.0.0", - "ts-jest": "29.4.6", + "ts-jest": "~29.4.6", "ts-node": "10.9.1", "ts-toolbelt": "^9.0.0", "tslib": "^2.3.0", "tslint": "~6.1.0", - "typescript": "~5.9.0", + "typescript": "~6.0.0", "vite": "^8.0.0", "vitest": "^4.0.0", - "zone.js": "~0.15.0" + "zone.js": "~0.15.0", + "jest-util": "~30.4.1" }, "lint-staged": { "**/*.{js,jsx,ts,tsx,html,css,scss}": [ diff --git a/npm/ng-packs/packages/account-core/project.json b/npm/ng-packs/packages/account-core/project.json index 8a201b80068..0825ff6e6f5 100644 --- a/npm/ng-packs/packages/account-core/project.json +++ b/npm/ng-packs/packages/account-core/project.json @@ -31,7 +31,7 @@ "executor": "@nx/vitest:test", "outputs": ["{options.reportsDirectory}"], "options": { - "reportsDirectory": "../../coverage/packages/account-core" + "reportsDirectory": "{projectRoot}/../../coverage/packages/account-core" } } } diff --git a/npm/ng-packs/packages/account/project.json b/npm/ng-packs/packages/account/project.json index 5d02bebd76d..74231300a2a 100644 --- a/npm/ng-packs/packages/account/project.json +++ b/npm/ng-packs/packages/account/project.json @@ -31,7 +31,7 @@ "executor": "@nx/vitest:test", "outputs": ["{options.reportsDirectory}"], "options": { - "reportsDirectory": "../../coverage/packages/account" + "reportsDirectory": "{projectRoot}/../../coverage/packages/account" } } } diff --git a/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.html b/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.html index a10e1906645..69608d6bad8 100644 --- a/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.html +++ b/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.html @@ -1,11 +1,17 @@ -
- @if (!hideCurrentPassword) { + + @if (!hideCurrentPassword()) {
* + * + * + * + * + * + {{ 'AbpIdentity::Save' | abpLocalization }} + {{ 'AbpIdentity::Save' | abpLocalization }} + diff --git a/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.ts b/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.ts index ad5db820ac3..16736017296 100644 --- a/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.ts +++ b/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.ts @@ -1,6 +1,13 @@ import { ProfileService } from '@abp/ng.account.core/proxy'; import { ButtonComponent, getPasswordValidators, ToasterService } from '@abp/ng.theme.shared'; -import { Component, Injector, OnInit, inject } from '@angular/core'; +import { + Component, + Injector, + OnInit, + inject, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { ReactiveFormsModule, UntypedFormBuilder, @@ -18,6 +25,7 @@ const { required } = Validators; const PASSWORD_FIELDS = ['newPassword', 'repeatNewPassword']; @Component({ + changeDetection: ChangeDetectionStrategy.OnPush, selector: 'abp-change-password-form', templateUrl: './change-password.component.html', exportAs: 'abpChangePasswordForm', @@ -40,9 +48,8 @@ export class ChangePasswordComponent form!: UntypedFormGroup; - inProgress?: boolean; - - hideCurrentPassword?: boolean; + readonly inProgress = signal(false); + readonly hideCurrentPassword = signal(false); mapErrorsFn: Validation.MapErrorsFn = (errors, groupErrors, control) => { if (PASSWORD_FIELDS.indexOf(String(control?.name)) < 0) return errors; @@ -51,7 +58,7 @@ export class ChangePasswordComponent }; ngOnInit(): void { - this.hideCurrentPassword = !this.manageProfileState.getProfile()?.hasPassword; + this.hideCurrentPassword.set(!this.manageProfileState.getProfile()?.hasPassword); const passwordValidations = getPasswordValidators(this.injector); @@ -76,18 +83,18 @@ export class ChangePasswordComponent }, ); - if (this.hideCurrentPassword) this.form.removeControl('password'); + if (this.hideCurrentPassword()) this.form.removeControl('password'); } onSubmit() { if (this.form.invalid) return; - this.inProgress = true; + this.inProgress.set(true); this.profileService .changePassword({ - ...(!this.hideCurrentPassword && { currentPassword: this.form.get('password')?.value }), + ...(!this.hideCurrentPassword() && { currentPassword: this.form.get('password')?.value }), newPassword: this.form.get('newPassword')?.value, }) - .pipe(finalize(() => (this.inProgress = false))) + .pipe(finalize(() => this.inProgress.set(false))) .subscribe({ next: () => { this.form.reset(); @@ -95,8 +102,8 @@ export class ChangePasswordComponent life: 5000, }); - if (this.hideCurrentPassword) { - this.hideCurrentPassword = false; + if (this.hideCurrentPassword()) { + this.hideCurrentPassword.set(false); this.form.addControl('password', new UntypedFormControl('', [required])); } }, diff --git a/npm/ng-packs/packages/account/src/lib/components/forgot-password/forgot-password.component.html b/npm/ng-packs/packages/account/src/lib/components/forgot-password/forgot-password.component.html index 90ad0254af6..ba415340b22 100644 --- a/npm/ng-packs/packages/account/src/lib/components/forgot-password/forgot-password.component.html +++ b/npm/ng-packs/packages/account/src/lib/components/forgot-password/forgot-password.component.html @@ -1,28 +1,28 @@

{{ 'AbpAccount::ForgotPassword' | abpLocalization }}

-@if (!isEmailSent) { +@if (!isEmailSent()) {

{{ 'AbpAccount::SendPasswordResetLink_Information' | abpLocalization }}

* + }} + *
{{ 'AbpAccount::Submit' | abpLocalization }} - {{ 'AbpAccount::Login' | abpLocalization }} + + + {{ 'AbpAccount::Login' | abpLocalization }} +
} @else {

diff --git a/npm/ng-packs/packages/account/src/lib/components/forgot-password/forgot-password.component.ts b/npm/ng-packs/packages/account/src/lib/components/forgot-password/forgot-password.component.ts index 5b00896acbf..d77ecc3b2b8 100644 --- a/npm/ng-packs/packages/account/src/lib/components/forgot-password/forgot-password.component.ts +++ b/npm/ng-packs/packages/account/src/lib/components/forgot-password/forgot-password.component.ts @@ -1,5 +1,5 @@ import { AccountService } from '@abp/ng.account.core/proxy'; -import { Component, inject } from '@angular/core'; +import { Component, inject, signal, ChangeDetectionStrategy } from '@angular/core'; import { ReactiveFormsModule, UntypedFormBuilder, @@ -13,6 +13,7 @@ import { RouterLink } from '@angular/router'; import { NgxValidateCoreModule } from '@ngx-validate/core'; @Component({ + changeDetection: ChangeDetectionStrategy.OnPush, selector: 'abp-forgot-password', templateUrl: 'forgot-password.component.html', imports: [ @@ -29,9 +30,8 @@ export class ForgotPasswordComponent { form: UntypedFormGroup; - inProgress?: boolean; - - isEmailSent = false; + readonly inProgress = signal(false); + readonly isEmailSent = signal(false); constructor() { this.form = this.fb.group({ @@ -42,16 +42,16 @@ export class ForgotPasswordComponent { onSubmit() { if (this.form.invalid) return; - this.inProgress = true; + this.inProgress.set(true); this.accountService .sendPasswordResetCode({ email: this.form.get('email')?.value, appName: 'Angular', }) - .pipe(finalize(() => (this.inProgress = false))) + .pipe(finalize(() => this.inProgress.set(false))) .subscribe(() => { - this.isEmailSent = true; + this.isEmailSent.set(true); }); } } diff --git a/npm/ng-packs/packages/account/src/lib/components/login/login.component.html b/npm/ng-packs/packages/account/src/lib/components/login/login.component.html index 49192534564..d57c78b3db7 100644 --- a/npm/ng-packs/packages/account/src/lib/components/login/login.component.html +++ b/npm/ng-packs/packages/account/src/lib/components/login/login.component.html @@ -7,7 +7,7 @@

{{ 'AbpAccount::Login' | abpLocalization }}

}} } -
+
err); }), - finalize(() => (this.inProgress = false)), + finalize(() => this.inProgress.set(false)), ) .subscribe(); } diff --git a/npm/ng-packs/packages/account/src/lib/components/manage-profile/manage-profile.component.html b/npm/ng-packs/packages/account/src/lib/components/manage-profile/manage-profile.component.html index 410a1f7ccf1..1ca3e1e457f 100644 --- a/npm/ng-packs/packages/account/src/lib/components/manage-profile/manage-profile.component.html +++ b/npm/ng-packs/packages/account/src/lib/components/manage-profile/manage-profile.component.html @@ -1,24 +1,24 @@
-
+