Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
508463d
upgrade: dev-app
sumeyyeKurtulus Jun 15, 2026
7c53945
upgrade: templates
sumeyyeKurtulus Jun 15, 2026
58e43f7
update: account package for the latest upgrade (state management and …
sumeyyeKurtulus Jun 15, 2026
514f950
update: account-core package for the latest upgrade (state management…
sumeyyeKurtulus Jun 15, 2026
da75e4c
update: cms-kit package for the latest upgrade ( html properties)
sumeyyeKurtulus Jun 15, 2026
29d667a
update: components package for the latest upgrade (html properties)
sumeyyeKurtulus Jun 15, 2026
d6acbff
update: core package for the latest upgrade (state management and htm…
sumeyyeKurtulus Jun 15, 2026
2e94964
update: feature-management package for the latest upgrade (state mana…
sumeyyeKurtulus Jun 15, 2026
b94e77e
update: identity package for the latest upgrade (state management and…
sumeyyeKurtulus Jun 15, 2026
d7f7d59
update: oauth package
sumeyyeKurtulus Jun 15, 2026
befbbf8
update: permission-management package for the latest upgrade (state m…
sumeyyeKurtulus Jun 15, 2026
f6c3bda
update: setting-management package for the latest upgrade (state mana…
sumeyyeKurtulus Jun 15, 2026
24b258d
update: tenant-management package for the latest upgrade (state manag…
sumeyyeKurtulus Jun 15, 2026
48d8b99
update: theme-basic package for the latest upgrade (state management …
sumeyyeKurtulus Jun 15, 2026
d8328ef
update: theme-shared package for the latest upgrade (state management…
sumeyyeKurtulus Jun 15, 2026
b9e3668
upgrade: code generation packages to the angular v22 structure
sumeyyeKurtulus Jun 16, 2026
eba15f1
update: some upgrade enhancements for the dev-app
sumeyyeKurtulus Jun 16, 2026
94c9628
update: some upgrade enhancements for the account
sumeyyeKurtulus Jun 16, 2026
2cb93fb
update: some upgrade enhancements for the cms-kit
sumeyyeKurtulus Jun 17, 2026
00e63d8
update: some upgrade enhancements for the components
sumeyyeKurtulus Jun 17, 2026
a055ce5
update: some upgrade enhancements for the core
sumeyyeKurtulus Jun 17, 2026
ecd4b6f
update: some upgrade enhancements for the feature management
sumeyyeKurtulus Jun 17, 2026
0295f5a
update: some upgrade enhancements for the identity
sumeyyeKurtulus Jun 17, 2026
7522836
update: some upgrade enhancements for the permission-management
sumeyyeKurtulus Jun 17, 2026
8a46a5a
update: some upgrade enhancements for the setting-management
sumeyyeKurtulus Jun 17, 2026
ea9ae5f
update: some upgrade enhancements for the tenant-management and theme…
sumeyyeKurtulus Jun 17, 2026
796a83c
update: some upgrade enhancements for the theme-shared
sumeyyeKurtulus Jun 17, 2026
3c06def
update: base tsconfig for `angularCompilerOptions`
sumeyyeKurtulus Jun 17, 2026
e392bdf
Merge remote-tracking branch 'origin/dev' into issue/ng-v22-upgrade
sumeyyeKurtulus Jun 18, 2026
f4d3326
Merge remote-tracking branch 'origin/dev' into issue/ng-v22-upgrade
sumeyyeKurtulus Jun 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion npm/ng-packs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@ Thumbs.db

.nx/

vitest.config.*.timestamp*
vitest.config.*.timestamp*
.claude/worktrees
2 changes: 2 additions & 0 deletions npm/ng-packs/.prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@
/.nx/cache
/.nx/workspace-data
.angular

.nx/self-healing
4 changes: 2 additions & 2 deletions npm/ng-packs/apps/dev-app/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand All @@ -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
}
},
Expand Down
7 changes: 4 additions & 3 deletions npm/ng-packs/apps/dev-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -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: `
<abp-loader-bar></abp-loader-bar>
<abp-dynamic-layout></abp-dynamic-layout>
<abp-loader-bar />
<abp-dynamic-layout />
`,
imports: [LoaderBarComponent, DynamicLayoutComponent],
})
Expand Down
6 changes: 2 additions & 4 deletions npm/ng-packs/apps/dev-app/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
provideClientHydration,
withEventReplay,
withHttpTransferCacheOptions,
withIncrementalHydration,
withNoIncrementalHydration,
} from '@angular/platform-browser';

import { appRoutes } from './app.routes';
Expand All @@ -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: [
Expand All @@ -46,12 +45,11 @@ export const appConfig: ApplicationConfig = {
provideFeatureManagementConfig(),
provideZoneChangeDetection({ eventCoalescing: true }),
provideThemeBasicConfig(),
provideAnimations(),
provideRouter(appRoutes),
provideClientHydration(
withEventReplay(),
withHttpTransferCacheOptions({}),
withIncrementalHydration(),
withNoIncrementalHydration(),
),
],
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ <h4 class="mb-0">
<small>All 16 field types + Nested Forms (Group & Array) with full accessibility support</small>
</div>
<div class="card-body p-4">
@if (formFields.length) {
@if (formFields().length) {
<abp-dynamic-form
[fields]="formFields"
[fields]="formFields()"
[submitButtonText]="'Register'"
[showCancelButton]="true"
(onSubmit)="submit($event)"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,34 @@
import { Component, inject, OnInit, viewChild } from '@angular/core';
import { ChangeDetectionStrategy, Component, inject, viewChild } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { DynamicFormComponent, FormFieldConfig } from '@abp/ng.components/dynamic-form';
import { FormConfigService } from './form-config.service';

@Component({
selector: 'app-dynamic-form-page',
templateUrl: './dynamic-form-page.component.html',
imports: [DynamicFormComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-dynamic-form-page',
templateUrl: './dynamic-form-page.component.html',
imports: [DynamicFormComponent],
})
export class DynamicFormPageComponent implements OnInit {
readonly dynamicFormComponent = viewChild(DynamicFormComponent);
protected readonly formConfigService = inject(FormConfigService);
export class DynamicFormPageComponent {
readonly dynamicFormComponent = viewChild(DynamicFormComponent);
protected readonly formConfigService = inject(FormConfigService);

formFields: FormFieldConfig[] = [];
readonly formFields = toSignal(this.formConfigService.getFormConfig(), {
initialValue: [] as FormFieldConfig[],
});

ngOnInit() {
this.formConfigService.getFormConfig().subscribe(config => {
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();
}
}
2 changes: 1 addition & 1 deletion npm/ng-packs/apps/dev-app/src/app/home/home.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ <h1>{{ '::Welcome' | abpLocalization }}</h1>
<p class="lead px-lg-5 mx-lg-5">{{ '::LongWelcomeMessage' | abpLocalization }}</p>

@if (!hasLoggedIn) {
<abp-button [loading]="loading" (click)="login()" [disabled]="loading" class="px-4 ml-1" role="button"
<abp-button [loading]="loading()" (click)="login()" [disabled]="loading()" class="px-4 ml-1" role="button"
iconClass="fa fa-sign-in">
{{ 'AbpAccount::Login' | abpLocalization }}
</abp-button>
Expand Down
7 changes: 4 additions & 3 deletions npm/ng-packs/apps/dev-app/src/app/home/home.component.ts
Original file line number Diff line number Diff line change
@@ -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: [
Expand All @@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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: `
<div class="container mt-5">
<h2>Hybrid Localization Test</h2>
Expand All @@ -22,7 +22,7 @@ import { AsyncPipe } from '@angular/common';

<abp-card cardClass="mt-4">
<abp-card-body>
<h5>UI Localization (from /assets/localization/{{ currentLanguage$ | async }}.json)</h5>
<h5>UI Localization (from /assets/localization/{{ currentLanguage() }}.json)</h5>
<p><strong>MyProjectName::CustomKey:</strong> {{ 'MyProjectName::CustomKey' | abpLocalization }}</p>
<p><strong>MyProjectName::TestMessage:</strong> {{ 'MyProjectName::TestMessage' | abpLocalization }}</p>
</abp-card-body>
Expand All @@ -39,20 +39,19 @@ import { AsyncPipe } from '@angular/common';
<abp-card cardClass="mt-4">
<abp-card-body>
<h5>Loaded UI Localizations</h5>
<pre>{{ loadedLocalizations | json }}</pre>
<pre>{{ loadedLocalizations() | json }}</pre>
</abp-card-body>
</abp-card>
</div>
`,
})
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(),
});
}
2 changes: 1 addition & 1 deletion npm/ng-packs/apps/dev-app/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@
"angularCompilerOptions": {
"strictInjectionParameters": false,
"strictInputAccessModifiers": false,
"strictTemplates": false
"strictTemplates": true
}
}
52 changes: 46 additions & 6 deletions npm/ng-packs/guides/DEVELOPMENT_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<MyDto>,
});

readonly isModalVisible = signal(false);
readonly modalBusy = signal(false);
}
```

```html
<abp-extensible-table [data]="data().items" [recordsTotal]="data().totalCount" [list]="list" />

<abp-modal
[visible]="isModalVisible()"
(visibleChange)="isModalVisible.set($event)"
[busy]="modalBusy()"
/>
```

## Service Layer

Expand Down
Loading
Loading