Browse Source

update: some upgrade enhancements for the dev-app

pull/25690/head
sumeyye 4 months ago
parent
commit
eba15f1b13
  1. 2
      npm/ng-packs/apps/dev-app/project.json
  2. 7
      npm/ng-packs/apps/dev-app/src/app/app.component.ts
  3. 4
      npm/ng-packs/apps/dev-app/src/app/dynamic-form-page/dynamic-form-page.component.html
  4. 52
      npm/ng-packs/apps/dev-app/src/app/dynamic-form-page/dynamic-form-page.component.ts
  5. 2
      npm/ng-packs/apps/dev-app/src/app/home/home.component.html
  6. 7
      npm/ng-packs/apps/dev-app/src/app/home/home.component.ts
  7. 23
      npm/ng-packs/apps/dev-app/src/app/localization-test/localization-test.component.ts
  8. 2
      npm/ng-packs/apps/dev-app/tsconfig.json
  9. 52
      npm/ng-packs/guides/DEVELOPMENT_GUIDE.md

2
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"
}

7
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: `
<abp-loader-bar></abp-loader-bar>
<abp-dynamic-layout></abp-dynamic-layout>
<abp-loader-bar />
<abp-dynamic-layout />
`,
imports: [LoaderBarComponent, DynamicLayoutComponent],
})

4
npm/ng-packs/apps/dev-app/src/app/dynamic-form-page/dynamic-form-page.component.html

@ -20,9 +20,9 @@
<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)"

52
npm/ng-packs/apps/dev-app/src/app/dynamic-form-page/dynamic-form-page.component.ts

@ -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
npm/ng-packs/apps/dev-app/src/app/home/home.component.html

@ -16,7 +16,7 @@
<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>

7
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();
}
}

23
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: `
<div class="container mt-5">
<h2>Hybrid Localization Test</h2>
@ -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>
@ -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
npm/ng-packs/apps/dev-app/tsconfig.json

@ -28,6 +28,6 @@
"angularCompilerOptions": {
"strictInjectionParameters": false,
"strictInputAccessModifiers": false,
"strictTemplates": false
"strictTemplates": true
}
}

52
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<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

Loading…
Cancel
Save