From 0e238e7360e360e89b83bd2328fd5fe9a3d7b777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=A1=E8=89=B2?= Date: Sat, 20 Jan 2024 22:47:37 +0800 Subject: [PATCH] refactor(*): use `inject` instead `constructor` (#1755) --- docs/i18n.en-US.md | 3 +- docs/i18n.zh-CN.md | 3 +- .../abc/auto-focus/auto-focus.directive.ts | 6 +- .../abc/avatar-list/avatar-list.component.ts | 27 +++-- packages/abc/cell/cell-host.directive.ts | 10 +- packages/abc/cell/cell.component.ts | 36 +++---- packages/abc/cell/cell.service.ts | 12 +-- packages/abc/date-picker/range.directive.ts | 22 ++-- packages/abc/down-file/down-file.directive.ts | 21 ++-- packages/abc/ellipsis/ellipsis.component.ts | 40 +++---- .../error-collect/error-collect.component.ts | 36 +++---- packages/abc/exception/exception.component.ts | 22 ++-- .../footer-toolbar.component.ts | 29 ++--- .../full-content-toggle.directive.ts | 4 +- .../full-content/full-content.component.ts | 42 +++----- .../global-footer-item.component.ts | 16 +-- .../global-footer/global-footer.component.ts | 29 +++-- packages/abc/hotkey/hotkey.directive.ts | 16 ++- packages/abc/loading/loading.component.ts | 2 +- packages/abc/loading/loading.service.ts | 16 +-- packages/abc/lodop/lodop.service.ts | 9 +- packages/abc/media/media.component.ts | 27 +++-- packages/abc/media/media.service.ts | 10 +- .../abc/notice-icon/notice-icon.component.ts | 26 ++--- packages/abc/observers/observer-size.ts | 15 ++- .../abc/onboarding/onboarding.component.ts | 19 ++-- packages/abc/onboarding/onboarding.service.ts | 27 +++-- .../abc/page-header/page-header.component.ts | 74 ++++++------- packages/abc/pdf/pdf.component.ts | 63 +++++------ packages/abc/qr/qr.component.ts | 14 +-- .../abc/quick-menu/quick-menu.component.ts | 25 ++--- packages/abc/result/result.component.ts | 21 ++-- packages/abc/reuse-tab/provide.ts | 1 + .../reuse-tab-context-menu.component.ts | 7 +- .../reuse-tab/reuse-tab-context.component.ts | 18 ++-- .../reuse-tab/reuse-tab-context.directive.ts | 6 +- .../reuse-tab/reuse-tab-context.service.ts | 6 +- packages/abc/reuse-tab/reuse-tab.component.ts | 72 ++++++------- packages/abc/reuse-tab/reuse-tab.service.ts | 24 ++--- packages/abc/reuse-tab/reuse-tab.strategy.ts | 3 +- packages/abc/se/se-container.component.ts | 53 ++++------ packages/abc/se/se.component.ts | 63 +++++------ packages/abc/sg/sg-container.component.ts | 14 +-- packages/abc/sg/sg.component.ts | 30 +++--- packages/abc/st/st-export.ts | 15 ++- packages/abc/st/st-filter.component.ts | 7 +- packages/abc/st/st-row.directive.ts | 9 +- packages/abc/st/st-widget-host.directive.ts | 10 +- packages/abc/st/st.component.ts | 100 +++++++----------- packages/abc/st/test/st.spec.ts | 2 +- packages/abc/sv/sv-container.component.ts | 47 ++++---- packages/abc/sv/sv.component.ts | 50 ++++----- .../abc/tag-select/tag-select.component.ts | 25 ++--- packages/abc/xlsx/xlsx.directive.ts | 6 +- packages/abc/xlsx/xlsx.service.ts | 16 +-- packages/abc/zip/zip.service.ts | 13 ++- packages/acl/src/acl-guard.ts | 8 +- packages/acl/src/acl-if.directive.ts | 21 ++-- packages/acl/src/acl.directive.ts | 22 ++-- packages/auth/src/social/social.service.ts | 14 ++- .../src/store/cookie-storage.service.spec.ts | 25 +++-- .../auth/src/store/cookie-storage.service.ts | 4 +- packages/auth/src/token/jwt/jwt.guard.ts | 6 +- .../auth/src/token/simple/simple.guard.ts | 6 +- packages/auth/src/token/token.service.ts | 12 +-- packages/cache/src/cache.service.ts | 17 ++- .../cache/src/local-storage-cache.service.ts | 4 +- packages/chart/bar/bar.component.ts | 12 +-- packages/chart/card/card.component.ts | 15 ++- .../chart/chart-echarts/echarts.component.ts | 14 +-- .../chart/chart-echarts/echarts.service.ts | 9 +- packages/chart/chart-echarts/echarts.spec.ts | 5 +- packages/chart/core/g2.base.component.ts | 29 ++--- packages/chart/core/g2.servicce.ts | 10 +- packages/chart/custom/custom.component.ts | 11 +- packages/chart/gauge/gauge.component.ts | 10 +- .../chart/mini-area/mini-area.component.ts | 28 ++--- packages/chart/mini-bar/mini-bar.component.ts | 18 ++-- .../mini-progress/mini-progress.component.ts | 23 ++-- .../number-info/number-info.component.ts | 14 ++- packages/chart/pie/pie.component.ts | 30 ++---- packages/chart/radar/radar.component.ts | 15 ++- .../chart/single-bar/single-bar.component.ts | 30 +++--- .../chart/tag-cloud/tag-cloud.component.ts | 18 ++-- packages/chart/timeline/timeline.component.ts | 18 ++-- packages/chart/trend/trend.component.ts | 10 +- .../chart/water-wave/water-wave.component.ts | 35 +++--- packages/chart/water-wave/water-wave.spec.ts | 2 +- packages/form/src/sf-fixed.directive.ts | 25 +++-- packages/form/src/sf-item-wrap.component.ts | 6 +- packages/form/src/sf-item.component.ts | 11 +- packages/form/src/sf.component.ts | 56 ++++------ packages/form/src/utils.ts | 3 +- packages/form/src/validator.factory.ts | 12 +-- packages/form/src/widget.factory.ts | 4 +- packages/form/src/widget.ts | 14 ++- .../widgets/custom/sf-template.directive.ts | 10 +- .../layout-default/layout-header.component.ts | 11 +- .../layout-default/layout-nav.component.ts | 59 +++++------ .../layout-default/layout-top-menu-item.ts | 11 +- .../theme/layout-default/layout.component.ts | 11 +- .../theme/layout-default/layout.service.ts | 5 +- .../setting-drawer-item.component.html | 10 +- .../setting-drawer.component.ts | 37 +++---- packages/theme/src/locale/locale.tokens.ts | 4 +- packages/theme/src/pipes/safe/html.pipe.ts | 4 +- packages/theme/src/pipes/safe/url.pipe.ts | 4 +- packages/theme/src/pipes/yn/yn.pipe.ts | 4 +- .../src/services/drawer/drawer.helper.ts | 9 +- .../theme/src/services/http/http.client.ts | 8 +- .../src/services/http/http.decorator.spec.ts | 15 ++- .../theme/src/services/http/http.decorator.ts | 4 +- .../theme/src/services/i18n/i18n-url.guard.ts | 14 +-- .../theme/src/services/menu/menu.service.ts | 19 ++-- .../theme/src/services/modal/modal.helper.ts | 16 +-- .../theme/src/services/rtl/rtl.service.ts | 20 ++-- .../src/services/settings/settings.service.ts | 10 +- .../theme/src/services/title/title.service.ts | 26 ++--- .../theme/theme-btn/theme-btn.component.ts | 34 +++--- packages/util/browser/cookie.service.ts | 10 +- packages/util/browser/scroll.service.ts | 13 +-- packages/util/decorator/convert.ts | 8 ++ packages/util/decorator/index.en-US.md | 6 +- packages/util/decorator/index.zh-CN.md | 6 +- packages/util/decorator/zone-outside.ts | 4 +- packages/util/format/currency.service.ts | 13 ++- packages/util/other/lazy.service.ts | 6 +- packages/util/pipes/currency/cny.pipe.ts | 14 +-- packages/util/pipes/currency/mega.pipe.ts | 21 ++-- packages/util/pipes/currency/price.pipe.ts | 23 ++-- packages/util/pipes/filter/filter.pipe.ts | 10 +- packages/util/pipes/format/mask.pipe.ts | 38 +++---- .../__name@dasherize__.component.ts.template | 16 ++- .../__name@dasherize__.component.ts.template | 7 +- .../__name@dasherize__.service.ts.template | 6 +- .../__name@dasherize__.component.ts.template | 16 ++- .../__name@dasherize__.component.ts.template | 14 +-- .../__name@dasherize__.component.ts.template | 14 ++- src/app/core/error-handler.ts | 6 +- src/app/core/i18n/service.ts | 13 ++- src/app/core/meta.service.ts | 8 +- src/app/core/startup.service.ts | 14 ++- src/app/layout/header/search-box.component.ts | 16 ++- src/app/routes/404/404.component.ts | 6 +- src/app/routes/home/home.component.ts | 15 ++- .../components/content/content.component.ts | 10 +- .../components/dialog/drawer.component.ts | 6 +- .../components/dialog/modal.component.ts | 6 +- .../shared/components/dialog/sf.component.ts | 6 +- .../shared/components/docs/docs.component.ts | 36 ++++--- .../edit-button/edit-button.component.ts | 12 +-- .../components/footer/footer.component.html | 2 +- .../components/footer/footer.component.ts | 36 ++++--- .../shared/components/icon/icon.component.ts | 6 +- .../main-menu/main-menu.component.ts | 14 +-- .../match-router/match-router.directive.ts | 12 +-- .../route-transfer.directive.ts | 12 +-- src/app/shared/st-widget/img.ts | 5 +- src/dev/demo.component.ts | 10 +- src/dev/lazy/test.component.ts | 6 +- src/dev/list/layout.component.ts | 6 +- src/dev/list/list-item.component.ts | 6 +- src/dev/list/list.component.ts | 6 +- 163 files changed, 1267 insertions(+), 1543 deletions(-) diff --git a/docs/i18n.en-US.md b/docs/i18n.en-US.md index 649fc86e4..a93933000 100644 --- a/docs/i18n.en-US.md +++ b/docs/i18n.en-US.md @@ -64,8 +64,7 @@ Of course, you can also use runtime changes: ```ts import { en_US, DelonLocaleService } from '@delon/theme'; ... -constructor(private delonLocaleService: DelonLocaleService) { -} +private readonly i18n = inject(DelonLocaleService); switchLanguage() { this.delonLocaleService.setLocale(en_US); diff --git a/docs/i18n.zh-CN.md b/docs/i18n.zh-CN.md index d5abe45a4..6f3669c8f 100644 --- a/docs/i18n.zh-CN.md +++ b/docs/i18n.zh-CN.md @@ -64,8 +64,7 @@ export class AppModule { } ```ts import { en_US, DelonLocaleService } from '@delon/theme'; ... -constructor(private delonLocaleService: DelonLocaleService) { -} +private readonly i18n = inject(DelonLocaleService); switchLanguage() { this.delonLocaleService.setLocale(en_US); diff --git a/packages/abc/auto-focus/auto-focus.directive.ts b/packages/abc/auto-focus/auto-focus.directive.ts index 86fd5239e..25c419448 100644 --- a/packages/abc/auto-focus/auto-focus.directive.ts +++ b/packages/abc/auto-focus/auto-focus.directive.ts @@ -18,9 +18,9 @@ import { timer } from 'rxjs'; standalone: true }) export class AutoFocusDirective implements AfterViewInit { - private readonly el = inject>(ElementRef).nativeElement; + private readonly el: HTMLElement = inject(ElementRef).nativeElement; private readonly platform = inject(Platform); - private readonly d$ = inject(DestroyRef); + private readonly destroy$ = inject(DestroyRef); @Input({ transform: booleanAttribute }) enabled = true; @Input({ transform: numberAttribute }) delay = 300; @@ -31,7 +31,7 @@ export class AutoFocusDirective implements AfterViewInit { return; } timer(this.delay) - .pipe(takeUntilDestroyed(this.d$)) + .pipe(takeUntilDestroyed(this.destroy$)) .subscribe(() => el.focus({ preventScroll: false })); } } diff --git a/packages/abc/avatar-list/avatar-list.component.ts b/packages/abc/avatar-list/avatar-list.component.ts index 3ec5fe472..1afbee9f5 100644 --- a/packages/abc/avatar-list/avatar-list.component.ts +++ b/packages/abc/avatar-list/avatar-list.component.ts @@ -6,15 +6,16 @@ import { ChangeDetectorRef, Component, ContentChildren, + DestroyRef, Input, OnChanges, - Optional, QueryList, - ViewEncapsulation + ViewEncapsulation, + inject, + numberAttribute } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { InputNumber, NumberInput } from '@delon/util/decorator'; import { NzAvatarComponent } from 'ng-zorro-antd/avatar'; import type { NgStyleInterface, NzSizeLDSType } from 'ng-zorro-antd/core/types'; import { NzTooltipDirective } from 'ng-zorro-antd/tooltip'; @@ -36,16 +37,17 @@ import { AvatarListItemComponent } from './avatar-list-item.component'; imports: [NgStyle, NgClass, NzAvatarComponent, NzTooltipDirective] }) export class AvatarListComponent implements AfterViewInit, OnChanges { - static ngAcceptInputType_maxLength: NumberInput; + private readonly cdr = inject(ChangeDetectorRef); + private readonly directionality = inject(Directionality, { optional: true }); + private readonly destroy$ = inject(DestroyRef); private inited = false; @ContentChildren(AvatarListItemComponent, { descendants: false }) - private _items!: QueryList; - private dir$ = this.directionality.change?.pipe(takeUntilDestroyed()); + private readonly _items!: QueryList; items: AvatarListItemComponent[] = []; exceedCount = 0; - dir: Direction = 'ltr'; + dir?: Direction = 'ltr'; cls = ''; avatarSize: NzSizeLDSType = 'default'; @@ -63,14 +65,9 @@ export class AvatarListComponent implements AfterViewInit, OnChanges { break; } } - @Input() @InputNumber() maxLength = 0; + @Input({ transform: numberAttribute }) maxLength = 0; @Input() excessItemsStyle: NgStyleInterface | null = null; - constructor( - private cdr: ChangeDetectorRef, - @Optional() private directionality: Directionality - ) {} - private gen(): void { const { _items } = this; const maxLength = this.maxLength > 0 ? this.maxLength : _items.length; @@ -82,8 +79,8 @@ export class AvatarListComponent implements AfterViewInit, OnChanges { } ngAfterViewInit(): void { - this.dir = this.directionality.value; - this.dir$.subscribe((direction: Direction) => { + this.dir = this.directionality?.value; + this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { this.dir = direction; this.cdr.detectChanges(); }); diff --git a/packages/abc/cell/cell-host.directive.ts b/packages/abc/cell/cell-host.directive.ts index 3b4d140c9..226e0dac3 100644 --- a/packages/abc/cell/cell-host.directive.ts +++ b/packages/abc/cell/cell-host.directive.ts @@ -1,4 +1,4 @@ -import { Directive, Input, OnInit, Type, ViewContainerRef } from '@angular/core'; +import { Directive, Input, OnInit, Type, ViewContainerRef, inject } from '@angular/core'; import { warn } from '@delon/util/other'; @@ -10,12 +10,10 @@ import { CellWidgetData } from './cell.types'; standalone: true }) export class CellHostDirective implements OnInit { - @Input() data!: CellWidgetData; + private readonly srv = inject(CellService); + private readonly viewContainerRef = inject(ViewContainerRef); - constructor( - private srv: CellService, - private viewContainerRef: ViewContainerRef - ) {} + @Input() data!: CellWidgetData; ngOnInit(): void { const widget = this.data.options!.widget!; diff --git a/packages/abc/cell/cell.component.ts b/packages/abc/cell/cell.component.ts index 2c56e8112..1e9cab8f4 100644 --- a/packages/abc/cell/cell.component.ts +++ b/packages/abc/cell/cell.component.ts @@ -5,14 +5,15 @@ import { Component, ElementRef, EventEmitter, - Inject, Input, OnChanges, OnDestroy, Output, Renderer2, SimpleChange, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + inject } from '@angular/core'; import { FormsModule } from '@angular/forms'; import type { SafeValue } from '@angular/platform-browser'; @@ -20,7 +21,6 @@ import { Router } from '@angular/router'; import { Subscription } from 'rxjs'; import { updateHostClass } from '@delon/util/browser'; -import { BooleanInput, InputBoolean } from '@delon/util/decorator'; import { WINDOW } from '@delon/util/token'; import { NzBadgeComponent } from 'ng-zorro-antd/badge'; import { NzCheckboxComponent } from 'ng-zorro-antd/checkbox'; @@ -126,8 +126,13 @@ import type { CellDefaultText, CellOptions, CellTextResult, CellValue, CellWidge ] }) export class CellComponent implements OnChanges, OnDestroy { - static ngAcceptInputType_loading: BooleanInput; - static ngAcceptInputType_disabled: BooleanInput; + private readonly srv = inject(CellService); + private readonly router = inject(Router); + private readonly cdr = inject(ChangeDetectorRef); + private readonly renderer = inject(Renderer2); + private readonly imgSrv = inject(NzImageService); + private readonly win = inject(WINDOW); + private readonly el: HTMLElement = inject(ElementRef).nativeElement; private destroy$?: Subscription; @@ -139,8 +144,8 @@ export class CellComponent implements OnChanges, OnDestroy { @Input() value?: CellValue; @Output() readonly valueChange = new EventEmitter(); @Input() options?: CellOptions; - @Input() @InputBoolean() loading = false; - @Input() @InputBoolean() disabled = false; + @Input({ transform: booleanAttribute }) loading = false; + @Input({ transform: booleanAttribute }) disabled = false; get safeOpt(): CellOptions { return this.res?.options ?? {}; @@ -157,17 +162,6 @@ export class CellComponent implements OnChanges, OnDestroy { }; } - constructor( - private srv: CellService, - private router: Router, - private cdr: ChangeDetectorRef, - private el: ElementRef, - private renderer: Renderer2, - private imgSrv: NzImageService, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - @Inject(WINDOW) private win: any - ) {} - private updateValue(): void { this.destroy$?.unsubscribe(); this.destroy$ = this.srv.get(this.value, this.options).subscribe(res => { @@ -183,7 +177,7 @@ export class CellComponent implements OnChanges, OnDestroy { private setClass(): void { const { el, renderer } = this; const { renderType, size, type } = this.safeOpt; - updateHostClass(el.nativeElement, renderer, { + updateHostClass(el, renderer, { [`cell`]: true, [`cell__${renderType}`]: renderType != null, [`cell__${size}`]: size != null, @@ -191,7 +185,7 @@ export class CellComponent implements OnChanges, OnDestroy { [`cell__has-default`]: this.showDefault, [`cell__disabled`]: this.disabled }); - el.nativeElement.setAttribute('data-type', `${type}`); + el.setAttribute('data-type', `${type}`); } ngOnChanges(changes: { [p in keyof CellComponent]?: SimpleChange }): void { @@ -219,7 +213,7 @@ export class CellComponent implements OnChanges, OnDestroy { if (url == null) return; if (/https?:\/\//g.test(url)) { - (this.win as Window).open(url, link?.target); + this.win.open(url, link?.target); } else { this.router.navigateByUrl(url); } diff --git a/packages/abc/cell/cell.service.ts b/packages/abc/cell/cell.service.ts index aa3d534e2..2210a5059 100644 --- a/packages/abc/cell/cell.service.ts +++ b/packages/abc/cell/cell.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Type } from '@angular/core'; +import { Injectable, Type, inject } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { map, Observable, of } from 'rxjs'; @@ -22,6 +22,9 @@ import type { @Injectable({ providedIn: 'root' }) export class CellService { + private readonly nzI18n = inject(NzI18nService); + private readonly currency = inject(CurrencyService); + private readonly dom = inject(DomSanitizer); private globalOptions!: AlainCellConfig; private widgets: { [key: string]: CellWidget } = { date: { @@ -63,12 +66,7 @@ export class CellService { } }; - constructor( - configSrv: AlainConfigService, - private nzI18n: NzI18nService, - private currency: CurrencyService, - private dom: DomSanitizer - ) { + constructor(configSrv: AlainConfigService) { this.globalOptions = configSrv.merge('cell', { date: { format: 'yyyy-MM-dd HH:mm:ss' }, img: { size: 32 }, diff --git a/packages/abc/date-picker/range.directive.ts b/packages/abc/date-picker/range.directive.ts index b9579e788..594b4fc88 100644 --- a/packages/abc/date-picker/range.directive.ts +++ b/packages/abc/date-picker/range.directive.ts @@ -3,13 +3,12 @@ import { ComponentRef, Directive, EventEmitter, - Host, Input, OnDestroy, - Optional, Output, TemplateRef, - ViewContainerRef + ViewContainerRef, + inject } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; @@ -30,6 +29,10 @@ import { RangePickerShortcutTplComponent } from './range-shortcut.component'; export class RangePickerDirective implements OnDestroy, AfterViewInit { static ngAcceptInputType_shortcut: AlainDateRangePickerShortcut | string | null; + private readonly dom = inject(DomSanitizer); + private readonly vcr = inject(ViewContainerRef); + private readonly nativeComp = inject(NzRangePickerComponent, { host: true, optional: true }); + private defaultShortcuts: AlainDateRangePickerShortcut; private _shortcut: AlainDateRangePickerShortcut | null = null; private shortcutFactory: ComponentRef | null = null; @@ -60,22 +63,17 @@ export class RangePickerDirective implements OnDestroy, AfterViewInit { @Output() readonly ngModelEndChange = new EventEmitter(); private get dp(): NzDatePickerComponent { - return this.nativeComp.datePicker; + return this.nativeComp!.datePicker; } private get srv(): DatePickerService { return this.dp.datePickerService; } - constructor( - private dom: DomSanitizer, - configSrv: AlainConfigService, - @Host() @Optional() private nativeComp: NzRangePickerComponent, - private vcr: ViewContainerRef - ) { + constructor(configSrv: AlainConfigService) { if (typeof ngDevMode === 'undefined' || ngDevMode) { assert( - !!nativeComp, + !!this.nativeComp, `It should be attached to nz-range-picker component, for example: ''` ); } @@ -175,7 +173,7 @@ export class RangePickerDirective implements OnDestroy, AfterViewInit { }; extraFooter = instance.tpl; } - this.nativeComp.datePicker.extraFooter = extraFooter; + this.nativeComp!.datePicker.extraFooter = extraFooter; Promise.resolve().then(() => this.cd()); } diff --git a/packages/abc/down-file/down-file.directive.ts b/packages/abc/down-file/down-file.directive.ts index cbeb2ddf1..ac7c4371d 100644 --- a/packages/abc/down-file/down-file.directive.ts +++ b/packages/abc/down-file/down-file.directive.ts @@ -1,5 +1,5 @@ import { HttpResponse } from '@angular/common/http'; -import { Directive, ElementRef, EventEmitter, Input, Output } from '@angular/core'; +import { Directive, ElementRef, EventEmitter, Input, Output, inject } from '@angular/core'; import { finalize } from 'rxjs'; import { saveAs } from 'file-saver'; @@ -16,7 +16,8 @@ import type { NzSafeAny } from 'ng-zorro-antd/core/types'; standalone: true }) export class DownFileDirective { - private isFileSaverSupported = true; + private readonly el: HTMLButtonElement = inject(ElementRef).nativeElement; + private readonly _http = inject(_HttpClient); @Input('http-data') httpData: NzSafeAny; @Input('http-body') httpBody: NzSafeAny; @Input('http-method') httpMethod: string = 'get'; @@ -39,23 +40,19 @@ export class DownFileDirective { }); return arr.reduce((_o, item) => item, {}); } + private isFileSaverSupported = false; - constructor( - private el: ElementRef, - private _http: _HttpClient - ) { - let isFileSaverSupported = false; + constructor() { try { - isFileSaverSupported = !!new Blob(); + this.isFileSaverSupported = !!new Blob(); } catch {} - this.isFileSaverSupported = isFileSaverSupported; - if (!isFileSaverSupported) { - el.nativeElement.classList.add(`down-file__not-support`); + if (!this.isFileSaverSupported) { + this.el.classList.add(`down-file__not-support`); } } private setDisabled(status: boolean): void { - const el = this.el.nativeElement; + const el = this.el; el.disabled = status; el.classList[status ? 'add' : 'remove'](`down-file__disabled`); } diff --git a/packages/abc/ellipsis/ellipsis.component.ts b/packages/abc/ellipsis/ellipsis.component.ts index b10c2f178..48cd25474 100644 --- a/packages/abc/ellipsis/ellipsis.component.ts +++ b/packages/abc/ellipsis/ellipsis.component.ts @@ -6,17 +6,18 @@ import { ChangeDetectorRef, Component, ElementRef, - Inject, Input, NgZone, OnChanges, ViewChild, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { take } from 'rxjs'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzTooltipDirective } from 'ng-zorro-antd/tooltip'; @@ -31,11 +32,11 @@ import { NzTooltipDirective } from 'ng-zorro-antd/tooltip'; imports: [CdkObserveContent, NzTooltipDirective, NgTemplateOutlet, NgClass, NgStyle] }) export class EllipsisComponent implements AfterViewInit, OnChanges { - static ngAcceptInputType_tooltip: BooleanInput; - static ngAcceptInputType_length: NumberInput; - static ngAcceptInputType_lines: NumberInput; - static ngAcceptInputType_fullWidthRecognition: BooleanInput; - + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly ngZone = inject(NgZone); + private readonly dom = inject(DomSanitizer); + private readonly doc = inject(DOCUMENT); + private readonly cdr = inject(ChangeDetectorRef); private isSupportLineClamp = this.doc.body.style['webkitLineClamp'] !== undefined; @ViewChild('orgEl', { static: false }) private orgEl!: ElementRef; @ViewChild('shadowOrgEl', { static: false }) private shadowOrgEl!: ElementRef; @@ -47,10 +48,10 @@ export class EllipsisComponent implements AfterViewInit, OnChanges { text = ''; targetCount = 0; - @Input() @InputBoolean() tooltip = false; - @Input() @InputNumber(null) length?: number; - @Input() @InputNumber(null) lines?: number; - @Input() @InputBoolean() fullWidthRecognition = false; + @Input({ transform: booleanAttribute }) tooltip = false; + @Input({ transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) length?: number; + @Input({ transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) lines?: number; + @Input({ transform: booleanAttribute }) fullWidthRecognition = false; @Input() tail = '...'; get linsWord(): string { @@ -65,14 +66,6 @@ export class EllipsisComponent implements AfterViewInit, OnChanges { return this.doc.defaultView || window; } - constructor( - private el: ElementRef, - private ngZone: NgZone, - private dom: DomSanitizer, - @Inject(DOCUMENT) private doc: NzSafeAny, - private cdr: ChangeDetectorRef - ) {} - private getStrFullLength(str: string): number { return str.split('').reduce((pre, cur) => { const charCode = cur.charCodeAt(0); @@ -181,7 +174,8 @@ export class EllipsisComponent implements AfterViewInit, OnChanges { const lineText = orgNode.innerText || orgNode.textContent!; const lineHeight = parseInt(this.win.getComputedStyle(this.getEl('.ellipsis')).lineHeight!, 10); const targetHeight = lines! * lineHeight; - this.getEl('.ellipsis__handle').style.height = `${targetHeight}px`; + const handleEl = this.getEl('.ellipsis__handle'); + handleEl!.style.height = `${targetHeight}px`; if (orgNode.offsetHeight <= targetHeight) { this.text = lineText; @@ -199,8 +193,8 @@ export class EllipsisComponent implements AfterViewInit, OnChanges { } } - private getEl(cls: string): HTMLElement { - return this.el.nativeElement.querySelector(cls); + private getEl(cls: string): HTMLElement | null { + return this.el.querySelector(cls); } private executeOnStable(fn: () => void): void { diff --git a/packages/abc/error-collect/error-collect.component.ts b/packages/abc/error-collect/error-collect.component.ts index 649903bdc..b6cbaac45 100644 --- a/packages/abc/error-collect/error-collect.component.ts +++ b/packages/abc/error-collect/error-collect.component.ts @@ -7,19 +7,16 @@ import { Component, DestroyRef, ElementRef, - Inject, Input, OnInit, - Optional, ViewEncapsulation, - inject + inject, + numberAttribute } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { interval } from 'rxjs'; import { AlainConfigService } from '@delon/util/config'; -import { InputNumber } from '@delon/util/decorator'; -import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzIconDirective } from 'ng-zorro-antd/icon'; @Component({ @@ -42,24 +39,23 @@ import { NzIconDirective } from 'ng-zorro-antd/icon'; imports: [NzIconDirective] }) export class ErrorCollectComponent implements OnInit { + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly cdr = inject(ChangeDetectorRef); + private readonly doc = inject(DOCUMENT); + private readonly directionality = inject(Directionality, { optional: true }); + private readonly platform = inject(Platform); + private readonly destroy$ = inject(DestroyRef); + private formEl: HTMLFormElement | null = null; - private destroy$ = inject(DestroyRef); _hiden = true; count = 0; - dir: Direction = 'ltr'; + dir?: Direction = 'ltr'; - @Input() @InputNumber() freq!: number; - @Input() @InputNumber() offsetTop!: number; + @Input({ transform: numberAttribute }) freq!: number; + @Input({ transform: numberAttribute }) offsetTop!: number; - constructor( - private el: ElementRef, - private cdr: ChangeDetectorRef, - @Inject(DOCUMENT) private doc: NzSafeAny, - configSrv: AlainConfigService, - @Optional() private directionality: Directionality, - private platform: Platform - ) { + constructor(configSrv: AlainConfigService) { configSrv.attach(this, 'errorCollect', { freq: 500, offsetTop: 65 + 64 + 8 * 2 }); } @@ -87,8 +83,8 @@ export class ErrorCollectComponent implements OnInit { } private install(): void { - this.dir = this.directionality.value; - this.directionality.change?.pipe(takeUntilDestroyed(this.destroy$)).subscribe((direction: Direction) => { + this.dir = this.directionality?.value; + this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { this.dir = direction; this.cdr.detectChanges(); }); @@ -113,7 +109,7 @@ export class ErrorCollectComponent implements OnInit { ngOnInit(): void { if (!this.platform.isBrowser) return; - this.formEl = this.findParent(this.el.nativeElement, 'form'); + this.formEl = this.findParent(this.el, 'form'); if (this.formEl === null) throw new Error('No found form element'); this.install(); } diff --git a/packages/abc/exception/exception.component.ts b/packages/abc/exception/exception.component.ts index 13d47e431..d2b6cce21 100644 --- a/packages/abc/exception/exception.component.ts +++ b/packages/abc/exception/exception.component.ts @@ -8,7 +8,6 @@ import { ElementRef, Input, OnInit, - Optional, ViewChild, ViewEncapsulation, inject @@ -42,13 +41,18 @@ export type ExceptionType = 403 | 404 | 500; export class ExceptionComponent implements OnInit { static ngAcceptInputType_type: ExceptionType | string; - private destroy$ = inject(DestroyRef); + private readonly i18n = inject(DelonLocaleService); + private readonly dom = inject(DomSanitizer); + private readonly directionality = inject(Directionality, { optional: true }); + private readonly cdr = inject(ChangeDetectorRef); + private readonly destroy$ = inject(DestroyRef); + @ViewChild('conTpl', { static: true }) private conTpl!: ElementRef; _type!: ExceptionType; locale: LocaleData = {}; hasCon = false; - dir: Direction = 'ltr'; + dir?: Direction = 'ltr'; _img: SafeUrl = ''; _title: SafeHtml = ''; @@ -92,13 +96,7 @@ export class ExceptionComponent implements OnInit { this.cdr.detectChanges(); } - constructor( - private i18n: DelonLocaleService, - private dom: DomSanitizer, - configSrv: AlainConfigService, - @Optional() private directionality: Directionality, - private cdr: ChangeDetectorRef - ) { + constructor(configSrv: AlainConfigService) { configSrv.attach(this, 'exception', { typeDict: { 403: { @@ -118,8 +116,8 @@ export class ExceptionComponent implements OnInit { } ngOnInit(): void { - this.dir = this.directionality.value; - this.directionality.change?.pipe(takeUntilDestroyed(this.destroy$)).subscribe((direction: Direction) => { + this.dir = this.directionality?.value; + this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { this.dir = direction; this.cdr.detectChanges(); }); diff --git a/packages/abc/footer-toolbar/footer-toolbar.component.ts b/packages/abc/footer-toolbar/footer-toolbar.component.ts index 8e93f0ae4..93026c3e0 100644 --- a/packages/abc/footer-toolbar/footer-toolbar.component.ts +++ b/packages/abc/footer-toolbar/footer-toolbar.component.ts @@ -3,19 +3,18 @@ import { ChangeDetectionStrategy, Component, ElementRef, - Inject, Input, OnDestroy, OnInit, Renderer2, TemplateRef, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + inject } from '@angular/core'; import { ErrorCollectComponent } from '@delon/abc/error-collect'; -import { BooleanInput, InputBoolean } from '@delon/util/decorator'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; -import type { NzSafeAny } from 'ng-zorro-antd/core/types'; const CLSBODY = 'footer-toolbar__body'; @@ -30,27 +29,19 @@ const CLSBODY = 'footer-toolbar__body'; imports: [NzStringTemplateOutletDirective, ErrorCollectComponent] }) export class FooterToolbarComponent implements OnInit, OnDestroy { - static ngAcceptInputType_errorCollect: BooleanInput; + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly renderer = inject(Renderer2); + private readonly bodyCls = inject(DOCUMENT).querySelector('body')?.classList; - @Input() @InputBoolean() errorCollect = false; + @Input({ transform: booleanAttribute }) errorCollect = false; @Input() extra?: string | TemplateRef; - constructor( - private el: ElementRef, - private renderer: Renderer2, - @Inject(DOCUMENT) private doc: NzSafeAny - ) {} - - private get bodyCls(): DOMTokenList { - return (this.doc.querySelector('body') as HTMLElement).classList; - } - ngOnInit(): void { - this.renderer.addClass(this.el.nativeElement, 'footer-toolbar'); - this.bodyCls.add(CLSBODY); + this.renderer.addClass(this.el, 'footer-toolbar'); + this.bodyCls?.add(CLSBODY); } ngOnDestroy(): void { - this.bodyCls.remove(CLSBODY); + this.bodyCls?.remove(CLSBODY); } } diff --git a/packages/abc/full-content/full-content-toggle.directive.ts b/packages/abc/full-content/full-content-toggle.directive.ts index 79a4639b7..b4f5d95ff 100644 --- a/packages/abc/full-content/full-content-toggle.directive.ts +++ b/packages/abc/full-content/full-content-toggle.directive.ts @@ -1,4 +1,4 @@ -import { Directive } from '@angular/core'; +import { Directive, inject } from '@angular/core'; import { FullContentComponent } from './full-content.component'; @@ -11,7 +11,7 @@ import { FullContentComponent } from './full-content.component'; standalone: true }) export class FullContentToggleDirective { - constructor(private parent: FullContentComponent) {} + private readonly parent = inject(FullContentComponent); _click(): void { this.parent.toggle(); diff --git a/packages/abc/full-content/full-content.component.ts b/packages/abc/full-content/full-content.component.ts index b5b41359c..83a0afb4a 100644 --- a/packages/abc/full-content/full-content.component.ts +++ b/packages/abc/full-content/full-content.component.ts @@ -7,22 +7,20 @@ import { DestroyRef, ElementRef, EventEmitter, - Inject, Input, OnChanges, OnDestroy, OnInit, Output, ViewEncapsulation, - inject + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ActivationEnd, ActivationStart, Event, Router } from '@angular/router'; import { fromEvent, debounceTime, filter } from 'rxjs'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; -import type { NzSafeAny } from 'ng-zorro-antd/core/types'; - import { FullContentService } from './full-content.service'; const wrapCls = `full-content__body`; @@ -43,30 +41,24 @@ const hideTitleCls = `full-content__hidden-title`; standalone: true }) export class FullContentComponent implements AfterViewInit, OnInit, OnChanges, OnDestroy { - static ngAcceptInputType_fullscreen: BooleanInput; - static ngAcceptInputType_hideTitle: BooleanInput; - static ngAcceptInputType_padding: NumberInput; - - private bodyEl!: HTMLElement; + private readonly destroy$ = inject(DestroyRef); + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly cdr = inject(ChangeDetectorRef); + private readonly srv = inject(FullContentService); + private readonly router = inject(Router); + private readonly doc = inject(DOCUMENT); + + private bodyEl = this.doc.querySelector('body')!; private inited = false; private id = `_full-content-${Math.random().toString(36).substring(2)}`; - private destroy$ = inject(DestroyRef); _height = 0; - @Input() @InputBoolean() fullscreen?: boolean; - @Input() @InputBoolean() hideTitle = true; - @Input() @InputNumber() padding = 24; + @Input({ transform: booleanAttribute }) fullscreen?: boolean; + @Input({ transform: booleanAttribute }) hideTitle = true; + @Input({ transform: numberAttribute }) padding = 24; @Output() readonly fullscreenChange = new EventEmitter(); - constructor( - private el: ElementRef, - private cdr: ChangeDetectorRef, - private srv: FullContentService, - private router: Router, - @Inject(DOCUMENT) private doc: NzSafeAny - ) {} - private updateCls(): void { const clss = this.bodyEl.classList; if (this.fullscreen) { @@ -89,8 +81,7 @@ export class FullContentComponent implements AfterViewInit, OnInit, OnChanges, O } private updateHeight(): void { - this._height = - this.bodyEl.getBoundingClientRect().height - this.el.nativeElement.getBoundingClientRect().top - this.padding; + this._height = this.bodyEl.getBoundingClientRect().height - this.el.getBoundingClientRect().top - this.padding; this.cdr.detectChanges(); } @@ -100,9 +91,8 @@ export class FullContentComponent implements AfterViewInit, OnInit, OnChanges, O ngOnInit(): void { this.inited = true; - this.bodyEl = this.doc.querySelector('body'); this.bodyEl.classList.add(wrapCls); - this.el.nativeElement.id = this.id; + this.el.id = this.id; this.updateCls(); diff --git a/packages/abc/global-footer/global-footer-item.component.ts b/packages/abc/global-footer/global-footer-item.component.ts index 63eddb566..f7e86b072 100644 --- a/packages/abc/global-footer/global-footer-item.component.ts +++ b/packages/abc/global-footer/global-footer-item.component.ts @@ -1,6 +1,12 @@ -import { ChangeDetectionStrategy, Component, Input, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; - -import { BooleanInput, InputBoolean } from '@delon/util/decorator'; +import { + ChangeDetectionStrategy, + Component, + Input, + TemplateRef, + ViewChild, + ViewEncapsulation, + booleanAttribute +} from '@angular/core'; @Component({ selector: 'global-footer-item', @@ -12,10 +18,8 @@ import { BooleanInput, InputBoolean } from '@delon/util/decorator'; standalone: true }) export class GlobalFooterItemComponent { - static ngAcceptInputType_blankTarget: BooleanInput; - @ViewChild('host', { static: true }) host!: TemplateRef; @Input() href?: string; - @Input() @InputBoolean() blankTarget?: boolean; + @Input({ transform: booleanAttribute }) blankTarget?: boolean; } diff --git a/packages/abc/global-footer/global-footer.component.ts b/packages/abc/global-footer/global-footer.component.ts index 6231a6a08..926f4c7cc 100644 --- a/packages/abc/global-footer/global-footer.component.ts +++ b/packages/abc/global-footer/global-footer.component.ts @@ -5,19 +5,18 @@ import { ChangeDetectorRef, Component, ContentChildren, - Inject, + DestroyRef, Input, OnInit, - Optional, QueryList, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { DomSanitizer } from '@angular/platform-browser'; import { Router } from '@angular/router'; import { WINDOW } from '@delon/util/token'; -import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { GlobalFooterItemComponent } from './global-footer-item.component'; import { GlobalFooterLink } from './global-footer.types'; @@ -37,10 +36,16 @@ import { GlobalFooterLink } from './global-footer.types'; imports: [NgTemplateOutlet] }) export class GlobalFooterComponent implements OnInit { - private dir$ = this.directionality.change?.pipe(takeUntilDestroyed()); + private readonly router = inject(Router); + private readonly win = inject(WINDOW); + private readonly dom = inject(DomSanitizer); + private readonly directionality = inject(Directionality, { optional: true }); + private readonly cdr = inject(ChangeDetectorRef); + private readonly destroy$ = inject(DestroyRef); + private _links: GlobalFooterLink[] = []; - dir: Direction = 'ltr'; + dir?: Direction = 'ltr'; @Input() set links(val: GlobalFooterLink[]) { @@ -53,14 +58,6 @@ export class GlobalFooterComponent implements OnInit { @ContentChildren(GlobalFooterItemComponent) readonly items!: QueryList; - constructor( - private router: Router, - @Inject(WINDOW) private win: NzSafeAny, - private dom: DomSanitizer, - @Optional() private directionality: Directionality, - private cdr: ChangeDetectorRef - ) {} - to(item: GlobalFooterLink | GlobalFooterItemComponent): void { if (!item.href) { return; @@ -77,8 +74,8 @@ export class GlobalFooterComponent implements OnInit { } ngOnInit(): void { - this.dir = this.directionality.value; - this.dir$.subscribe((direction: Direction) => { + this.dir = this.directionality?.value; + this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { this.dir = direction; this.cdr.detectChanges(); }); diff --git a/packages/abc/hotkey/hotkey.directive.ts b/packages/abc/hotkey/hotkey.directive.ts index 914f4d78d..487a0c686 100644 --- a/packages/abc/hotkey/hotkey.directive.ts +++ b/packages/abc/hotkey/hotkey.directive.ts @@ -1,10 +1,14 @@ import { Platform } from '@angular/cdk/platform'; -import { Directive, ElementRef, Input, NgZone, OnDestroy } from '@angular/core'; +import { Directive, ElementRef, Input, NgZone, OnDestroy, inject } from '@angular/core'; import { install, uninstall } from '@github/hotkey'; @Directive({ selector: '[hotkey]', standalone: true }) export class HotkeyDirective implements OnDestroy { + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly ngZone = inject(NgZone); + private readonly platform = inject(Platform); + /** * Specify [hotkey format](https://github.com/github/hotkey#hotkey-string-format) * @@ -14,18 +18,12 @@ export class HotkeyDirective implements OnDestroy { set hotkey(key: string) { if (!this.platform.isBrowser) return; - this.ngZone.runOutsideAngular(() => install(this.el.nativeElement, key)); + this.ngZone.runOutsideAngular(() => install(this.el, key)); } - constructor( - private el: ElementRef, - private ngZone: NgZone, - private platform: Platform - ) {} - ngOnDestroy(): void { if (!this.platform.isBrowser) return; - this.ngZone.runOutsideAngular(() => uninstall(this.el.nativeElement)); + this.ngZone.runOutsideAngular(() => uninstall(this.el)); } } diff --git a/packages/abc/loading/loading.component.ts b/packages/abc/loading/loading.component.ts index 5412213f5..c8f9bc81c 100644 --- a/packages/abc/loading/loading.component.ts +++ b/packages/abc/loading/loading.component.ts @@ -22,7 +22,7 @@ import { LoadingCustom, LoadingIcon, LoadingShowOptions } from './loading.types' }) export class LoadingDefaultComponent { options!: LoadingShowOptions; - dir: Direction = 'ltr'; + dir?: Direction = 'ltr'; get icon(): LoadingIcon { return this.options.icon!; diff --git a/packages/abc/loading/loading.service.ts b/packages/abc/loading/loading.service.ts index 3299b0373..fecb84609 100644 --- a/packages/abc/loading/loading.service.ts +++ b/packages/abc/loading/loading.service.ts @@ -1,7 +1,7 @@ import { Directionality } from '@angular/cdk/bidi'; import { Overlay, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; -import { ComponentRef, Injectable, OnDestroy, Optional } from '@angular/core'; +import { ComponentRef, Injectable, OnDestroy, inject } from '@angular/core'; import { Subject, Subscription, timer, debounce } from 'rxjs'; import { AlainConfigService, AlainLoadingConfig } from '@delon/util/config'; @@ -11,6 +11,10 @@ import { LoadingShowOptions } from './loading.types'; @Injectable({ providedIn: 'root' }) export class LoadingService implements OnDestroy { + private readonly overlay = inject(Overlay); + private readonly configSrv = inject(AlainConfigService); + private readonly directionality = inject(Directionality, { optional: true }); + private _overlayRef?: OverlayRef; private compRef: ComponentRef | null = null; private opt: LoadingShowOptions | null = null; @@ -22,12 +26,8 @@ export class LoadingService implements OnDestroy { return this.compRef != null ? this.compRef.instance : null; } - constructor( - private overlay: Overlay, - private configSrv: AlainConfigService, - @Optional() private directionality: Directionality - ) { - this.cog = configSrv.merge('loading', { + constructor() { + this.cog = this.configSrv.merge('loading', { type: 'spin', text: '加载中...', icon: { @@ -55,7 +55,7 @@ export class LoadingService implements OnDestroy { backdropClass: 'loading-backdrop' }); this.compRef = this._overlayRef.attach(new ComponentPortal(LoadingDefaultComponent)); - const dir = this.configSrv.get('loading')!.direction || this.directionality.value; + const dir = this.configSrv.get('loading')!.direction || this.directionality?.value; if (this.instance != null) { this.instance!!.options = this.opt; this.instance!!.dir = dir; diff --git a/packages/abc/lodop/lodop.service.ts b/packages/abc/lodop/lodop.service.ts index d82bddc3e..e482dc9e5 100644 --- a/packages/abc/lodop/lodop.service.ts +++ b/packages/abc/lodop/lodop.service.ts @@ -1,4 +1,4 @@ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { Observable, of, Subject } from 'rxjs'; import { AlainConfigService, AlainLodopConfig } from '@delon/util/config'; @@ -9,6 +9,8 @@ import { Lodop, LodopPrintResult, LodopResult } from './lodop.types'; @Injectable({ providedIn: 'root' }) export class LodopService implements OnDestroy { + private readonly scriptSrv = inject(LazyService); + private defaultConfig: AlainLodopConfig; private _cog!: AlainLodopConfig; private pending = false; @@ -17,10 +19,7 @@ export class LodopService implements OnDestroy { private _events = new Subject(); private printBuffer: NzSafeAny[] = []; - constructor( - private scriptSrv: LazyService, - configSrv: AlainConfigService - ) { + constructor(configSrv: AlainConfigService) { this.defaultConfig = configSrv.merge('lodop', { url: '//localhost:8443/CLodopfuncs.js', name: 'CLODOP', diff --git a/packages/abc/media/media.component.ts b/packages/abc/media/media.component.ts index fc6b7d031..c196d5141 100644 --- a/packages/abc/media/media.component.ts +++ b/packages/abc/media/media.component.ts @@ -14,14 +14,15 @@ import { Renderer2, SimpleChange, ViewEncapsulation, - inject + inject, + numberAttribute } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { timer, take } from 'rxjs'; import type Plyr from 'plyr'; -import { InputNumber, NumberInput, ZoneOutside } from '@delon/util/decorator'; +import { ZoneOutside } from '@delon/util/decorator'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { MediaService } from './media.service'; @@ -41,30 +42,26 @@ export type MediaType = 'html5' | 'youtube' | 'video' | 'audio'; standalone: true }) export class MediaComponent implements OnChanges, AfterViewInit, OnDestroy { - static ngAcceptInputType_delay: NumberInput; + private readonly destroy$ = inject(DestroyRef); + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly renderer = inject(Renderer2); + private readonly ngZone = inject(NgZone); + private readonly srv = inject(MediaService); + private readonly platform = inject(Platform); private _p?: Plyr | null; private videoEl?: HTMLElement; - private destroy$ = inject(DestroyRef); @Input() type: MediaType = 'video'; @Input() source?: string | Plyr.SourceInfo; @Input() options?: Plyr.Options; - @Input() @InputNumber() delay = 0; + @Input({ transform: numberAttribute }) delay = 0; @Output() readonly ready = new EventEmitter(); get player(): Plyr | undefined | null { return this._p; } - constructor( - private el: ElementRef, - private renderer: Renderer2, - private srv: MediaService, - private ngZone: NgZone, - private platform: Platform - ) {} - @ZoneOutside() private initDelay(): void { timer(this.delay) @@ -95,11 +92,11 @@ export class MediaComponent implements OnChanges, AfterViewInit, OnDestroy { private ensureElement(): void { const { type } = this; - let el = this.el.nativeElement.querySelector(type) as HTMLElement; + let el = this.el.querySelector(type) as HTMLElement; if (!el) { el = this.renderer.createElement(type); (el as HTMLVideoElement).controls = true; - this.el.nativeElement.appendChild(el); + this.el.appendChild(el); } this.videoEl = el; } diff --git a/packages/abc/media/media.service.ts b/packages/abc/media/media.service.ts index 6fedd1e7c..5316ce958 100644 --- a/packages/abc/media/media.service.ts +++ b/packages/abc/media/media.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, Subject, share } from 'rxjs'; import { AlainConfigService, AlainMediaConfig } from '@delon/util/config'; @@ -6,6 +6,9 @@ import { LazyService } from '@delon/util/other'; @Injectable({ providedIn: 'root' }) export class MediaService { + private readonly cogSrv = inject(AlainConfigService); + private readonly lazySrv = inject(LazyService); + private _cog!: AlainMediaConfig; private loading = false; private loaded = false; @@ -24,11 +27,6 @@ export class MediaService { )!; } - constructor( - private cogSrv: AlainConfigService, - private lazySrv: LazyService - ) {} - load(): this { if (this.loading) { if (this.loaded) { diff --git a/packages/abc/notice-icon/notice-icon.component.ts b/packages/abc/notice-icon/notice-icon.component.ts index 99252a53a..834377f82 100644 --- a/packages/abc/notice-icon/notice-icon.component.ts +++ b/packages/abc/notice-icon/notice-icon.component.ts @@ -9,12 +9,14 @@ import { OnDestroy, OnInit, Output, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import { Subscription } from 'rxjs'; import { DelonLocaleService, LocaleData } from '@delon/theme'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import { NzBadgeComponent } from 'ng-zorro-antd/badge'; import type { NgClassType } from 'ng-zorro-antd/core/types'; import { NzDropDownDirective, NzDropdownMenuComponent } from 'ng-zorro-antd/dropdown'; @@ -48,21 +50,18 @@ import { NoticeIconSelect, NoticeItem } from './notice-icon.types'; ] }) export class NoticeIconComponent implements OnInit, OnChanges, OnDestroy { - static ngAcceptInputType_count: NumberInput; - static ngAcceptInputType_loading: BooleanInput; - static ngAcceptInputType_popoverVisible: BooleanInput; - static ngAcceptInputType_centered: BooleanInput; - + private readonly i18n = inject(DelonLocaleService); + private readonly cdr = inject(ChangeDetectorRef); private i18n$!: Subscription; locale: LocaleData = {}; @Input() data: NoticeItem[] = []; - @Input() @InputNumber() count?: number; - @Input() @InputBoolean() loading = false; - @Input() @InputBoolean() popoverVisible = false; + @Input({ transform: numberAttribute }) count?: number; + @Input({ transform: booleanAttribute }) loading = false; + @Input({ transform: booleanAttribute }) popoverVisible = false; @Input() btnClass?: NgClassType; @Input() btnIconClass?: NgClassType; - @Input() @InputBoolean() centered = false; + @Input({ transform: booleanAttribute }) centered = false; @Output() readonly select = new EventEmitter(); @Output() readonly clear = new EventEmitter(); @Output() readonly popoverVisibleChange = new EventEmitter(); @@ -71,11 +70,6 @@ export class NoticeIconComponent implements OnInit, OnChanges, OnDestroy { return `header-dropdown notice-icon${!this.centered ? ' notice-icon__tab-left' : ''}`; } - constructor( - private i18n: DelonLocaleService, - private cdr: ChangeDetectorRef - ) {} - onVisibleChange(result: boolean): void { this.popoverVisibleChange.emit(result); } diff --git a/packages/abc/observers/observer-size.ts b/packages/abc/observers/observer-size.ts index 3d81d96a9..54d1ae43f 100644 --- a/packages/abc/observers/observer-size.ts +++ b/packages/abc/observers/observer-size.ts @@ -7,7 +7,8 @@ import { NgModule, NgZone, OnDestroy, - Output + Output, + inject } from '@angular/core'; import { Observable, Observer, Subject, Subscription } from 'rxjs'; @@ -84,15 +85,13 @@ export class SizeObserver implements OnDestroy { standalone: true }) export class ObserverSize implements AfterViewInit, OnDestroy { + private readonly _obs = inject(SizeObserver); + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly ngZone = inject(NgZone); + private _sub$: Subscription | null = null; @Output('observeSize') readonly event = new EventEmitter(); - constructor( - private _obs: SizeObserver, - private el: ElementRef, - private ngZone: NgZone - ) {} - ngAfterViewInit(): void { if (!this._sub$) { this._sub(); @@ -101,7 +100,7 @@ export class ObserverSize implements AfterViewInit, OnDestroy { private _sub(): void { this._unsub(); - const stream = this._obs.observe(this.el.nativeElement); + const stream = this._obs.observe(this.el); this.ngZone.runOutsideAngular(() => { this._sub$ = stream.subscribe(this.event); }); diff --git a/packages/abc/onboarding/onboarding.component.ts b/packages/abc/onboarding/onboarding.component.ts index 60b442553..da678b59e 100644 --- a/packages/abc/onboarding/onboarding.component.ts +++ b/packages/abc/onboarding/onboarding.component.ts @@ -8,11 +8,10 @@ import { Component, ElementRef, EventEmitter, - Inject, OnDestroy, - Optional, ViewChild, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { NzButtonComponent } from 'ng-zorro-antd/button'; @@ -48,6 +47,11 @@ interface OnboardingLightData { imports: [NzPopoverDirective, NzStringTemplateOutletDirective, NzButtonComponent, NzNoAnimationDirective] }) export class OnboardingComponent implements OnDestroy, AfterViewInit { + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly platform = inject(Platform); + private readonly cdr = inject(ChangeDetectorRef); + private readonly doc = inject(DOCUMENT); + private time: NzSafeAny; private prevSelectorEl?: HTMLElement; config!: OnboardingConfig; @@ -75,13 +79,6 @@ export class OnboardingComponent implements OnDestroy, AfterViewInit { return this._getDoc().defaultView || window; } - constructor( - private el: ElementRef, - @Optional() @Inject(DOCUMENT) private doc: NzSafeAny, - private platform: Platform, - private cdr: ChangeDetectorRef - ) {} - private getLightData(): OnboardingLightData | null { const doc = this._getDoc(); const win = this._getWin(); @@ -143,7 +140,7 @@ export class OnboardingComponent implements OnDestroy, AfterViewInit { return; } - const lightStyle = (this.el.nativeElement.querySelector('.onboarding__light') as HTMLElement).style; + const lightStyle = (this.el.querySelector('.onboarding__light') as HTMLElement).style; lightStyle.top = `${pos.top}px`; lightStyle.left = `${pos.left}px`; lightStyle.width = `${pos.width}px`; diff --git a/packages/abc/onboarding/onboarding.service.ts b/packages/abc/onboarding/onboarding.service.ts index 2c3bfb00f..83193cb7a 100644 --- a/packages/abc/onboarding/onboarding.service.ts +++ b/packages/abc/onboarding/onboarding.service.ts @@ -4,11 +4,10 @@ import { ApplicationRef, ComponentRef, EmbeddedViewRef, - Inject, Injectable, OnDestroy, - Optional, - createComponent + createComponent, + inject } from '@angular/core'; import { Router } from '@angular/router'; import { of, pipe, Subscription, delay, switchMap } from 'rxjs'; @@ -18,11 +17,19 @@ import { AlainConfigService } from '@delon/util/config'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { OnboardingComponent } from './onboarding.component'; -import { ONBOARDING_STORE_TOKEN, OnBoardingKeyStore } from './onboarding.storage'; +import { ONBOARDING_STORE_TOKEN } from './onboarding.storage'; import { OnboardingConfig, OnboardingItem, OnboardingOpType } from './onboarding.types'; @Injectable({ providedIn: 'root' }) export class OnboardingService implements OnDestroy { + private readonly i18n = inject(DelonLocaleService); + private readonly appRef = inject(ApplicationRef); + private readonly router = inject(Router); + private readonly doc = inject(DOCUMENT); + private readonly configSrv = inject(AlainConfigService); + private readonly keyStoreSrv = inject(ONBOARDING_STORE_TOKEN); + private readonly directionality = inject(Directionality, { optional: true }); + private compRef!: ComponentRef; private op$!: Subscription; private config?: OnboardingConfig; @@ -44,16 +51,6 @@ export class OnboardingService implements OnDestroy { return this._running; } - constructor( - private i18n: DelonLocaleService, - private appRef: ApplicationRef, - private router: Router, - @Inject(DOCUMENT) private doc: NzSafeAny, - private configSrv: AlainConfigService, - @Inject(ONBOARDING_STORE_TOKEN) private keyStoreSrv: OnBoardingKeyStore, - @Optional() private directionality: Directionality - ) {} - private attach(): void { const compRef = createComponent(OnboardingComponent, { environmentInjector: this.appRef.injector @@ -119,7 +116,7 @@ export class OnboardingService implements OnDestroy { ...this.i18n.getData('onboarding'), ...items[this.active] } as OnboardingItem; - const dir = this.configSrv.get('onboarding')!.direction || this.directionality.value; + const dir = this.configSrv.get('onboarding')!.direction || this.directionality?.value; Object.assign(this.compRef.instance, { item, config: this.config, active: this.active, max: items.length, dir }); const pipes = [ switchMap(() => (item.url ? this.router.navigateByUrl(item.url) : of(true))), diff --git a/packages/abc/page-header/page-header.component.ts b/packages/abc/page-header/page-header.component.ts index dea739bd3..6d357f140 100644 --- a/packages/abc/page-header/page-header.component.ts +++ b/packages/abc/page-header/page-header.component.ts @@ -7,26 +7,27 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + DestroyRef, ElementRef, - Inject, Input, OnChanges, OnInit, - Optional, Renderer2, TemplateRef, ViewChild, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { NavigationEnd, Router, RouterLink } from '@angular/router'; -import { merge, filter } from 'rxjs'; +import { merge, filter, Observable } from 'rxjs'; import { ReuseTabService } from '@delon/abc/reuse-tab'; -import { AlainI18NService, ALAIN_I18N_TOKEN, Menu, MenuService, SettingsService, TitleService } from '@delon/theme'; +import { ALAIN_I18N_TOKEN, Menu, MenuService, SettingsService, TitleService } from '@delon/theme'; import { isEmpty } from '@delon/util/browser'; import { AlainConfigService } from '@delon/util/config'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import { NzAffixComponent } from 'ng-zorro-antd/affix'; import { NzBreadCrumbComponent, NzBreadCrumbItemComponent } from 'ng-zorro-antd/breadcrumb'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; @@ -58,21 +59,21 @@ interface PageHeaderPath { ] }) export class PageHeaderComponent implements OnInit, OnChanges, AfterViewInit { - static ngAcceptInputType_loading: BooleanInput; - static ngAcceptInputType_wide: BooleanInput; - static ngAcceptInputType_autoBreadcrumb: BooleanInput; - static ngAcceptInputType_autoTitle: BooleanInput; - static ngAcceptInputType_syncTitle: BooleanInput; - static ngAcceptInputType_fixed: BooleanInput; - static ngAcceptInputType_fixedOffsetTop: NumberInput; - static ngAcceptInputType_recursiveBreadcrumb: BooleanInput; - - private dir$ = this.directionality.change?.pipe(takeUntilDestroyed()); + private readonly renderer = inject(Renderer2); + private readonly router = inject(Router); + private readonly cdr = inject(ChangeDetectorRef); + private readonly menuSrv = inject(MenuService); + private readonly i18nSrv = inject(ALAIN_I18N_TOKEN, { optional: true }); + private readonly titleSrv = inject(TitleService, { optional: true }); + private readonly reuseSrv = inject(ReuseTabService, { optional: true }); + private readonly directionality = inject(Directionality, { optional: true }); + private readonly destroy$ = inject(DestroyRef); + @ViewChild('conTpl', { static: false }) private conTpl!: ElementRef; @ViewChild('affix', { static: false }) private affix!: NzAffixComponent; inited = false; isBrowser = true; - dir: Direction = 'ltr'; + dir?: Direction = 'ltr'; private get menus(): Menu[] { return this.menuSrv.getPathByUrl(this.router.url, this.recursiveBreadcrumb); @@ -98,18 +99,18 @@ export class PageHeaderComponent implements OnInit, OnChanges, AfterViewInit { } @Input() titleSub?: string | TemplateRef | null; - @Input() @InputBoolean() loading = false; - @Input() @InputBoolean() wide = false; + @Input({ transform: booleanAttribute }) loading = false; + @Input({ transform: booleanAttribute }) wide = false; @Input() home?: string; @Input() homeLink?: string; @Input() homeI18n?: string; - @Input() @InputBoolean() autoBreadcrumb!: boolean; - @Input() @InputBoolean() autoTitle!: boolean; - @Input() @InputBoolean() syncTitle!: boolean; - @Input() @InputBoolean() fixed!: boolean; - @Input() @InputNumber() fixedOffsetTop!: number; + @Input({ transform: booleanAttribute }) autoBreadcrumb!: boolean; + @Input({ transform: booleanAttribute }) autoTitle!: boolean; + @Input({ transform: booleanAttribute }) syncTitle!: boolean; + @Input({ transform: booleanAttribute }) fixed!: boolean; + @Input({ transform: numberAttribute }) fixedOffsetTop!: number; @Input() breadcrumb?: TemplateRef | null = null; - @Input() @InputBoolean() recursiveBreadcrumb!: boolean; + @Input({ transform: booleanAttribute }) recursiveBreadcrumb!: boolean; @Input() logo?: TemplateRef | null = null; @Input() action?: TemplateRef | null = null; @Input() content?: TemplateRef | null = null; @@ -118,19 +119,7 @@ export class PageHeaderComponent implements OnInit, OnChanges, AfterViewInit { // #endregion - constructor( - settings: SettingsService, - private renderer: Renderer2, - private router: Router, - private menuSrv: MenuService, - @Optional() @Inject(ALAIN_I18N_TOKEN) private i18nSrv: AlainI18NService, - @Optional() @Inject(TitleService) private titleSrv: TitleService, - @Optional() @Inject(ReuseTabService) private reuseSrv: ReuseTabService, - private cdr: ChangeDetectorRef, - configSrv: AlainConfigService, - platform: Platform, - @Optional() private directionality: Directionality - ) { + constructor(settings: SettingsService, configSrv: AlainConfigService, platform: Platform) { this.isBrowser = platform.isBrowser; configSrv.attach(this, 'pageHeader', { home: '首页', @@ -149,7 +138,10 @@ export class PageHeaderComponent implements OnInit, OnChanges, AfterViewInit { ) .subscribe(() => this.affix.updatePosition({} as NzSafeAny)); - merge(menuSrv.change, router.events.pipe(filter(ev => ev instanceof NavigationEnd)), i18nSrv.change) + const obsList: Array> = [this.router.events.pipe(filter(ev => ev instanceof NavigationEnd))]; + if (this.menuSrv != null) obsList.push(this.menuSrv.change); + if (this.i18nSrv != null) obsList.push(this.i18nSrv.change); + merge(...obsList) .pipe( takeUntilDestroyed(), filter(() => this.inited) @@ -215,8 +207,8 @@ export class PageHeaderComponent implements OnInit, OnChanges, AfterViewInit { } ngOnInit(): void { - this.dir = this.directionality.value; - this.dir$.subscribe((direction: Direction) => { + this.dir = this.directionality?.value; + this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { this.dir = direction; this.cdr.detectChanges(); }); diff --git a/packages/abc/pdf/pdf.component.ts b/packages/abc/pdf/pdf.component.ts index 6620726ee..edc1d11f0 100644 --- a/packages/abc/pdf/pdf.component.ts +++ b/packages/abc/pdf/pdf.component.ts @@ -8,16 +8,16 @@ import { DestroyRef, ElementRef, EventEmitter, - Inject, Input, NgZone, OnChanges, OnDestroy, - Optional, Output, SimpleChange, ViewEncapsulation, - inject + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { fromEvent, timer, debounceTime, filter } from 'rxjs'; @@ -29,7 +29,7 @@ import { fromEvent, timer, debounceTime, filter } from 'rxjs'; // import type { PDFViewer } from 'pdfjs-dist/types/web/pdf_viewer'; import { AlainConfigService } from '@delon/util/config'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput, ZoneOutside } from '@delon/util/decorator'; +import { ZoneOutside } from '@delon/util/decorator'; import { LazyService } from '@delon/util/other'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzSkeletonComponent } from 'ng-zorro-antd/skeleton'; @@ -71,17 +71,15 @@ const BORDER_WIDTH = 9; imports: [NzSkeletonComponent] }) export class PdfComponent implements OnChanges, AfterViewInit, OnDestroy { - static ngAcceptInputType_pi: NumberInput; - static ngAcceptInputType_delay: NumberInput; - static ngAcceptInputType_showAllPages: BooleanInput; - static ngAcceptInputType_stickToPage: BooleanInput; - static ngAcceptInputType_originalSize: BooleanInput; - static ngAcceptInputType_fitToPage: BooleanInput; - static ngAcceptInputType_disableTextLayer: BooleanInput; - static ngAcceptInputType_removePageBorders: BooleanInput; + private readonly lazySrv = inject(LazyService); + private readonly platform = inject(Platform); + private readonly _el: HTMLElement = inject(ElementRef).nativeElement; + private readonly doc = inject(DOCUMENT); + private readonly cdr = inject(ChangeDetectorRef); + private readonly ngZone = inject(NgZone); + private readonly destroy$ = inject(DestroyRef); inited = false; - private destroy$ = inject(DestroyRef); private lib: string = ''; private _pdf?: PDFDocumentProxy | null; private loadingTask?: PDFDocumentLoadingTask; @@ -107,44 +105,43 @@ export class PdfComponent implements OnChanges, AfterViewInit, OnDestroy { this._src = dataOrBuffer; this.load(); } - @Input() - @InputNumber() + @Input({ transform: numberAttribute }) set pi(val: number) { this._pi = this.getValidPi(val); if (this.pageViewer) { this.pageViewer.scrollPageIntoView({ pageNumber: this._pi }); } } - @Input() @InputBoolean() set showAll(val: boolean) { + @Input({ transform: booleanAttribute }) set showAll(val: boolean) { this._showAll = val; this.resetDoc(); } - @Input() @InputBoolean() set renderText(val: boolean) { + @Input({ transform: booleanAttribute }) set renderText(val: boolean) { this._renderText = val; if (this.pageViewer) { this.resetDoc(); } } @Input() textLayerMode = PdfTextLayerMode.ENABLE; - @Input() @InputBoolean() showBorders = false; - @Input() @InputBoolean() stickToPage = false; - @Input() @InputBoolean() originalSize = true; - @Input() @InputBoolean() fitToPage = false; - @Input() @InputNumber() set zoom(val: number) { + @Input({ transform: booleanAttribute }) showBorders = false; + @Input({ transform: booleanAttribute }) stickToPage = false; + @Input({ transform: booleanAttribute }) originalSize = true; + @Input({ transform: booleanAttribute }) fitToPage = false; + @Input({ transform: numberAttribute }) set zoom(val: number) { if (val <= 0) return; this._zoom = val; } @Input() zoomScale: PdfZoomScale = 'page-width'; - @Input() @InputNumber() set rotation(val: number) { + @Input({ transform: numberAttribute }) set rotation(val: number) { if (val % 90 !== 0) { console.warn(`Invalid rotation angle, shoule be divisible by 90.`); return; } this._rotation = val; } - @Input() @InputBoolean() autoReSize = true; + @Input({ transform: booleanAttribute }) autoReSize = true; @Input() externalLinkTarget = PdfExternalLinkTarget.BLANK; - @Input() @InputNumber() delay?: number; + @Input({ transform: numberAttribute }) delay?: number; @Output() readonly change = new EventEmitter(); get loading(): boolean { @@ -180,18 +177,10 @@ export class PdfComponent implements OnChanges, AfterViewInit, OnDestroy { } private get el(): HTMLElement { - return this._el.nativeElement.querySelector('.pdf-container') as HTMLElement; - } - - constructor( - private ngZone: NgZone, - configSrv: AlainConfigService, - private lazySrv: LazyService, - private platform: Platform, - private _el: ElementRef, - @Optional() @Inject(DOCUMENT) private doc: NzSafeAny, - private cdr: ChangeDetectorRef - ) { + return this._el.querySelector('.pdf-container') as HTMLElement; + } + + constructor(configSrv: AlainConfigService) { const cog = configSrv.merge('pdf', PDF_DEFULAT_CONFIG)!; Object.assign(this, cog); diff --git a/packages/abc/qr/qr.component.ts b/packages/abc/qr/qr.component.ts index cb1e0346f..7ee19267e 100644 --- a/packages/abc/qr/qr.component.ts +++ b/packages/abc/qr/qr.component.ts @@ -9,12 +9,12 @@ import { OnChanges, OnDestroy, Output, - ViewEncapsulation + ViewEncapsulation, + numberAttribute } from '@angular/core'; import { Subscription, filter } from 'rxjs'; import { AlainConfigService, AlainQRConfig } from '@delon/util/config'; -import { InputNumber, NumberInput } from '@delon/util/decorator'; import { LazyService } from '@delon/util/other'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -40,10 +40,6 @@ import { QROptions } from './qr.types'; encapsulation: ViewEncapsulation.None }) export class QRComponent implements OnChanges, AfterViewInit, OnDestroy { - static ngAcceptInputType_padding: NumberInput; - static ngAcceptInputType_size: NumberInput; - static ngAcceptInputType_delay: NumberInput; - private lazy$?: Subscription; private qr: NzSafeAny; private cog: AlainQRConfig; @@ -58,10 +54,10 @@ export class QRComponent implements OnChanges, AfterViewInit, OnDestroy { @Input() foregroundAlpha?: number; @Input() level?: string; @Input() mime?: string; - @Input() @InputNumber(null) padding?: number; - @Input() @InputNumber() size?: number; + @Input({ transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) padding?: number; + @Input({ transform: numberAttribute }) size?: number; @Input() value: string | (() => string) = ''; - @Input() @InputNumber() delay?: number; + @Input({ transform: numberAttribute }) delay?: number; @Output() readonly change = new EventEmitter(); constructor( diff --git a/packages/abc/quick-menu/quick-menu.component.ts b/packages/abc/quick-menu/quick-menu.component.ts index 729c93314..5c3770b3f 100644 --- a/packages/abc/quick-menu/quick-menu.component.ts +++ b/packages/abc/quick-menu/quick-menu.component.ts @@ -11,10 +11,12 @@ import { Output, Renderer2, TemplateRef, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; import { NzIconDirective } from 'ng-zorro-antd/icon'; @@ -33,23 +35,18 @@ import { NzIconDirective } from 'ng-zorro-antd/icon'; imports: [NgStyle, NzIconDirective, NzStringTemplateOutletDirective] }) export class QuickMenuComponent implements OnInit, OnChanges { - static ngAcceptInputType_top: NumberInput; - static ngAcceptInputType_width: NumberInput; - static ngAcceptInputType_expand: BooleanInput; + private readonly cdr = inject(ChangeDetectorRef); + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly render = inject(Renderer2); - constructor( - private cdr: ChangeDetectorRef, - private el: ElementRef, - private render: Renderer2 - ) {} ctrlStyle: { [key: string]: string | undefined } = {}; @Input() icon: string | TemplateRef = 'question-circle'; - @Input() @InputNumber() top = 120; - @Input() @InputNumber() width = 200; + @Input({ transform: numberAttribute }) top = 120; + @Input({ transform: numberAttribute }) width = 200; @Input() bgColor?: string; @Input() borderColor?: string; - @Input() @InputBoolean() expand: boolean = false; + @Input({ transform: booleanAttribute }) expand: boolean = false; @Output() readonly expandChange = new EventEmitter(); private show = false; @@ -78,7 +75,7 @@ export class QuickMenuComponent implements OnInit, OnChanges { if (this.borderColor) { res.push(`border-color:${this.borderColor}`); } - this.render.setAttribute(this.el.nativeElement, 'style', res.join(';')); + this.render.setAttribute(this.el, 'style', res.join(';')); this.cdr.detectChanges(); } diff --git a/packages/abc/result/result.component.ts b/packages/abc/result/result.component.ts index ed492562d..c6b975f12 100644 --- a/packages/abc/result/result.component.ts +++ b/packages/abc/result/result.component.ts @@ -3,11 +3,12 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + DestroyRef, Input, OnInit, - Optional, TemplateRef, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -29,7 +30,10 @@ import { NzIconDirective } from 'ng-zorro-antd/icon'; imports: [NzIconDirective, NzStringTemplateOutletDirective] }) export class ResultComponent implements OnInit { - private dir$ = this.directionality.change?.pipe(takeUntilDestroyed()); + private readonly cdr = inject(ChangeDetectorRef); + private readonly directionality = inject(Directionality, { optional: true }); + private readonly destroy$ = inject(DestroyRef); + _type = ''; _icon = ''; @Input() @@ -51,16 +55,11 @@ export class ResultComponent implements OnInit { @Input() title?: string | TemplateRef; @Input() description?: string | TemplateRef; @Input() extra?: string | TemplateRef; - dir: Direction = 'ltr'; - - constructor( - @Optional() private directionality: Directionality, - private cdr: ChangeDetectorRef - ) {} + dir?: Direction = 'ltr'; ngOnInit(): void { - this.dir = this.directionality.value; - this.dir$.subscribe((direction: Direction) => { + this.dir = this.directionality?.value; + this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { this.dir = direction; this.cdr.detectChanges(); }); diff --git a/packages/abc/reuse-tab/provide.ts b/packages/abc/reuse-tab/provide.ts index a41dde6a7..237677c1d 100644 --- a/packages/abc/reuse-tab/provide.ts +++ b/packages/abc/reuse-tab/provide.ts @@ -47,6 +47,7 @@ export function provideReuseTabConfig(options?: { store?: ReuseTabFeature; }): EnvironmentProviders { const providers: Provider[] = [ + ReuseTabService, { provide: REUSE_TAB_STORAGE_KEY, useValue: options?.storeKey ?? '_reuse-tab-state' diff --git a/packages/abc/reuse-tab/reuse-tab-context-menu.component.ts b/packages/abc/reuse-tab/reuse-tab-context-menu.component.ts index 6dd0478ee..ee32ef4a3 100644 --- a/packages/abc/reuse-tab/reuse-tab-context-menu.component.ts +++ b/packages/abc/reuse-tab/reuse-tab-context-menu.component.ts @@ -5,7 +5,8 @@ import { Input, OnInit, Output, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { DelonLocaleService } from '@delon/theme'; @@ -33,6 +34,8 @@ import { imports: [NzMenuDirective, NzMenuItemComponent] }) export class ReuseTabContextMenuComponent implements OnInit { + private readonly i18nSrv = inject(DelonLocaleService); + private _i18n!: ReuseContextI18n; @Input() set i18n(value: ReuseContextI18n) { @@ -53,8 +56,6 @@ export class ReuseTabContextMenuComponent implements OnInit { return this.event.ctrlKey; } - constructor(private i18nSrv: DelonLocaleService) {} - private notify(type: CloseType): void { this.close.next({ type, diff --git a/packages/abc/reuse-tab/reuse-tab-context.component.ts b/packages/abc/reuse-tab/reuse-tab-context.component.ts index f5e276267..1eb187256 100644 --- a/packages/abc/reuse-tab/reuse-tab-context.component.ts +++ b/packages/abc/reuse-tab/reuse-tab-context.component.ts @@ -1,5 +1,5 @@ -import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core'; -import { Subscription } from 'rxjs'; +import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ReuseTabContextService } from './reuse-tab-context.service'; import { ReuseContextCloseEvent, ReuseContextI18n } from './reuse-tab.interfaces'; @@ -9,8 +9,8 @@ import { ReuseContextCloseEvent, ReuseContextI18n } from './reuse-tab.interfaces template: ``, standalone: true }) -export class ReuseTabContextComponent implements OnDestroy { - private sub$: Subscription = new Subscription(); +export class ReuseTabContextComponent { + private readonly srv = inject(ReuseTabContextService); @Input() set i18n(value: ReuseContextI18n | undefined) { @@ -19,12 +19,8 @@ export class ReuseTabContextComponent implements OnDestroy { @Output() readonly change = new EventEmitter(); - constructor(private srv: ReuseTabContextService) { - this.sub$.add(srv.show.subscribe(context => this.srv.open(context))); - this.sub$.add(srv.close.subscribe(res => this.change.emit(res))); - } - - ngOnDestroy(): void { - this.sub$.unsubscribe(); + constructor() { + this.srv.show.pipe(takeUntilDestroyed()).subscribe(context => this.srv.open(context)); + this.srv.close.pipe(takeUntilDestroyed()).subscribe(res => this.change.emit(res)); } } diff --git a/packages/abc/reuse-tab/reuse-tab-context.directive.ts b/packages/abc/reuse-tab/reuse-tab-context.directive.ts index f44492ba7..37b770c4f 100644 --- a/packages/abc/reuse-tab/reuse-tab-context.directive.ts +++ b/packages/abc/reuse-tab/reuse-tab-context.directive.ts @@ -1,4 +1,4 @@ -import { Directive, Input } from '@angular/core'; +import { Directive, Input, inject } from '@angular/core'; import { ReuseTabContextService } from './reuse-tab-context.service'; import { ReuseCustomContextMenu, ReuseItem } from './reuse-tab.interfaces'; @@ -12,11 +12,11 @@ import { ReuseCustomContextMenu, ReuseItem } from './reuse-tab.interfaces'; standalone: true }) export class ReuseTabContextDirective { + private readonly srv = inject(ReuseTabContextService); + @Input('reuse-tab-context-menu') item!: ReuseItem; @Input() customContextMenu!: ReuseCustomContextMenu[]; - constructor(private srv: ReuseTabContextService) {} - _onContextMenu(event: MouseEvent): void { this.srv.show.next({ event, diff --git a/packages/abc/reuse-tab/reuse-tab-context.service.ts b/packages/abc/reuse-tab/reuse-tab-context.service.ts index 83ade6e6d..3e33dc959 100644 --- a/packages/abc/reuse-tab/reuse-tab-context.service.ts +++ b/packages/abc/reuse-tab/reuse-tab-context.service.ts @@ -1,6 +1,6 @@ import { ConnectionPositionPair, Overlay, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Subject, Subscription } from 'rxjs'; import { ReuseTabContextMenuComponent } from './reuse-tab-context-menu.component'; @@ -13,14 +13,14 @@ import { @Injectable() export class ReuseTabContextService { + private readonly overlay = inject(Overlay); + private ref: OverlayRef | null = null; i18n?: ReuseContextI18n; show: Subject = new Subject(); close: Subject = new Subject(); - constructor(private overlay: Overlay) {} - remove(): void { if (!this.ref) return; this.ref.detach(); diff --git a/packages/abc/reuse-tab/reuse-tab.component.ts b/packages/abc/reuse-tab/reuse-tab.component.ts index 6a7fc5b7d..7954f7a31 100644 --- a/packages/abc/reuse-tab/reuse-tab.component.ts +++ b/packages/abc/reuse-tab/reuse-tab.component.ts @@ -7,25 +7,24 @@ import { Component, DestroyRef, EventEmitter, - Inject, Input, OnChanges, OnInit, - Optional, Output, SimpleChange, SimpleChanges, TemplateRef, ViewChild, ViewEncapsulation, - inject + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router } from '@angular/router'; import { debounceTime, filter, of } from 'rxjs'; -import { AlainI18NService, ALAIN_I18N_TOKEN } from '@delon/theme'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; +import { ALAIN_I18N_TOKEN } from '@delon/theme'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzIconDirective } from 'ng-zorro-antd/icon'; import { NzTabComponent, NzTabSetComponent } from 'ng-zorro-antd/tabs'; @@ -47,7 +46,7 @@ import { ReuseTitle } from './reuse-tab.interfaces'; import { ReuseTabService } from './reuse-tab.service'; -import { ReuseTabStorageState, REUSE_TAB_STORAGE_KEY, REUSE_TAB_STORAGE_STATE } from './reuse-tab.state'; +import { REUSE_TAB_STORAGE_KEY, REUSE_TAB_STORAGE_STATE } from './reuse-tab.state'; @Component({ selector: 'reuse-tab, [reuse-tab]', @@ -76,33 +75,36 @@ import { ReuseTabStorageState, REUSE_TAB_STORAGE_KEY, REUSE_TAB_STORAGE_STATE } ] }) export class ReuseTabComponent implements OnInit, OnChanges { - static ngAcceptInputType_debug: BooleanInput; - static ngAcceptInputType_max: NumberInput; - static ngAcceptInputType_tabMaxWidth: NumberInput; - static ngAcceptInputType_allowClose: BooleanInput; - static ngAcceptInputType_keepingScroll: BooleanInput; - static ngAcceptInputType_disabled: BooleanInput; - static ngAcceptInputType_storageState: BooleanInput; + private readonly srv = inject(ReuseTabService, { optional: true })!; + private readonly cdr = inject(ChangeDetectorRef); + private readonly router = inject(Router); + private readonly route = inject(ActivatedRoute); + private readonly i18nSrv = inject(ALAIN_I18N_TOKEN, { optional: true }); + private readonly doc = inject(DOCUMENT); + private readonly platform = inject(Platform); + private readonly directionality = inject(Directionality, { optional: true }); + private readonly stateKey = inject(REUSE_TAB_STORAGE_KEY); + private readonly stateSrv = inject(REUSE_TAB_STORAGE_STATE); @ViewChild('tabset') private tabset!: NzTabSetComponent; private destroy$ = inject(DestroyRef); - private _keepingScrollContainer?: Element; + private _keepingScrollContainer?: Element | null; list: ReuseItem[] = []; item?: ReuseItem; pos = 0; - dir: Direction = 'ltr'; + dir?: Direction = 'ltr'; // #region fields @Input() mode: ReuseTabMatchMode = ReuseTabMatchMode.Menu; @Input() i18n?: ReuseContextI18n; - @Input() @InputBoolean() debug = false; - @Input() @InputNumber() max?: number; - @Input() @InputNumber() tabMaxWidth?: number; + @Input({ transform: booleanAttribute }) debug = false; + @Input({ transform: numberAttribute }) max?: number; + @Input({ transform: numberAttribute }) tabMaxWidth?: number; @Input() excludes?: RegExp[]; - @Input() @InputBoolean() allowClose = true; - @Input() @InputBoolean() keepingScroll = false; - @Input() @InputBoolean() storageState = false; + @Input({ transform: booleanAttribute }) allowClose = true; + @Input({ transform: booleanAttribute }) keepingScroll = false; + @Input({ transform: booleanAttribute }) storageState = false; @Input() set keepingScrollContainer(value: string | Element) { this._keepingScrollContainer = typeof value === 'string' ? this.doc.querySelector(value) : value; @@ -113,7 +115,7 @@ export class ReuseTabComponent implements OnInit, OnChanges { @Input() tabBarStyle: { [key: string]: string } | null = null; @Input() tabType: 'line' | 'card' = 'line'; @Input() routeParamMatchMode: ReuseTabRouteParamMatchMode = 'strict'; - @Input() @InputBoolean() disabled = false; + @Input({ transform: booleanAttribute }) disabled = false; @Input() titleRender?: TemplateRef<{ $implicit: ReuseItem }>; @Input() canClose?: ReuseCanClose; @Output() readonly change = new EventEmitter(); @@ -121,19 +123,6 @@ export class ReuseTabComponent implements OnInit, OnChanges { // #endregion - constructor( - private srv: ReuseTabService, - private cdr: ChangeDetectorRef, - private router: Router, - private route: ActivatedRoute, - @Optional() @Inject(ALAIN_I18N_TOKEN) private i18nSrv: AlainI18NService, - @Inject(DOCUMENT) private doc: NzSafeAny, - private platform: Platform, - @Optional() private directionality: Directionality, - @Optional() @Inject(REUSE_TAB_STORAGE_KEY) private stateKey: string, - @Optional() @Inject(REUSE_TAB_STORAGE_STATE) private stateSrv: ReuseTabStorageState - ) {} - private genTit(title: ReuseTitle): string { return title.i18n && this.i18nSrv ? this.i18nSrv.fanyi(title.i18n) : title.text!; } @@ -215,7 +204,7 @@ export class ReuseTabComponent implements OnInit, OnChanges { private saveState(): void { if (!this.srv.inited || !this.storageState) return; - this.stateSrv.update(this.stateKey, this.list); + this.stateSrv?.update(this.stateKey!, this.list); } // #region UI @@ -285,6 +274,7 @@ export class ReuseTabComponent implements OnInit, OnChanges { * */ activate(instance: NzSafeAny): void { + if (this.srv == null) return; this.srv.componentRef = { instance }; } @@ -312,13 +302,13 @@ export class ReuseTabComponent implements OnInit, OnChanges { // #endregion ngOnInit(): void { - this.dir = this.directionality.value; - this.directionality.change?.pipe(takeUntilDestroyed(this.destroy$)).subscribe((direction: Direction) => { + this.dir = this.directionality?.value; + this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { this.dir = direction; this.cdr.detectChanges(); }); - if (!this.platform.isBrowser) { + if (!this.platform.isBrowser || this.srv == null) { return; } @@ -337,7 +327,7 @@ export class ReuseTabComponent implements OnInit, OnChanges { this.genList(res); }); - this.i18nSrv.change + this.i18nSrv?.change .pipe( filter(() => this.srv.inited), takeUntilDestroyed(this.destroy$), @@ -349,7 +339,7 @@ export class ReuseTabComponent implements OnInit, OnChanges { } ngOnChanges(changes: { [P in keyof this]?: SimpleChange } & SimpleChanges): void { - if (!this.platform.isBrowser) { + if (!this.platform.isBrowser || this.srv == null) { return; } diff --git a/packages/abc/reuse-tab/reuse-tab.service.ts b/packages/abc/reuse-tab/reuse-tab.service.ts index 27d3a6a9e..28ddca363 100644 --- a/packages/abc/reuse-tab/reuse-tab.service.ts +++ b/packages/abc/reuse-tab/reuse-tab.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable, Injector, OnDestroy, Optional } from '@angular/core'; +import { Injectable, Injector, OnDestroy, inject } from '@angular/core'; import { ActivatedRoute, ActivatedRouteSnapshot, @@ -14,7 +14,7 @@ import { Menu, MenuService } from '@delon/theme'; import { ScrollService } from '@delon/util/browser'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; -import { REUSE_TAB_CACHED_MANAGER, ReuseTabCachedManager } from './reuse-tab.cache'; +import { REUSE_TAB_CACHED_MANAGER } from './reuse-tab.cache'; import { ReuseComponentRef, ReuseHookOnReuseInitType, @@ -25,10 +25,16 @@ import { ReuseTabRouteParamMatchMode, ReuseTitle } from './reuse-tab.interfaces'; -import { ReuseTabStorageState, REUSE_TAB_STORAGE_KEY, REUSE_TAB_STORAGE_STATE } from './reuse-tab.state'; +import { REUSE_TAB_STORAGE_KEY, REUSE_TAB_STORAGE_STATE } from './reuse-tab.state'; -@Injectable({ providedIn: 'root' }) +@Injectable() export class ReuseTabService implements OnDestroy { + private readonly injector = inject(Injector); + private readonly menuService = inject(MenuService); + private readonly cached = inject(REUSE_TAB_CACHED_MANAGER); + private readonly stateKey = inject(REUSE_TAB_STORAGE_KEY); + private readonly stateSrv = inject(REUSE_TAB_STORAGE_STATE); + private _inited = false; private _max = 10; private _keepingScroll = false; @@ -84,7 +90,7 @@ export class ReuseTabService implements OnDestroy { get keepingScroll(): boolean { return this._keepingScroll; } - keepingScrollContainer?: Element; + keepingScrollContainer?: Element | null; /** 获取已缓存的路由 */ get items(): ReuseTabCached[] { return this.cached.list; @@ -369,13 +375,7 @@ export class ReuseTabService implements OnDestroy { // #endregion - constructor( - private injector: Injector, - private menuService: MenuService, - @Optional() @Inject(REUSE_TAB_CACHED_MANAGER) private cached: ReuseTabCachedManager, - @Optional() @Inject(REUSE_TAB_STORAGE_KEY) private stateKey: string, - @Optional() @Inject(REUSE_TAB_STORAGE_STATE) private stateSrv: ReuseTabStorageState - ) { + constructor() { if (this.cached == null) { this.cached = { list: [], title: {}, closable: {} }; } diff --git a/packages/abc/reuse-tab/reuse-tab.strategy.ts b/packages/abc/reuse-tab/reuse-tab.strategy.ts index 4a1624b3a..9e8f738fa 100644 --- a/packages/abc/reuse-tab/reuse-tab.strategy.ts +++ b/packages/abc/reuse-tab/reuse-tab.strategy.ts @@ -1,3 +1,4 @@ +import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, RouteReuseStrategy } from '@angular/router'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -5,7 +6,7 @@ import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { ReuseTabService } from './reuse-tab.service'; export class ReuseTabStrategy implements RouteReuseStrategy { - constructor(private srv: ReuseTabService) {} + private readonly srv = inject(ReuseTabService); shouldDetach(route: ActivatedRouteSnapshot): boolean { return this.srv.shouldDetach(route); diff --git a/packages/abc/se/se-container.component.ts b/packages/abc/se/se-container.component.ts index 0f3783416..50b36ae0f 100644 --- a/packages/abc/se/se-container.component.ts +++ b/packages/abc/se/se-container.component.ts @@ -2,19 +2,19 @@ import { ChangeDetectionStrategy, Component, ElementRef, - Host, Input, OnInit, - Optional, Renderer2, TemplateRef, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import { BehaviorSubject, Observable, filter } from 'rxjs'; import type { REP_TYPE } from '@delon/theme'; import { AlainConfigService } from '@delon/util/config'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput, toNumber } from '@delon/util/decorator'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -33,23 +33,18 @@ import { SEErrorRefresh, SELayout } from './se.types'; standalone: true }) export class SETitleComponent implements OnInit { - private el: HTMLElement; - constructor( - @Host() - @Optional() - private parent: SEContainerComponent, - el: ElementRef, - private ren: Renderer2 - ) { - if (parent == null) { + private readonly parentComp = inject(SEContainerComponent, { host: true, optional: true }); + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly ren = inject(Renderer2); + constructor() { + if (this.parentComp == null) { throw new Error(`[se-title] must include 'se-container' component`); } - this.el = el.nativeElement; } private setClass(): void { const { el } = this; - const gutter = this.parent.gutter as number; + const gutter = this.parentComp!.gutter as number; this.ren.setStyle(el, 'padding-left', `${gutter / 2}px`); this.ren.setStyle(el, 'padding-right', `${gutter / 2}px`); } @@ -87,28 +82,20 @@ export class SETitleComponent implements OnInit { imports: [SETitleComponent, NzStringTemplateOutletDirective] }) export class SEContainerComponent { - static ngAcceptInputType_gutter: NumberInput; - static ngAcceptInputType_col: NumberInput; - static ngAcceptInputType_colInCon: NumberInput; - static ngAcceptInputType_labelWidth: NumberInput; - static ngAcceptInputType_firstVisual: BooleanInput; - static ngAcceptInputType_ingoreDirty: BooleanInput; - static ngAcceptInputType_line: BooleanInput; - static ngAcceptInputType_noColon: BooleanInput; - private errorNotify$ = new BehaviorSubject(null as NzSafeAny); - @Input('se-container') @InputNumber(null) colInCon?: REP_TYPE; - @Input() @InputNumber(null) col!: REP_TYPE; - @Input() @InputNumber(null) labelWidth!: number; - @Input() @InputBoolean() noColon = false; + @Input({ alias: 'se-container', transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) + colInCon?: REP_TYPE; + @Input({ transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) col!: REP_TYPE; + @Input({ transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) labelWidth!: number; + @Input({ transform: booleanAttribute }) noColon = false; @Input() title?: string | TemplateRef | null; - @Input() + @Input({ transform: numberAttribute }) get gutter(): number { return this.nzLayout === 'horizontal' ? this._gutter : 0; } set gutter(value: number) { - this._gutter = toNumber(value); + this._gutter = value; } private _gutter!: number; @@ -125,9 +112,9 @@ export class SEContainerComponent { private _nzLayout!: SELayout; @Input() size!: 'default' | 'compact'; - @Input() @InputBoolean() firstVisual!: boolean; - @Input() @InputBoolean() ingoreDirty!: boolean; - @Input() @InputBoolean() line = false; + @Input({ transform: booleanAttribute }) firstVisual!: boolean; + @Input({ transform: booleanAttribute }) ingoreDirty!: boolean; + @Input({ transform: booleanAttribute }) line = false; @Input() set errors(val: SEErrorRefresh[]) { this.setErrors(val); diff --git a/packages/abc/se/se.component.ts b/packages/abc/se/se.component.ts index 40567f1f4..ed9af1908 100644 --- a/packages/abc/se/se.component.ts +++ b/packages/abc/se/se.component.ts @@ -9,15 +9,15 @@ import { ContentChild, DestroyRef, ElementRef, - Host, Input, OnChanges, - Optional, Renderer2, TemplateRef, ViewChild, ViewEncapsulation, - inject + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormControlName, NgModel, RequiredValidator, Validator, Validators } from '@angular/forms'; @@ -25,7 +25,6 @@ import { filter } from 'rxjs'; import { ResponsiveService } from '@delon/theme'; import { isEmpty } from '@delon/util/browser'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import { helpMotion } from 'ng-zorro-antd/core/animation'; import { NzFormStatusService } from 'ng-zorro-antd/core/form'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; @@ -59,15 +58,14 @@ let nextUniqueId = 0; imports: [NgClass, NzStringTemplateOutletDirective, NzTooltipDirective, NzIconDirective, CdkObserveContent] }) export class SEComponent implements OnChanges, AfterContentInit, AfterViewInit { - static ngAcceptInputType_col: NumberInput; - static ngAcceptInputType_required: BooleanInput; - static ngAcceptInputType_line: BooleanInput; - static ngAcceptInputType_labelWidth: NumberInput; - static ngAcceptInputType_noColon: BooleanInput; - static ngAcceptInputType_hideLabel: BooleanInput; - - private el: HTMLElement; - private destroy$ = inject(DestroyRef); + private readonly parentComp = inject(SEContainerComponent, { host: true, optional: true })!; + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly rep = inject(ResponsiveService); + private readonly ren = inject(Renderer2); + private readonly cdr = inject(ChangeDetectorRef); + private readonly statusSrv = inject(NzFormStatusService); + private readonly destroy$ = inject(DestroyRef); + @ContentChild(NgModel, { static: true }) private readonly ngModel?: NgModel; @ContentChild(FormControlName, { static: true }) private readonly formControlName?: FormControlName; @@ -93,13 +91,13 @@ export class SEComponent implements OnChanges, AfterContentInit, AfterViewInit { } @Input() extra?: string | TemplateRef | null; @Input() label?: string | TemplateRef | null; - @Input() @InputNumber(null) col?: number | null; - @Input() @InputBoolean() required = false; + @Input({ transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) col?: number | null; + @Input({ transform: booleanAttribute }) required = false; @Input() controlClass?: string | null = ''; - @Input() @InputBoolean(null) line?: boolean | null; - @Input() @InputNumber(null) labelWidth?: number | null; - @Input() @InputBoolean(null) noColon?: boolean | null; - @Input() @InputBoolean() hideLabel = false; + @Input({ transform: (v: unknown) => (v == null ? null : booleanAttribute(v)) }) line?: boolean | null; + @Input({ transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) labelWidth?: number | null; + @Input({ transform: (v: unknown) => (v == null ? null : booleanAttribute(v)) }) noColon?: boolean | null; + @Input({ transform: booleanAttribute }) hideLabel = false; @Input() set id(value: string) { @@ -113,7 +111,7 @@ export class SEComponent implements OnChanges, AfterContentInit, AfterViewInit { // #endregion get paddingValue(): number { - return (this.parent.gutter as number) / 2; + return (this.parentComp.gutter as number) / 2; } get showErr(): boolean { @@ -121,28 +119,20 @@ export class SEComponent implements OnChanges, AfterContentInit, AfterViewInit { } get compact(): boolean { - return this.parent.size === 'compact'; + return this.parentComp.size === 'compact'; } private get ngControl(): NgModel | FormControlName | null | undefined { return this.ngModel || this.formControlName; } - constructor( - el: ElementRef, - @Optional() @Host() private parent: SEContainerComponent, - private statusSrv: NzFormStatusService, - private rep: ResponsiveService, - private ren: Renderer2, - private cdr: ChangeDetectorRef - ) { - if (parent == null) { + constructor() { + if (this.parentComp == null) { throw new Error(`[se] must include 'se-container' component`); } - this.el = el.nativeElement; - parent.errorNotify + this.parentComp.errorNotify .pipe( - takeUntilDestroyed(this.destroy$), + takeUntilDestroyed(), filter(w => this.inited && this.ngControl != null && this.ngControl.name === w.name) ) .subscribe(item => { @@ -152,7 +142,8 @@ export class SEComponent implements OnChanges, AfterContentInit, AfterViewInit { } private setClass(): this { - const { el, ren, clsMap, col, parent, cdr, line, labelWidth, rep, noColon } = this; + const { el, ren, clsMap, col, cdr, line, labelWidth, rep, noColon } = this; + const parent = this.parentComp!; this._noColon = noColon != null ? noColon : parent.noColon; this._labelWidth = parent.nzLayout === 'horizontal' ? (labelWidth != null ? labelWidth : parent.labelWidth) : null; clsMap.forEach(cls => ren.removeClass(el, cls)); @@ -203,7 +194,7 @@ export class SEComponent implements OnChanges, AfterContentInit, AfterViewInit { return; } this.invalid = - !this.onceFlag && invalid && this.parent.ingoreDirty === false && !this.ngControl?.dirty ? false : invalid; + !this.onceFlag && invalid && this.parentComp.ingoreDirty === false && !this.ngControl?.dirty ? false : invalid; const errors = this.ngControl?.errors; if (errors != null && Object.keys(errors).length > 0) { const key = Object.keys(errors)[0] || ''; @@ -231,7 +222,7 @@ export class SEComponent implements OnChanges, AfterContentInit, AfterViewInit { } ngOnChanges(): void { - this.onceFlag = this.parent.firstVisual; + this.onceFlag = this.parentComp.firstVisual; if (this.inited) { this.setClass().bindModel(); } diff --git a/packages/abc/sg/sg-container.component.ts b/packages/abc/sg/sg-container.component.ts index 05c0af2d1..8877a24ef 100644 --- a/packages/abc/sg/sg-container.component.ts +++ b/packages/abc/sg/sg-container.component.ts @@ -1,8 +1,7 @@ -import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation, numberAttribute } from '@angular/core'; import type { REP_TYPE } from '@delon/theme'; import { AlainConfigService } from '@delon/util/config'; -import { InputNumber, NumberInput } from '@delon/util/decorator'; @Component({ selector: 'sg-container, [sg-container]', @@ -20,13 +19,10 @@ import { InputNumber, NumberInput } from '@delon/util/decorator'; standalone: true }) export class SGContainerComponent { - static ngAcceptInputType_gutter: NumberInput; - static ngAcceptInputType_colInCon: NumberInput; - static ngAcceptInputType_col: NumberInput; - - @Input() @InputNumber() gutter!: number; - @Input('sg-container') @InputNumber(null) colInCon?: REP_TYPE; - @Input() @InputNumber(null) col!: REP_TYPE; + @Input({ transform: numberAttribute }) gutter!: number; + @Input({ alias: 'sg-container', transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) + colInCon?: REP_TYPE; + @Input({ transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) col!: REP_TYPE; get marginValue(): number { return -(this.gutter / 2); diff --git a/packages/abc/sg/sg.component.ts b/packages/abc/sg/sg.component.ts index 260961595..75182e29f 100644 --- a/packages/abc/sg/sg.component.ts +++ b/packages/abc/sg/sg.component.ts @@ -3,16 +3,15 @@ import { ChangeDetectionStrategy, Component, ElementRef, - Host, Input, OnChanges, - Optional, Renderer2, - ViewEncapsulation + ViewEncapsulation, + inject, + numberAttribute } from '@angular/core'; import { ResponsiveService } from '@delon/theme'; -import { InputNumber, NumberInput } from '@delon/util/decorator'; import { SGContainerComponent } from './sg-container.component'; @@ -32,32 +31,29 @@ const prefixCls = `sg`; standalone: true }) export class SGComponent implements OnChanges, AfterViewInit { - static ngAcceptInputType_col: NumberInput; + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly ren = inject(Renderer2); + private readonly rep = inject(ResponsiveService); + private readonly parentComp = inject(SGContainerComponent, { host: true, optional: true })!; - private el: HTMLElement; private clsMap: string[] = []; private inited = false; - @Input() @InputNumber(null) col: number | null = null; + @Input({ transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) col: number | null = null; get paddingValue(): number { - return this.parent.gutter / 2; + return this.parentComp.gutter / 2; } - constructor( - el: ElementRef, - private ren: Renderer2, - @Optional() @Host() private parent: SGContainerComponent, - private rep: ResponsiveService - ) { - if (parent == null) { + constructor() { + if (this.parentComp == null) { throw new Error(`[sg] must include 'sg-container' component`); } - this.el = el.nativeElement; } private setClass(): this { - const { el, ren, clsMap, col, parent } = this; + const { el, ren, clsMap, col } = this; + const parent = this.parentComp; clsMap.forEach(cls => ren.removeClass(el, cls)); clsMap.length = 0; const parentCol = parent.colInCon || parent.col; diff --git a/packages/abc/st/st-export.ts b/packages/abc/st/st-export.ts index d8ebf8d37..b1daca433 100644 --- a/packages/abc/st/st-export.ts +++ b/packages/abc/st/st-export.ts @@ -1,4 +1,4 @@ -import { Injectable, Optional } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { XlsxExportResult, XlsxService } from '@delon/abc/xlsx'; import { deepGet } from '@delon/util/other'; @@ -9,7 +9,7 @@ import { _STColumn } from './st.types'; @Injectable() export class STExport { - constructor(@Optional() private xlsxSrv: XlsxService) {} + private readonly xlsxSrv = inject(XlsxService, { optional: true }); private _stGet(item: NzSafeAny, col: STColumn, index: number, colIndex: number): NzSafeAny { const ret: { [key: string]: NzSafeAny } = { t: 's', v: '' }; @@ -59,7 +59,7 @@ export class STExport { if (invalidFn(col)) continue; if (!wpx && col._width != null) wpx = true; ++validColCount; - const columnName = this.xlsxSrv.numberToSchema(validColCount); + const columnName = this.xlsxSrv!.numberToSchema(validColCount); sheet[`${columnName}1`] = { t: 's', v: typeof col.title === 'object' ? col.title.text : col.title @@ -74,13 +74,20 @@ export class STExport { } if (validColCount > 0 && dataLen > 0) { - sheet['!ref'] = `A1:${this.xlsxSrv.numberToSchema(validColCount)}${dataLen + 1}`; + sheet['!ref'] = `A1:${this.xlsxSrv!.numberToSchema(validColCount)}${dataLen + 1}`; } return sheets; } async export(opt: STExportOptions): Promise { + if (this.xlsxSrv == null) { + if (typeof ngDevMode === 'undefined' || ngDevMode) { + console.warn(`XlsxService service not found`); + } + return Promise.reject(); + } + const sheets = this.genSheet(opt); return this.xlsxSrv.export({ sheets, diff --git a/packages/abc/st/st-filter.component.ts b/packages/abc/st/st-filter.component.ts index 41ac253d7..f8b4ddb7e 100644 --- a/packages/abc/st/st-filter.component.ts +++ b/packages/abc/st/st-filter.component.ts @@ -5,7 +5,8 @@ import { EventEmitter, Input, Output, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { LocaleData } from '@delon/theme'; @@ -134,6 +135,8 @@ import { _STColumn } from './st.types'; encapsulation: ViewEncapsulation.None }) export class STFilterComponent { + private readonly cdr = inject(ChangeDetectorRef); + visible = false; @Input() col!: _STColumn; @Input() locale: LocaleData = {}; @@ -144,8 +147,6 @@ export class STFilterComponent { return this.f.icon as STIcon; } - constructor(private cdr: ChangeDetectorRef) {} - stopPropagation($event: MouseEvent): void { $event.stopPropagation(); } diff --git a/packages/abc/st/st-row.directive.ts b/packages/abc/st/st-row.directive.ts index a5801b190..ca44ad6cd 100644 --- a/packages/abc/st/st-row.directive.ts +++ b/packages/abc/st/st-row.directive.ts @@ -1,4 +1,4 @@ -import { Directive, Host, Injectable, Input, OnInit, TemplateRef } from '@angular/core'; +import { Directive, Injectable, Input, OnInit, TemplateRef, inject } from '@angular/core'; @Injectable() export class STRowSource { @@ -20,15 +20,12 @@ export class STRowSource { @Directive({ selector: '[st-row]' }) export class STRowDirective implements OnInit { + private readonly source = inject(STRowSource, { host: true }); + private readonly ref = inject(TemplateRef); @Input('st-row') id!: string; @Input() type?: 'title'; - constructor( - private ref: TemplateRef, - @Host() private source: STRowSource - ) {} - ngOnInit(): void { this.source.add(this.type, this.id, this.ref); } diff --git a/packages/abc/st/st-widget-host.directive.ts b/packages/abc/st/st-widget-host.directive.ts index c8775ef8f..00d95b479 100644 --- a/packages/abc/st/st-widget-host.directive.ts +++ b/packages/abc/st/st-widget-host.directive.ts @@ -1,4 +1,4 @@ -import { Directive, Input, OnInit, ViewContainerRef } from '@angular/core'; +import { Directive, Input, OnInit, ViewContainerRef, inject } from '@angular/core'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -7,14 +7,12 @@ import { STColumn, STData } from './st.interfaces'; @Directive({ selector: '[st-widget-host]' }) export class STWidgetHostDirective implements OnInit { + private readonly stWidgetRegistry = inject(STWidgetRegistry); + private readonly viewContainerRef = inject(ViewContainerRef); + @Input() record!: STData; @Input() column!: STColumn; - constructor( - private stWidgetRegistry: STWidgetRegistry, - private viewContainerRef: ViewContainerRef - ) {} - ngOnInit(): void { const widget = this.column.widget!; const componentType = this.stWidgetRegistry.get(widget.type); diff --git a/packages/abc/st/st.component.ts b/packages/abc/st/st.component.ts index 3959073fc..9dbc73c27 100644 --- a/packages/abc/st/st.component.ts +++ b/packages/abc/st/st.component.ts @@ -2,18 +2,17 @@ import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { DecimalPipe, DOCUMENT } from '@angular/common'; import { AfterViewInit, + booleanAttribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, ElementRef, EventEmitter, - Host, inject, - Inject, Input, + numberAttribute, OnChanges, - Optional, Output, SimpleChange, SimpleChanges, @@ -27,7 +26,6 @@ import { Router } from '@angular/router'; import { isObservable, Observable, of, filter, catchError, map, finalize, throwError, lastValueFrom } from 'rxjs'; import { - AlainI18NService, ALAIN_I18N_TOKEN, DatePipe, DelonLocaleService, @@ -37,7 +35,6 @@ import { YNPipe } from '@delon/theme'; import { AlainConfigService, AlainSTConfig } from '@delon/util/config'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput, toBoolean } from '@delon/util/decorator'; import { deepCopy, deepMergeKey } from '@delon/util/other'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzContextMenuService, NzDropdownMenuComponent } from 'ng-zorro-antd/dropdown'; @@ -49,7 +46,7 @@ import { STDataSource, STDataSourceOptions, STDataSourceResult } from './st-data import { STExport } from './st-export'; import { STRowSource } from './st-row.directive'; import { ST_DEFAULT_CONFIG } from './st.config'; -import { +import type { STChange, STChangeType, STClickRowClassName, @@ -76,7 +73,7 @@ import { STStatisticalResults, STWidthMode } from './st.interfaces'; -import { _STColumn, _STDataValue, _STHeader, _STTdNotify, _STTdNotifyType } from './st.types'; +import type { _STColumn, _STDataValue, _STHeader, _STTdNotify, _STTdNotifyType } from './st.types'; @Component({ selector: 'st', @@ -97,21 +94,17 @@ import { _STColumn, _STDataValue, _STHeader, _STTdNotify, _STTdNotifyType } from encapsulation: ViewEncapsulation.None }) export class STComponent implements AfterViewInit, OnChanges { - static ngAcceptInputType_ps: NumberInput; - static ngAcceptInputType_pi: NumberInput; - static ngAcceptInputType_total: NumberInput; - static ngAcceptInputType_loadingDelay: NumberInput; - static ngAcceptInputType_bordered: BooleanInput; - static ngAcceptInputType_expandRowByClick: BooleanInput; - static ngAcceptInputType_expandAccordion: BooleanInput; - static ngAcceptInputType_responsive: BooleanInput; - static ngAcceptInputType_responsiveHideHeaderFooter: BooleanInput; - static ngAcceptInputType_virtualScroll: BooleanInput; - static ngAcceptInputType_virtualItemSize: NumberInput; - static ngAcceptInputType_virtualMaxBufferPx: NumberInput; - static ngAcceptInputType_virtualMinBufferPx: NumberInput; - - private destroy$ = inject(DestroyRef); + private readonly i18nSrv = inject(ALAIN_I18N_TOKEN, { optional: true }); + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly cdr = inject(ChangeDetectorRef); + private readonly doc = inject(DOCUMENT); + private readonly exportSrv = inject(STExport); + private readonly columnSource = inject(STColumnSource); + private readonly dataSource = inject(STDataSource); + private readonly delonI18n = inject(DelonLocaleService); + private readonly cms = inject(NzContextMenuService); + private readonly destroy$ = inject(DestroyRef); + private totalTpl = ``; private inied = false; cog!: AlainSTConfig; @@ -167,13 +160,13 @@ export class STComponent implements AfterViewInit, OnChanges { @Input() data?: string | STData[] | Observable; @Input() columns?: STColumn[] | null; @Input() contextmenu?: STContextmenuFn | null; - @Input() @InputNumber() ps = 10; - @Input() @InputNumber() pi = 1; - @Input() @InputNumber() total = 0; + @Input({ transform: (v: unknown) => numberAttribute(v, 10) }) ps = 10; + @Input({ transform: (v: unknown) => numberAttribute(v, 1) }) pi = 1; + @Input({ transform: (v: unknown) => numberAttribute(v, 0) }) total = 0; @Input() loading: boolean | null = null; - @Input() @InputNumber() loadingDelay = 0; + @Input({ transform: numberAttribute }) loadingDelay = 0; @Input() loadingIndicator: TemplateRef | null = null; - @Input() @InputBoolean() bordered = false; + @Input({ transform: booleanAttribute }) bordered = false; @Input() size!: 'small' | 'middle' | 'default'; @Input() scroll: { x?: string | null; y?: string | null } = { x: null, y: null }; @Input() singleSort?: STSingleSort | null; @@ -184,7 +177,7 @@ export class STComponent implements AfterViewInit, OnChanges { } set multiSort(value: NzSafeAny) { if ( - (typeof value === 'boolean' && !toBoolean(value)) || + (typeof value === 'boolean' && !booleanAttribute(value)) || (typeof value === 'object' && Object.keys(value).length === 0) ) { this._multiSort = undefined; @@ -211,25 +204,25 @@ export class STComponent implements AfterViewInit, OnChanges { private _resizable?: STResizable; @Input() set resizable(val: STResizable | boolean | string) { - this._resizable = typeof val === 'object' ? val : { disabled: !toBoolean(val) }; + this._resizable = typeof val === 'object' ? val : { disabled: !booleanAttribute(val) }; } @Input() header?: string | TemplateRef | null; - @Input() @InputBoolean() showHeader = true; + @Input({ transform: booleanAttribute }) showHeader = true; @Input() footer?: string | TemplateRef | null; @Input() bodyHeader?: TemplateRef<{ $implicit: STStatisticalResults }> | null; @Input() body?: TemplateRef<{ $implicit: STStatisticalResults }> | null; - @Input() @InputBoolean() expandRowByClick = false; - @Input() @InputBoolean() expandAccordion = false; + @Input({ transform: booleanAttribute }) expandRowByClick = false; + @Input({ transform: booleanAttribute }) expandAccordion = false; @Input() expand: TemplateRef<{ $implicit: NzSafeAny; index: number }> | null = null; @Input() noResult?: string | TemplateRef | null; - @Input() @InputBoolean() responsive: boolean = true; - @Input() @InputBoolean() responsiveHideHeaderFooter?: boolean; + @Input({ transform: booleanAttribute }) responsive: boolean = true; + @Input({ transform: booleanAttribute }) responsiveHideHeaderFooter?: boolean; @Output() readonly error = new EventEmitter(); @Output() readonly change = new EventEmitter(); - @Input() @InputBoolean() virtualScroll = false; - @Input() @InputNumber() virtualItemSize = 54; - @Input() @InputNumber() virtualMaxBufferPx = 200; - @Input() @InputNumber() virtualMinBufferPx = 100; + @Input({ transform: booleanAttribute }) virtualScroll = false; + @Input({ transform: numberAttribute }) virtualItemSize = 54; + @Input({ transform: numberAttribute }) virtualMaxBufferPx = 200; + @Input({ transform: numberAttribute }) virtualMinBufferPx = 100; @Input() customRequest?: (options: STCustomRequestOptions) => Observable; @Input() virtualForTrackBy: TrackByFunction = index => index; @@ -251,18 +244,7 @@ export class STComponent implements AfterViewInit, OnChanges { return this.columns == null; } - constructor( - @Optional() @Inject(ALAIN_I18N_TOKEN) i18nSrv: AlainI18NService, - private cdr: ChangeDetectorRef, - private el: ElementRef, - private exportSrv: STExport, - @Inject(DOCUMENT) private doc: NzSafeAny, - private columnSource: STColumnSource, - private dataSource: STDataSource, - private delonI18n: DelonLocaleService, - configSrv: AlainConfigService, - private cms: NzContextMenuService - ) { + constructor(configSrv: AlainConfigService) { this.delonI18n.change.pipe(takeUntilDestroyed()).subscribe(() => { this.locale = this.delonI18n.getData('st'); if (this._columns.length > 0) { @@ -271,7 +253,7 @@ export class STComponent implements AfterViewInit, OnChanges { } }); - i18nSrv.change + this.i18nSrv?.change .pipe( takeUntilDestroyed(), filter(() => this._columns.length > 0) @@ -339,7 +321,7 @@ export class STComponent implements AfterViewInit, OnChanges { const { total } = this.page; if (typeof total === 'string' && total.length) { this.totalTpl = total; - } else if (toBoolean(total)) { + } else if (booleanAttribute(total)) { this.totalTpl = this.locale.total; } else { this.totalTpl = ''; @@ -466,7 +448,7 @@ export class STComponent implements AfterViewInit, OnChanges { private _toTop(enforce?: boolean): void { if (!(enforce == null ? this.page.toTop : enforce)) return; - const el = this.el.nativeElement as HTMLElement; + const el = this.el; el.scrollIntoView(); // fix header height this.doc.documentElement.scrollTop -= this.page.toTopOffset!; @@ -919,6 +901,11 @@ export class STComponent implements AfterViewInit, OnChanges { encapsulation: ViewEncapsulation.None }) export class STTdComponent { + private readonly stComp = inject(STComponent, { host: true }); + private readonly router = inject(Router); + private readonly modalHelper = inject(ModalHelper); + private readonly drawerHelper = inject(DrawerHelper); + @Input() c!: _STColumn; @Input() cIdx!: number; @Input() data!: STData[]; @@ -931,13 +918,6 @@ export class STTdComponent { return { pi, ps, total }; } - constructor( - @Host() private stComp: STComponent, - private router: Router, - private modalHelper: ModalHelper, - private drawerHelper: DrawerHelper - ) {} - private report(type: _STTdNotifyType): void { this.n.emit({ type, item: this.i, col: this.c }); } diff --git a/packages/abc/st/test/st.spec.ts b/packages/abc/st/test/st.spec.ts index db7e32fcd..94d1700ac 100644 --- a/packages/abc/st/test/st.spec.ts +++ b/packages/abc/st/test/st.spec.ts @@ -1438,7 +1438,7 @@ describe('abc: st', () => { describe('#export', () => { let exportSrv: STExport; beforeEach(() => { - exportSrv = comp['exportSrv'] = { + exportSrv = (comp as any)['exportSrv'] = { export: jasmine.createSpy('export') } as any; }); diff --git a/packages/abc/sv/sv-container.component.ts b/packages/abc/sv/sv-container.component.ts index 0e3624546..2f2bd065e 100644 --- a/packages/abc/sv/sv-container.component.ts +++ b/packages/abc/sv/sv-container.component.ts @@ -3,18 +3,18 @@ import { ChangeDetectionStrategy, Component, ElementRef, - Host, Input, OnInit, - Optional, Renderer2, TemplateRef, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import type { REP_TYPE } from '@delon/theme'; import { AlainConfigService } from '@delon/util/config'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; @Component({ @@ -30,19 +30,19 @@ import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; standalone: true }) export class SVTitleComponent implements OnInit { - constructor( - private el: ElementRef, - @Host() @Optional() private parent: SVContainerComponent, - private ren: Renderer2 - ) { - if (parent == null) { + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly parentComp = inject(SVContainerComponent, { host: true, optional: true }); + private readonly ren = inject(Renderer2); + + constructor() { + if (this.parentComp == null) { throw new Error(`[sv-title] must include 'sv-container' component`); } } private setClass(): void { - const gutter = this.parent.gutter; - const el = this.el.nativeElement; + const gutter = this.parentComp!.gutter; + const el = this.el; this.ren.setStyle(el, 'padding-left', `${gutter / 2}px`); this.ren.setStyle(el, 'padding-right', `${gutter / 2}px`); } @@ -81,26 +81,19 @@ export class SVTitleComponent implements OnInit { imports: [NgStyle, SVTitleComponent, NzStringTemplateOutletDirective] }) export class SVContainerComponent { - static ngAcceptInputType_gutter: NumberInput; - static ngAcceptInputType_labelWidth: NumberInput; - static ngAcceptInputType_col: NumberInput; - static ngAcceptInputType_colInCon: NumberInput; - static ngAcceptInputType_default: BooleanInput; - static ngAcceptInputType_noColon: BooleanInput; - static ngAcceptInputType_bordered: BooleanInput; - - @Input('sv-container') @InputNumber(null) colInCon?: REP_TYPE; + @Input({ alias: 'sv-container', transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) + colInCon?: REP_TYPE; @Input() title?: string | TemplateRef; @Input() size?: 'small' | 'large' | 'default'; /** 列表项间距,单位为 `px` */ - @Input() @InputNumber() gutter!: number; + @Input({ transform: numberAttribute }) gutter!: number; @Input() layout!: 'horizontal' | 'vertical'; - @Input() @InputNumber() labelWidth?: number; + @Input({ transform: numberAttribute }) labelWidth?: number; /** 指定信息最多分几列展示,最终一行几列由 col 配置结合响应式规则决定 */ - @Input() @InputNumber() col!: number; - @Input() @InputBoolean() default!: boolean; - @Input() @InputBoolean() noColon = false; - @Input() @InputBoolean() bordered = false; + @Input({ transform: numberAttribute }) col!: number; + @Input({ transform: booleanAttribute }) default!: boolean; + @Input({ transform: booleanAttribute }) noColon = false; + @Input({ transform: booleanAttribute }) bordered = false; get margin(): { [k: string]: number } { return this.bordered ? {} : { 'margin-left.px': -(this.gutter / 2), 'margin-right.px': -(this.gutter / 2) }; diff --git a/packages/abc/sv/sv.component.ts b/packages/abc/sv/sv.component.ts index 9e2dc2c44..3eb275d98 100644 --- a/packages/abc/sv/sv.component.ts +++ b/packages/abc/sv/sv.component.ts @@ -4,19 +4,19 @@ import { ChangeDetectionStrategy, Component, ElementRef, - Host, Input, OnChanges, - Optional, Renderer2, TemplateRef, ViewChild, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import { ResponsiveService } from '@delon/theme'; import { isEmpty } from '@delon/util/browser'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; import { NzIconDirective } from 'ng-zorro-antd/icon'; import { NzTooltipDirective } from 'ng-zorro-antd/tooltip'; @@ -40,13 +40,13 @@ const prefixCls = `sv`; imports: [NzStringTemplateOutletDirective, NzTooltipDirective, NzIconDirective, CdkObserveContent] }) export class SVComponent implements AfterViewInit, OnChanges { - static ngAcceptInputType_col: NumberInput; - static ngAcceptInputType_default: BooleanInput; - static ngAcceptInputType_noColon: BooleanInput; - static ngAcceptInputType_hideLabel: BooleanInput; + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly parentComp = inject(SVContainerComponent, { host: true, optional: true }); + private readonly rep = inject(ResponsiveService); + private readonly ren = inject(Renderer2); @ViewChild('conEl', { static: false }) - private conEl!: ElementRef; + private readonly conEl!: ElementRef; private clsMap: string[] = []; _noColon = false; @@ -57,45 +57,41 @@ export class SVComponent implements AfterViewInit, OnChanges { @Input() optionalHelpColor?: string; @Input() label?: string | TemplateRef | null; @Input() unit?: string | TemplateRef | null; - @Input() @InputNumber(null) col?: number | null; - @Input() @InputBoolean(null) default?: boolean | null; + @Input({ transform: (v: unknown) => (v == null ? null : numberAttribute(v)) }) col?: number | null; + @Input({ transform: (v: unknown) => (v == null ? null : booleanAttribute(v)) }) default?: boolean | null; @Input() type?: 'primary' | 'success' | 'danger' | 'warning'; - @Input() @InputBoolean(null) noColon?: boolean | null; - @Input() @InputBoolean() hideLabel = false; + @Input({ transform: (v: unknown) => (v == null ? null : booleanAttribute(v)) }) noColon?: boolean | null; + @Input({ transform: booleanAttribute }) hideLabel = false; // #endregion get paddingValue(): number | null { - if (this.parent.bordered) return null; - return this.parent.gutter / 2; + if (this.parentComp!.bordered) return null; + return this.parentComp!.gutter / 2; } get labelWidth(): number | null | undefined { - const { labelWidth, layout } = this.parent; + const { labelWidth, layout } = this.parentComp!; return layout === 'horizontal' ? labelWidth : null; } - constructor( - private el: ElementRef, - @Host() @Optional() public parent: SVContainerComponent, - private rep: ResponsiveService, - private ren: Renderer2 - ) { - if (parent == null) { + constructor() { + if (this.parentComp == null) { throw new Error(`[sv] must include 'sv-container' component`); } } private setClass(): void { - const { ren, col, clsMap, type, rep, noColon, parent } = this; - const el = this.el.nativeElement; + const { ren, col, clsMap, type, rep, noColon } = this; + const parent = this.parentComp!; + const el = this.el; this._noColon = parent.bordered ? true : noColon != null ? noColon : parent.noColon; clsMap.forEach(cls => ren.removeClass(el, cls)); clsMap.length = 0; const parentCol = parent.colInCon || parent.col; clsMap.push(...rep.genCls(col != null ? col : parentCol, parentCol)); clsMap.push(`${prefixCls}__item`); - if (this.parent.labelWidth) clsMap.push(`${prefixCls}__item-fixed`); + if (parent.labelWidth) clsMap.push(`${prefixCls}__item-fixed`); if (type) clsMap.push(`${prefixCls}__type-${type}`); clsMap.forEach(cls => ren.addClass(el, cls)); } @@ -112,7 +108,7 @@ export class SVComponent implements AfterViewInit, OnChanges { checkContent(): void { const { conEl } = this; const def = this.default; - if (!(def != null ? def : this.parent.default)) { + if (!(def != null ? def : this.parentComp?.default)) { return; } const el = conEl.nativeElement as HTMLElement; diff --git a/packages/abc/tag-select/tag-select.component.ts b/packages/abc/tag-select/tag-select.component.ts index cf9a1ea20..e952c7184 100644 --- a/packages/abc/tag-select/tag-select.component.ts +++ b/packages/abc/tag-select/tag-select.component.ts @@ -7,15 +7,14 @@ import { EventEmitter, Input, OnInit, - Optional, Output, ViewEncapsulation, + booleanAttribute, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { DelonLocaleService, LocaleData } from '@delon/theme'; -import { BooleanInput, InputBoolean } from '@delon/util/decorator'; import { NzIconDirective } from 'ng-zorro-antd/icon'; @Component({ @@ -36,27 +35,23 @@ import { NzIconDirective } from 'ng-zorro-antd/icon'; imports: [NzIconDirective] }) export class TagSelectComponent implements OnInit { - static ngAcceptInputType_expandable: BooleanInput; - - private destroy$ = inject(DestroyRef); + private readonly i18n = inject(DelonLocaleService); + private readonly directionality = inject(Directionality, { optional: true }); + private readonly cdr = inject(ChangeDetectorRef); + private readonly destroy$ = inject(DestroyRef); locale: LocaleData = {}; expand = false; - dir: Direction = 'ltr'; + dir?: Direction = 'ltr'; /** 是否启用 `展开与收进` */ - @Input() @InputBoolean() expandable = true; + @Input({ transform: booleanAttribute }) expandable = true; @Output() readonly change = new EventEmitter(); - constructor( - private i18n: DelonLocaleService, - @Optional() private directionality: Directionality, - private cdr: ChangeDetectorRef - ) {} - ngOnInit(): void { - this.dir = this.directionality.value; - this.directionality.change?.pipe(takeUntilDestroyed(this.destroy$)).subscribe((direction: Direction) => { + this.dir = this.directionality?.value; + this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { this.dir = direction; + this.cdr.detectChanges(); }); this.i18n.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(() => { this.locale = this.i18n.getData('tagSelect'); diff --git a/packages/abc/xlsx/xlsx.directive.ts b/packages/abc/xlsx/xlsx.directive.ts index 44babf260..abf0a8b7f 100644 --- a/packages/abc/xlsx/xlsx.directive.ts +++ b/packages/abc/xlsx/xlsx.directive.ts @@ -1,4 +1,4 @@ -import { Directive, Input } from '@angular/core'; +import { Directive, Input, inject } from '@angular/core'; import { XlsxService } from './xlsx.service'; import { XlsxExportOptions } from './xlsx.types'; @@ -12,9 +12,9 @@ import { XlsxExportOptions } from './xlsx.types'; standalone: true }) export class XlsxDirective { - @Input('xlsx') data!: XlsxExportOptions; + private readonly srv = inject(XlsxService); - constructor(private srv: XlsxService) {} + @Input('xlsx') data!: XlsxExportOptions; _click(): void { this.srv.export(this.data); diff --git a/packages/abc/xlsx/xlsx.service.ts b/packages/abc/xlsx/xlsx.service.ts index 0bf81b2d6..e6040577d 100644 --- a/packages/abc/xlsx/xlsx.service.ts +++ b/packages/abc/xlsx/xlsx.service.ts @@ -1,5 +1,5 @@ import { HttpClient } from '@angular/common/http'; -import { Injectable, NgZone } from '@angular/core'; +import { Injectable, NgZone, inject } from '@angular/core'; import isUtf8 from 'isutf8'; @@ -15,18 +15,18 @@ declare var cptable: NzSafeAny; @Injectable({ providedIn: 'root' }) export class XlsxService { - constructor( - private http: HttpClient, - private lazy: LazyService, - configSrv: AlainConfigService, - private ngZone: NgZone - ) { + private readonly http = inject(HttpClient); + private readonly lazy = inject(LazyService); + private readonly ngZone = inject(NgZone); + + private cog: AlainXlsxConfig; + + constructor(configSrv: AlainConfigService) { this.cog = configSrv.merge('xlsx', { url: 'https://cdn.jsdelivr.net/npm/xlsx/dist/xlsx.full.min.js', modules: [`https://cdn.jsdelivr.net/npm/xlsx/dist/cpexcel.js`] })!; } - private cog: AlainXlsxConfig; private init(): Promise { return typeof XLSX !== 'undefined' diff --git a/packages/abc/zip/zip.service.ts b/packages/abc/zip/zip.service.ts index 1e844b6b6..5dd03423c 100644 --- a/packages/abc/zip/zip.service.ts +++ b/packages/abc/zip/zip.service.ts @@ -1,5 +1,5 @@ import { HttpClient } from '@angular/common/http'; -import { Injectable, NgZone } from '@angular/core'; +import { Injectable, NgZone, inject } from '@angular/core'; import { saveAs } from 'file-saver'; import type jsZipType from 'jszip'; @@ -15,14 +15,13 @@ declare var JSZip: jsZipType; @Injectable({ providedIn: 'root' }) export class ZipService { + private readonly http = inject(HttpClient); + private readonly lazy = inject(LazyService); + private readonly ngZone = inject(NgZone); + private cog: AlainZipConfig; - constructor( - private http: HttpClient, - private lazy: LazyService, - configSrv: AlainConfigService, - private ngZone: NgZone - ) { + constructor(configSrv: AlainConfigService) { this.cog = configSrv.merge('zip', { url: 'https://cdn.jsdelivr.net/npm/jszip@3/dist/jszip.min.js', utils: [] diff --git a/packages/acl/src/acl-guard.ts b/packages/acl/src/acl-guard.ts index d178f942f..35970221b 100644 --- a/packages/acl/src/acl-guard.ts +++ b/packages/acl/src/acl-guard.ts @@ -7,11 +7,9 @@ import type { ACLCanType, ACLGuardData } from './acl.type'; @Injectable({ providedIn: 'root' }) export class ACLGuardService { - constructor( - private srv: ACLService, - private router: Router, - private injector: Injector - ) {} + private readonly srv = inject(ACLService); + private readonly router = inject(Router); + private readonly injector = inject(Injector); process(data?: ACLGuardData): Observable { data = { diff --git a/packages/acl/src/acl-if.directive.ts b/packages/acl/src/acl-if.directive.ts index 6b3e2753d..04db9f550 100644 --- a/packages/acl/src/acl-if.directive.ts +++ b/packages/acl/src/acl-if.directive.ts @@ -1,4 +1,5 @@ -import { Directive, EmbeddedViewRef, Input, OnDestroy, TemplateRef, ViewContainerRef } from '@angular/core'; +import { Directive, EmbeddedViewRef, Input, OnDestroy, TemplateRef, ViewContainerRef, inject } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { Subscription, filter } from 'rxjs'; import { ACLService } from './acl.service'; @@ -10,23 +11,25 @@ import { ACLCanType } from './acl.type'; standalone: true }) export class ACLIfDirective implements OnDestroy { + private readonly srv = inject(ACLService); + private readonly _viewContainer = inject(ViewContainerRef); static ngAcceptInputType_except: boolean | string | undefined | null; private _value!: ACLCanType; private _change$: Subscription; - private _thenTemplateRef: TemplateRef | null = null; + private _thenTemplateRef: TemplateRef | null = inject(TemplateRef); private _elseTemplateRef: TemplateRef | null = null; private _thenViewRef: EmbeddedViewRef | null = null; private _elseViewRef: EmbeddedViewRef | null = null; private _except = false; - constructor( - templateRef: TemplateRef, - private srv: ACLService, - private _viewContainer: ViewContainerRef - ) { - this._change$ = this.srv.change.pipe(filter(r => r != null)).subscribe(() => this._updateView()); - this._thenTemplateRef = templateRef; + constructor() { + this._change$ = this.srv.change + .pipe( + takeUntilDestroyed(), + filter(r => r != null) + ) + .subscribe(() => this._updateView()); } @Input() diff --git a/packages/acl/src/acl.directive.ts b/packages/acl/src/acl.directive.ts index 12d11d55b..c85a9900c 100644 --- a/packages/acl/src/acl.directive.ts +++ b/packages/acl/src/acl.directive.ts @@ -1,4 +1,5 @@ -import { Directive, ElementRef, Input, OnDestroy, Renderer2 } from '@angular/core'; +import { Directive, ElementRef, Input, OnDestroy, Renderer2, inject } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { Subscription, filter } from 'rxjs'; import { ACLService } from './acl.service'; @@ -10,6 +11,10 @@ import { ACLCanType } from './acl.type'; standalone: true }) export class ACLDirective implements OnDestroy { + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly renderer = inject(Renderer2); + private readonly srv = inject(ACLService); + private _value!: ACLCanType; private change$: Subscription; @@ -26,7 +31,7 @@ export class ACLDirective implements OnDestroy { private set(value: ACLCanType): void { this._value = value; const CLS = 'acl__hide'; - const el = this.el.nativeElement; + const el = this.el; if (this.srv.can(this._value)) { this.renderer.removeClass(el, CLS); } else { @@ -34,12 +39,13 @@ export class ACLDirective implements OnDestroy { } } - constructor( - private el: ElementRef, - private renderer: Renderer2, - protected srv: ACLService - ) { - this.change$ = this.srv.change.pipe(filter(r => r != null)).subscribe(() => this.set(this._value)); + constructor() { + this.change$ = this.srv.change + .pipe( + takeUntilDestroyed(), + filter(r => r != null) + ) + .subscribe(() => this.set(this._value)); } ngOnDestroy(): void { diff --git a/packages/auth/src/social/social.service.ts b/packages/auth/src/social/social.service.ts index 1683e4228..ffaa93d34 100644 --- a/packages/auth/src/social/social.service.ts +++ b/packages/auth/src/social/social.service.ts @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { DOCUMENT } from '@angular/common'; -import { Inject, Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { Router } from '@angular/router'; import { Observable, Observer } from 'rxjs'; -import { DA_SERVICE_TOKEN, ITokenModel, ITokenService } from '../token/interface'; +import { DA_SERVICE_TOKEN, ITokenModel } from '../token/interface'; const OPENTYPE = '_delonAuthSocialType'; const HREFCALLBACK = '_delonAuthSocialCallbackByHref'; @@ -13,16 +13,14 @@ export type SocialOpenType = 'href' | 'window'; @Injectable() export class SocialService implements OnDestroy { + private readonly tokenService = inject(DA_SERVICE_TOKEN); + private readonly doc = inject(DOCUMENT); + private readonly router = inject(Router); + private _win: Window | null = null; private _winTime: any; private observer!: Observer; - constructor( - @Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService, - @Inject(DOCUMENT) private doc: any, - private router: Router - ) {} - /** * 使用窗体打开授权页,返回值是 `Observable` 用于订阅授权后返回的结果 * diff --git a/packages/auth/src/store/cookie-storage.service.spec.ts b/packages/auth/src/store/cookie-storage.service.spec.ts index 840d8dd0f..71b1fbe64 100644 --- a/packages/auth/src/store/cookie-storage.service.spec.ts +++ b/packages/auth/src/store/cookie-storage.service.spec.ts @@ -1,4 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { TestBed } from '@angular/core/testing'; + import { CookieOptions, CookieService } from '@delon/util/browser'; import { CookieStorageStore } from './cookie-storage.service'; @@ -16,13 +18,22 @@ describe('auth: cookie-storage', () => { beforeEach(() => { data = {}; putSpy = jasmine.createSpy('put').and.callFake((key: string, value: string) => (data[key] = value)); - store = new CookieStorageStore({ - put: putSpy, - get: jasmine.createSpy('get').and.callFake((key: string) => data[key]), - remove: jasmine.createSpy('remove').and.callFake((key: string) => { - delete data[key]; - }) - } as unknown as CookieService); + TestBed.configureTestingModule({ + providers: [ + { + provide: CookieService, + useValue: { + put: putSpy, + get: jasmine.createSpy('get').and.callFake((key: string) => data[key]), + remove: jasmine.createSpy('remove').and.callFake((key: string) => { + delete data[key]; + }) + } + }, + CookieStorageStore + ] + }); + store = TestBed.inject(CookieStorageStore); }); it('should be never return null', () => { diff --git a/packages/auth/src/store/cookie-storage.service.ts b/packages/auth/src/store/cookie-storage.service.ts index dc2f920a4..842eea662 100644 --- a/packages/auth/src/store/cookie-storage.service.ts +++ b/packages/auth/src/store/cookie-storage.service.ts @@ -1,3 +1,5 @@ +import { inject } from '@angular/core'; + import { CookieService } from '@delon/util/browser'; import { IStore } from './interface'; @@ -11,7 +13,7 @@ import { ITokenModel } from '../token/interface'; * ``` */ export class CookieStorageStore implements IStore { - constructor(private srv: CookieService) {} + private readonly srv = inject(CookieService); get(key: string): ITokenModel { try { diff --git a/packages/auth/src/token/jwt/jwt.guard.ts b/packages/auth/src/token/jwt/jwt.guard.ts index 963b69c4b..5ae1adfa5 100644 --- a/packages/auth/src/token/jwt/jwt.guard.ts +++ b/packages/auth/src/token/jwt/jwt.guard.ts @@ -1,13 +1,13 @@ -import { Inject, Injectable, inject } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { CanActivateChildFn, CanActivateFn, CanMatchFn } from '@angular/router'; import { JWTTokenModel } from './jwt.model'; import { CheckJwt, ToLogin } from '../helper'; -import { DA_SERVICE_TOKEN, ITokenService } from '../interface'; +import { DA_SERVICE_TOKEN } from '../interface'; @Injectable({ providedIn: 'root' }) export class AuthJWTGuardService { - constructor(@Inject(DA_SERVICE_TOKEN) private srv: ITokenService) {} + private readonly srv = inject(DA_SERVICE_TOKEN); process(url?: string): boolean { const cog = this.srv.options; diff --git a/packages/auth/src/token/simple/simple.guard.ts b/packages/auth/src/token/simple/simple.guard.ts index 7786968d8..ac2ef6ee8 100644 --- a/packages/auth/src/token/simple/simple.guard.ts +++ b/packages/auth/src/token/simple/simple.guard.ts @@ -1,13 +1,13 @@ -import { Inject, Injectable, inject } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { CanActivateChildFn, CanActivateFn, CanMatchFn } from '@angular/router'; import { SimpleTokenModel } from './simple.model'; import { CheckSimple, ToLogin } from '../helper'; -import { DA_SERVICE_TOKEN, ITokenService } from '../interface'; +import { DA_SERVICE_TOKEN } from '../interface'; @Injectable({ providedIn: 'root' }) export class AuthSimpleGuardService { - constructor(@Inject(DA_SERVICE_TOKEN) private srv: ITokenService) {} + private readonly srv = inject(DA_SERVICE_TOKEN); process(url?: string): boolean { const res = CheckSimple(this.srv.get() as SimpleTokenModel); diff --git a/packages/auth/src/token/token.service.ts b/packages/auth/src/token/token.service.ts index 8f6cad619..dfa24f59b 100644 --- a/packages/auth/src/token/token.service.ts +++ b/packages/auth/src/token/token.service.ts @@ -1,14 +1,14 @@ -import { inject, Inject, Injectable, OnDestroy } from '@angular/core'; +import { inject, Injectable, OnDestroy } from '@angular/core'; import { BehaviorSubject, interval, Observable, Subject, Subscription, filter, map, share } from 'rxjs'; import { AlainAuthConfig, AlainConfigService } from '@delon/util/config'; import { AuthReferrer, ITokenModel, ITokenService } from './interface'; import { mergeConfig } from '../auth.config'; -import { DA_STORE_TOKEN, IStore } from '../store/interface'; +import { DA_STORE_TOKEN } from '../store/interface'; export function DA_SERVICE_TOKEN_FACTORY(): ITokenService { - return new TokenService(inject(AlainConfigService), inject(DA_STORE_TOKEN)); + return new TokenService(inject(AlainConfigService)); } /** @@ -16,16 +16,14 @@ export function DA_SERVICE_TOKEN_FACTORY(): ITokenService { */ @Injectable() export class TokenService implements ITokenService, OnDestroy { + private readonly store = inject(DA_STORE_TOKEN); private refresh$ = new Subject(); private change$ = new BehaviorSubject(null); private interval$?: Subscription; private _referrer: AuthReferrer = {}; private _options: AlainAuthConfig; - constructor( - configSrv: AlainConfigService, - @Inject(DA_STORE_TOKEN) private store: IStore - ) { + constructor(configSrv: AlainConfigService) { this._options = mergeConfig(configSrv); } diff --git a/packages/cache/src/cache.service.ts b/packages/cache/src/cache.service.ts index b4d1e218b..ed3bd59b7 100644 --- a/packages/cache/src/cache.service.ts +++ b/packages/cache/src/cache.service.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Platform } from '@angular/cdk/platform'; import { HttpClient } from '@angular/common/http'; -import { Inject, Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { BehaviorSubject, Observable, of, map, tap } from 'rxjs'; import { addSeconds } from 'date-fns'; @@ -9,11 +9,15 @@ import { addSeconds } from 'date-fns'; import { AlainCacheConfig, AlainConfigService } from '@delon/util/config'; import { deepGet } from '@delon/util/other'; -import { CacheNotifyResult, CacheNotifyType, ICache, ICacheStore } from './interface'; +import { CacheNotifyResult, CacheNotifyType, ICache } from './interface'; import { DC_STORE_STORAGE_TOKEN } from './local-storage-cache.service'; @Injectable({ providedIn: 'root' }) export class CacheService implements OnDestroy { + private readonly store = inject(DC_STORE_STORAGE_TOKEN); + private readonly http = inject(HttpClient); + private readonly platform = inject(Platform); + private readonly memory: Map = new Map(); private readonly notifyBuffer: Map> = new Map< string, @@ -24,19 +28,14 @@ export class CacheService implements OnDestroy { private freqTime: any; private cog: AlainCacheConfig; - constructor( - cogSrv: AlainConfigService, - @Inject(DC_STORE_STORAGE_TOKEN) private store: ICacheStore, - private http: HttpClient, - private platform: Platform - ) { + constructor(cogSrv: AlainConfigService) { this.cog = cogSrv.merge('cache', { mode: 'promise', reName: '', prefix: '', meta_key: '__cache_meta' })!; - if (!platform.isBrowser) return; + if (!this.platform.isBrowser) return; this.loadMeta(); this.startExpireNotify(); } diff --git a/packages/cache/src/local-storage-cache.service.ts b/packages/cache/src/local-storage-cache.service.ts index 622bfae62..bb0293614 100644 --- a/packages/cache/src/local-storage-cache.service.ts +++ b/packages/cache/src/local-storage-cache.service.ts @@ -5,11 +5,11 @@ import { ICache, ICacheStore } from './interface'; export const DC_STORE_STORAGE_TOKEN = new InjectionToken('DC_STORE_STORAGE_TOKEN', { providedIn: 'root', - factory: () => new LocalStorageCacheService(inject(Platform)) + factory: () => new LocalStorageCacheService() }); export class LocalStorageCacheService implements ICacheStore { - constructor(private platform: Platform) {} + private readonly platform = inject(Platform); get(key: string): ICache | null { if (!this.platform.isBrowser) { diff --git a/packages/chart/bar/bar.component.ts b/packages/chart/bar/bar.component.ts index 1fb1a7edf..98239c659 100644 --- a/packages/chart/bar/bar.component.ts +++ b/packages/chart/bar/bar.component.ts @@ -5,14 +5,15 @@ import { Input, Output, TemplateRef, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + numberAttribute } from '@angular/core'; import { fromEvent, debounceTime, filter, takeUntil } from 'rxjs'; import type { Chart, Event } from '@antv/g2'; import { G2BaseComponent, G2InteractionType } from '@delon/chart/core'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzSkeletonComponent } from 'ng-zorro-antd/skeleton'; @@ -53,17 +54,14 @@ export interface G2BarClickItem { imports: [NzStringTemplateOutletDirective, NzSkeletonComponent] }) export class G2BarComponent extends G2BaseComponent { - static ngAcceptInputType_height: NumberInput; - static ngAcceptInputType_autoLabel: BooleanInput; - // #region fields @Input() title?: string | TemplateRef; @Input() color = 'rgba(24, 144, 255, 0.85)'; - @Input() @InputNumber() height = 0; + @Input({ transform: numberAttribute }) height = 0; @Input() padding: number | number[] | 'auto' = 'auto'; @Input() data: G2BarData[] = []; - @Input() @InputBoolean() autoLabel = true; + @Input({ transform: booleanAttribute }) autoLabel = true; @Input() interaction: G2InteractionType = 'none'; @Output() readonly clickItem = new EventEmitter(); diff --git a/packages/chart/card/card.component.ts b/packages/chart/card/card.component.ts index 8f6b58e4a..1c06bd01f 100644 --- a/packages/chart/card/card.component.ts +++ b/packages/chart/card/card.component.ts @@ -5,10 +5,11 @@ import { Input, OnChanges, TemplateRef, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + inject } from '@angular/core'; -import { BooleanInput, InputBoolean } from '@delon/util/decorator'; import { NzCardComponent } from 'ng-zorro-antd/card'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; import { NzSpinComponent } from 'ng-zorro-antd/spin'; @@ -25,11 +26,9 @@ import { NzSpinComponent } from 'ng-zorro-antd/spin'; imports: [NzCardComponent, NzSpinComponent, NzStringTemplateOutletDirective] }) export class G2CardComponent implements OnChanges { - static ngAcceptInputType_bordered: BooleanInput; - static ngAcceptInputType_loading: BooleanInput; - + private readonly cdr = inject(ChangeDetectorRef); /** 是否显示边框 */ - @Input() @InputBoolean() bordered = false; + @Input({ transform: booleanAttribute }) bordered = false; @Input() avatar?: string | TemplateRef | null; @Input() title?: string | TemplateRef | null; @Input() action?: string | TemplateRef | null; @@ -43,9 +42,7 @@ export class G2CardComponent implements OnChanges { } @Input() footer?: string | TemplateRef | null; /** 是否显示Loading */ - @Input() @InputBoolean() loading = false; - - constructor(private cdr: ChangeDetectorRef) {} + @Input({ transform: booleanAttribute }) loading = false; ngOnChanges(): void { this.cdr.detectChanges(); diff --git a/packages/chart/chart-echarts/echarts.component.ts b/packages/chart/chart-echarts/echarts.component.ts index 27ca9f059..a9ae2632c 100644 --- a/packages/chart/chart-echarts/echarts.component.ts +++ b/packages/chart/chart-echarts/echarts.component.ts @@ -55,6 +55,11 @@ export class ChartEChartsComponent implements OnInit, OnDestroy { static ngAcceptInputType_width: NumberInput; static ngAcceptInputType_height: NumberInput; + private readonly srv = inject(ChartEChartsService); + private readonly cdr = inject(ChangeDetectorRef); + private readonly ngZone = inject(NgZone); + private readonly platform = inject(Platform); + @ViewChild('container', { static: true }) private node!: ElementRef; private destroy$ = inject(DestroyRef); private _chart: ChartECharts | null = null; @@ -106,12 +111,7 @@ export class ChartEChartsComponent implements OnInit, OnDestroy { } loaded = false; - constructor( - private srv: ChartEChartsService, - private cdr: ChangeDetectorRef, - private ngZone: NgZone, - private platform: Platform - ) { + constructor() { this.srv.notify .pipe( takeUntilDestroyed(), @@ -119,7 +119,7 @@ export class ChartEChartsComponent implements OnInit, OnDestroy { ) .subscribe(() => this.load()); - this.theme = srv.cog.echartsTheme; + this.theme = this.srv.cog.echartsTheme; } private emit(type: ChartEChartsEventType, other?: ChartEChartsEvent): void { diff --git a/packages/chart/chart-echarts/echarts.service.ts b/packages/chart/chart-echarts/echarts.service.ts index 029c78b21..1c6f1bc6a 100644 --- a/packages/chart/chart-echarts/echarts.service.ts +++ b/packages/chart/chart-echarts/echarts.service.ts @@ -1,4 +1,4 @@ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { Observable, Subject } from 'rxjs'; import { AlainChartConfig, AlainConfigService } from '@delon/util/config'; @@ -6,6 +6,8 @@ import { LazyService } from '@delon/util/other'; @Injectable({ providedIn: 'root' }) export class ChartEChartsService implements OnDestroy { + private readonly cogSrv = inject(AlainConfigService); + private readonly lazySrv = inject(LazyService); private _cog!: AlainChartConfig; private loading = false; private loaded = false; @@ -25,10 +27,7 @@ export class ChartEChartsService implements OnDestroy { )!; } - constructor( - private cogSrv: AlainConfigService, - private lazySrv: LazyService - ) { + constructor() { this.cog = { theme: '' }; } diff --git a/packages/chart/chart-echarts/echarts.spec.ts b/packages/chart/chart-echarts/echarts.spec.ts index 0f7371fa2..41b97eb0c 100644 --- a/packages/chart/chart-echarts/echarts.spec.ts +++ b/packages/chart/chart-echarts/echarts.spec.ts @@ -3,7 +3,6 @@ import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testin import { By } from '@angular/platform-browser'; import { createTestContext } from '@delon/testing'; -import { NumberInput } from '@delon/util/decorator'; import { LazyService } from '@delon/util/other'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -102,8 +101,8 @@ describe('chart: chart-echarts', () => { }) class TestComponent { @ViewChild('cmp') readonly cmp!: ChartEChartsComponent; - width: NumberInput = 600; - height: NumberInput = 400; + width: string | number = 600; + height: string | number = 400; theme?: string | Record | null = null; option: ChartEChartsOption = {}; initOpt: NzSafeAny; diff --git a/packages/chart/core/g2.base.component.ts b/packages/chart/core/g2.base.component.ts index c5ece0b67..955c6b836 100644 --- a/packages/chart/core/g2.base.component.ts +++ b/packages/chart/core/g2.base.component.ts @@ -11,19 +11,28 @@ import { OnInit, Output, SimpleChanges, - ViewChild + ViewChild, + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import { Subject, Subscription, filter, takeUntil } from 'rxjs'; import type { Chart, Types } from '@antv/g2'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput, ZoneOutside } from '@delon/util/decorator'; +import { ZoneOutside } from '@delon/util/decorator'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { G2Service } from './g2.servicce'; @Directive() export abstract class G2BaseComponent implements OnInit, OnChanges, OnDestroy { + protected readonly srv = inject(G2Service); + protected readonly el: ElementRef = inject(ElementRef); + protected readonly ngZone = inject(NgZone); + protected readonly platform = inject(Platform); + protected readonly cdr = inject(ChangeDetectorRef); + get chart(): Chart { return this._chart; } @@ -32,14 +41,8 @@ export abstract class G2BaseComponent implements OnInit, OnChanges, OnDestroy { return (window as NzSafeAny).G2; } - constructor( - protected srv: G2Service, - protected el: ElementRef, - protected ngZone: NgZone, - protected platform: Platform, - protected cdr: ChangeDetectorRef - ) { - this.theme = srv.cog.theme!; + constructor() { + this.theme = this.srv.cog.theme!; this.srv.notify .pipe( takeUntil(this.destroy$), @@ -47,9 +50,7 @@ export abstract class G2BaseComponent implements OnInit, OnChanges, OnDestroy { ) .subscribe(() => this.load()); } - static ngAcceptInputType_repaint: BooleanInput; - static ngAcceptInputType_delay: NumberInput; - @Input() @InputBoolean() repaint = true; + @Input({ transform: booleanAttribute }) repaint = true; @ViewChild('container', { static: true }) protected node!: ElementRef; protected resize$?: Subscription; @@ -57,7 +58,7 @@ export abstract class G2BaseComponent implements OnInit, OnChanges, OnDestroy { protected _chart!: Chart; loaded = false; - @Input() @InputNumber() delay = 0; + @Input({ transform: numberAttribute }) delay = 0; @Input() theme: string | Types.LooseObject; @Output() readonly ready = new EventEmitter(); diff --git a/packages/chart/core/g2.servicce.ts b/packages/chart/core/g2.servicce.ts index 8189d89c3..39f50f550 100644 --- a/packages/chart/core/g2.servicce.ts +++ b/packages/chart/core/g2.servicce.ts @@ -1,4 +1,4 @@ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { Observable, Subject } from 'rxjs'; import { AlainChartConfig, AlainConfigService } from '@delon/util/config'; @@ -6,6 +6,9 @@ import { LazyService } from '@delon/util/other'; @Injectable({ providedIn: 'root' }) export class G2Service implements OnDestroy { + private readonly cogSrv = inject(AlainConfigService); + private readonly lazySrv = inject(LazyService); + private _cog!: AlainChartConfig; private loading = false; private loaded = false; @@ -28,10 +31,7 @@ export class G2Service implements OnDestroy { )!; } - constructor( - private cogSrv: AlainConfigService, - private lazySrv: LazyService - ) { + constructor() { this.cog = { theme: '' }; } diff --git a/packages/chart/custom/custom.component.ts b/packages/chart/custom/custom.component.ts index 5af49e562..09a13aae7 100644 --- a/packages/chart/custom/custom.component.ts +++ b/packages/chart/custom/custom.component.ts @@ -5,12 +5,12 @@ import { EventEmitter, Input, Output, - ViewEncapsulation + ViewEncapsulation, + numberAttribute } from '@angular/core'; import { fromEvent, debounceTime, takeUntil } from 'rxjs'; import { G2BaseComponent } from '@delon/chart/core'; -import { InputNumber, NumberInput } from '@delon/util/decorator'; import { NzSkeletonComponent } from 'ng-zorro-antd/skeleton'; @Component({ @@ -32,13 +32,10 @@ import { NzSkeletonComponent } from 'ng-zorro-antd/skeleton'; imports: [NzSkeletonComponent] }) export class G2CustomComponent extends G2BaseComponent { - static ngAcceptInputType_height: NumberInput; - static ngAcceptInputType_resizeTime: NumberInput; - // #region fields - @Input() @InputNumber() height?: number; - @Input() @InputNumber() resizeTime = 0; + @Input({ transform: numberAttribute }) height?: number; + @Input({ transform: numberAttribute }) resizeTime = 0; @Output() readonly render = new EventEmitter(); @Output() readonly resize = new EventEmitter(); @Output() readonly destroy = new EventEmitter(); diff --git a/packages/chart/gauge/gauge.component.ts b/packages/chart/gauge/gauge.component.ts index cf6464d36..e4d29ce31 100644 --- a/packages/chart/gauge/gauge.component.ts +++ b/packages/chart/gauge/gauge.component.ts @@ -1,9 +1,8 @@ -import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation, numberAttribute } from '@angular/core'; import type { Chart } from '@antv/g2'; import { G2BaseComponent } from '@delon/chart/core'; -import { InputNumber, NumberInput } from '@delon/util/decorator'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzSkeletonComponent } from 'ng-zorro-antd/skeleton'; @@ -23,17 +22,14 @@ import { NzSkeletonComponent } from 'ng-zorro-antd/skeleton'; imports: [NzSkeletonComponent] }) export class G2GaugeComponent extends G2BaseComponent { - static ngAcceptInputType_height: NumberInput; - static ngAcceptInputType_percent: NumberInput; - // #region fields @Input() title?: string; - @Input() @InputNumber() height?: number; + @Input({ transform: numberAttribute }) height?: number; @Input() color = '#2f9cff'; @Input() bgColor?: string; // = '#f0f2f5'; @Input() format?: (text: string, item: NzSafeAny, index: number) => string; - @Input() @InputNumber() percent?: number; + @Input({ transform: numberAttribute }) percent?: number; @Input() padding: number | number[] | 'auto' = [10, 10, 30, 10]; // #endregion diff --git a/packages/chart/mini-area/mini-area.component.ts b/packages/chart/mini-area/mini-area.component.ts index e85573f87..9cf0012ca 100644 --- a/packages/chart/mini-area/mini-area.component.ts +++ b/packages/chart/mini-area/mini-area.component.ts @@ -1,9 +1,17 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, + ViewEncapsulation, + booleanAttribute, + numberAttribute +} from '@angular/core'; import type { Chart, Event } from '@antv/g2'; import { G2BaseComponent, genMiniTooltipOptions } from '@delon/chart/core'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; export interface G2MiniAreaData { @@ -30,21 +38,15 @@ export interface G2MiniAreaClickItem { standalone: true }) export class G2MiniAreaComponent extends G2BaseComponent { - static ngAcceptInputType_borderWidth: NumberInput; - static ngAcceptInputType_height: NumberInput; - static ngAcceptInputType_fit: BooleanInput; - static ngAcceptInputType_line: BooleanInput; - static ngAcceptInputType_animate: BooleanInput; - // #region fields @Input() color = 'rgba(24, 144, 255, 0.2)'; @Input() borderColor = '#1890FF'; - @Input() @InputNumber() borderWidth = 2; - @Input() @InputNumber() height = 56; - @Input() @InputBoolean() fit = true; - @Input() @InputBoolean() line = false; - @Input() @InputBoolean() animate = true; + @Input({ transform: numberAttribute }) borderWidth = 2; + @Input({ transform: numberAttribute }) height = 56; + @Input({ transform: booleanAttribute }) fit = true; + @Input({ transform: booleanAttribute }) line = false; + @Input({ transform: booleanAttribute }) animate = true; @Input() xAxis: NzSafeAny; @Input() yAxis: NzSafeAny; @Input() padding: number | number[] | 'auto' = [8, 8, 8, 8]; diff --git a/packages/chart/mini-bar/mini-bar.component.ts b/packages/chart/mini-bar/mini-bar.component.ts index c542b9eb3..0f7295631 100644 --- a/packages/chart/mini-bar/mini-bar.component.ts +++ b/packages/chart/mini-bar/mini-bar.component.ts @@ -1,9 +1,16 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, + ViewEncapsulation, + numberAttribute +} from '@angular/core'; import type { Chart, Event } from '@antv/g2'; import { G2BaseComponent, genMiniTooltipOptions } from '@delon/chart/core'; -import { InputNumber, NumberInput } from '@delon/util/decorator'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; export interface G2MiniBarData { @@ -31,14 +38,11 @@ export interface G2MiniBarClickItem { standalone: true }) export class G2MiniBarComponent extends G2BaseComponent { - static ngAcceptInputType_height: NumberInput; - static ngAcceptInputType_borderWidth: NumberInput; - // #region fields @Input() color = '#1890FF'; - @Input() @InputNumber() height = 0; - @Input() @InputNumber() borderWidth = 5; + @Input({ transform: numberAttribute }) height = 0; + @Input({ transform: numberAttribute }) borderWidth = 5; @Input() padding: number | number[] | 'auto' = [8, 8, 8, 8]; @Input() data: G2MiniBarData[] = []; @Input() yTooltipSuffix = ''; diff --git a/packages/chart/mini-progress/mini-progress.component.ts b/packages/chart/mini-progress/mini-progress.component.ts index ff43223f4..26c4f29dc 100644 --- a/packages/chart/mini-progress/mini-progress.component.ts +++ b/packages/chart/mini-progress/mini-progress.component.ts @@ -5,11 +5,12 @@ import { Component, Input, OnChanges, - ViewEncapsulation + ViewEncapsulation, + inject, + numberAttribute } from '@angular/core'; import { DelonLocaleService } from '@delon/theme'; -import { InputNumber, NumberInput, toNumber } from '@delon/util/decorator'; import { NzTooltipDirective } from 'ng-zorro-antd/tooltip'; @Component({ @@ -24,22 +25,16 @@ import { NzTooltipDirective } from 'ng-zorro-antd/tooltip'; imports: [NzTooltipDirective, NgStyle] }) export class G2MiniProgressComponent implements OnChanges { - static ngAcceptInputType_target: NumberInput; - static ngAcceptInputType_percent: NumberInput; - static ngAcceptInputType_strokeWidth: NumberInput; + readonly i18n = inject(DelonLocaleService); + private readonly cdr = inject(ChangeDetectorRef); @Input() color = '#1890FF'; - @Input() @InputNumber() target?: number | null; - @Input() @InputNumber() percent?: number | null; - @Input() @InputNumber() strokeWidth?: number | null; - - constructor( - public i18n: DelonLocaleService, - private cdr: ChangeDetectorRef - ) {} + @Input({ transform: numberAttribute }) target?: number | null; + @Input({ transform: numberAttribute }) percent?: number | null; + @Input({ transform: numberAttribute }) strokeWidth?: number | null; private fixNum(value: number | undefined | null): number { - return Math.min(Math.max(toNumber(value), 0), 100); + return Math.min(Math.max(numberAttribute(value), 0), 100); } ngOnChanges(): void { diff --git a/packages/chart/number-info/number-info.component.ts b/packages/chart/number-info/number-info.component.ts index c118dff84..e06b95107 100644 --- a/packages/chart/number-info/number-info.component.ts +++ b/packages/chart/number-info/number-info.component.ts @@ -1,6 +1,12 @@ -import { ChangeDetectionStrategy, Component, Input, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Input, + TemplateRef, + ViewEncapsulation, + numberAttribute +} from '@angular/core'; -import { InputNumber, NumberInput } from '@delon/util/decorator'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; import { NzIconDirective } from 'ng-zorro-antd/icon'; @@ -20,8 +26,6 @@ import { NzIconDirective } from 'ng-zorro-antd/icon'; imports: [NzStringTemplateOutletDirective, NzIconDirective] }) export class NumberInfoComponent { - static ngAcceptInputType_gap: NumberInput; - /** 标题 */ @Input() title?: string | TemplateRef | null; /** 子标题 */ @@ -37,5 +41,5 @@ export class NumberInfoComponent { /** 状态样式 */ @Input() theme: 'light' | 'default' = 'light'; /** 设置数字和描述直接的间距(像素) */ - @Input() @InputNumber() gap = 8; + @Input({ transform: numberAttribute }) gap = 8; } diff --git a/packages/chart/pie/pie.component.ts b/packages/chart/pie/pie.component.ts index bc9ce7eea..6c7e4f2cd 100644 --- a/packages/chart/pie/pie.component.ts +++ b/packages/chart/pie/pie.component.ts @@ -6,13 +6,14 @@ import { Input, Output, TemplateRef, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + numberAttribute } from '@angular/core'; import type { Chart, Event } from '@antv/g2'; import { G2BaseComponent, G2InteractionType } from '@delon/chart/core'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzDividerComponent } from 'ng-zorro-antd/divider'; @@ -57,34 +58,25 @@ export interface G2PieRatio { imports: [NzSkeletonComponent, NzStringTemplateOutletDirective, NzDividerComponent, NgStyle] }) export class G2PieComponent extends G2BaseComponent { - static ngAcceptInputType_height: NumberInput; - static ngAcceptInputType_animate: BooleanInput; - static ngAcceptInputType_hasLegend: BooleanInput; - static ngAcceptInputType_percent: NumberInput; - static ngAcceptInputType_tooltip: BooleanInput; - static ngAcceptInputType_lineWidth: NumberInput; - static ngAcceptInputType_blockMaxWidth: NumberInput; - static ngAcceptInputType_select: BooleanInput; - private percentColor!: (value: string) => string; legendData: NzSafeAny[] = []; isPercent = false; // #region fields - @Input() @InputBoolean() animate = true; + @Input({ transform: booleanAttribute }) animate = true; @Input() color = 'rgba(24, 144, 255, 0.85)'; @Input() subTitle?: string | TemplateRef | null; @Input() total?: string | number | TemplateRef | null; - @Input() @InputNumber() height = 0; - @Input() @InputBoolean() hasLegend = false; + @Input({ transform: numberAttribute }) height = 0; + @Input({ transform: booleanAttribute }) hasLegend = false; @Input() inner = 0.75; @Input() padding: number | number[] | 'auto' = [12, 0, 12, 0]; - @Input() @InputNumber() percent?: number; - @Input() @InputBoolean() tooltip = true; - @Input() @InputNumber() lineWidth = 0; - @Input() @InputNumber() blockMaxWidth = 380; - @Input() @InputBoolean() select = true; + @Input({ transform: numberAttribute }) percent?: number; + @Input({ transform: booleanAttribute }) tooltip = true; + @Input({ transform: numberAttribute }) lineWidth = 0; + @Input({ transform: numberAttribute }) blockMaxWidth = 380; + @Input({ transform: booleanAttribute }) select = true; @Input() valueFormat?: (y: number) => string; @Input() data: G2PieData[] = []; @Input() colors?: string[]; diff --git a/packages/chart/radar/radar.component.ts b/packages/chart/radar/radar.component.ts index ae7b65ec1..a70205661 100644 --- a/packages/chart/radar/radar.component.ts +++ b/packages/chart/radar/radar.component.ts @@ -6,13 +6,14 @@ import { Input, Output, TemplateRef, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + numberAttribute } from '@angular/core'; import type { Chart, Event } from '@antv/g2'; import { G2BaseComponent } from '@delon/chart/core'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzColDirective, NzRowDirective } from 'ng-zorro-antd/grid'; @@ -45,19 +46,15 @@ export interface G2RadarClickItem { imports: [NzSkeletonComponent, NzStringTemplateOutletDirective, NzRowDirective, NzColDirective, NgStyle] }) export class G2RadarComponent extends G2BaseComponent { - static ngAcceptInputType_height: NumberInput; - static ngAcceptInputType_hasLegend: BooleanInput; - static ngAcceptInputType_tickCount: NumberInput; - legendData: NzSafeAny[] = []; // #region fields @Input() title?: string | TemplateRef | null; - @Input() @InputNumber() height = 0; + @Input({ transform: numberAttribute }) height = 0; @Input() padding: number | number[] | 'auto' = [44, 30, 16, 30]; - @Input() @InputBoolean() hasLegend = true; - @Input() @InputNumber() tickCount = 4; + @Input({ transform: booleanAttribute }) hasLegend = true; + @Input({ transform: numberAttribute }) tickCount = 4; @Input() data: G2RadarData[] = []; @Input() colors = ['#1890FF', '#FACC14', '#2FC25B', '#8543E0', '#F04864', '#13C2C2', '#fa8c16', '#a0d911']; @Output() readonly clickItem = new EventEmitter(); diff --git a/packages/chart/single-bar/single-bar.component.ts b/packages/chart/single-bar/single-bar.component.ts index 0b13cdd7f..75a159050 100644 --- a/packages/chart/single-bar/single-bar.component.ts +++ b/packages/chart/single-bar/single-bar.component.ts @@ -1,9 +1,16 @@ -import { ChangeDetectionStrategy, Component, Input, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Input, + SimpleChanges, + ViewEncapsulation, + booleanAttribute, + numberAttribute +} from '@angular/core'; import type { Chart } from '@antv/g2'; import { G2BaseComponent } from '@delon/chart/core'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @Component({ @@ -19,23 +26,16 @@ import type { NzSafeAny } from 'ng-zorro-antd/core/types'; standalone: true }) export class G2SingleBarComponent extends G2BaseComponent { - static ngAcceptInputType_height: NumberInput; - static ngAcceptInputType_barSize: NumberInput; - static ngAcceptInputType_min: NumberInput; - static ngAcceptInputType_max: NumberInput; - static ngAcceptInputType_value: NumberInput; - static ngAcceptInputType_line: BooleanInput; - // #region fields @Input() plusColor = '#40a9ff'; @Input() minusColor = '#ff4d4f'; - @Input() @InputNumber() height = 60; - @Input() @InputNumber() barSize = 30; - @Input() @InputNumber() min = 0; - @Input() @InputNumber() max = 100; - @Input() @InputNumber() value = 0; - @Input() @InputBoolean() line = false; + @Input({ transform: numberAttribute }) height = 60; + @Input({ transform: numberAttribute }) barSize = 30; + @Input({ transform: numberAttribute }) min = 0; + @Input({ transform: numberAttribute }) max = 100; + @Input({ transform: numberAttribute }) value = 0; + @Input({ transform: booleanAttribute }) line = false; @Input() format?: (value: number, item: NzSafeAny, index: number) => string; @Input() padding: number | number[] | 'auto' = 0; @Input() textStyle: { [key: string]: NzSafeAny } = { fontSize: 12, color: '#595959' }; diff --git a/packages/chart/tag-cloud/tag-cloud.component.ts b/packages/chart/tag-cloud/tag-cloud.component.ts index 3719fb555..8c67099b5 100644 --- a/packages/chart/tag-cloud/tag-cloud.component.ts +++ b/packages/chart/tag-cloud/tag-cloud.component.ts @@ -1,10 +1,17 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, + ViewEncapsulation, + numberAttribute +} from '@angular/core'; import { fromEvent, debounceTime, filter } from 'rxjs'; import type { Chart, Event } from '@antv/g2'; import { G2BaseComponent } from '@delon/chart/core'; -import { InputNumber, NumberInput } from '@delon/util/decorator'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzSkeletonComponent } from 'ng-zorro-antd/skeleton'; @@ -32,13 +39,10 @@ export interface G2TagCloudClickItem { imports: [NzSkeletonComponent] }) export class G2TagCloudComponent extends G2BaseComponent { - static ngAcceptInputType_height: NumberInput; - static ngAcceptInputType_width: NumberInput; - // #region fields - @Input() @InputNumber() width = 0; - @Input() @InputNumber() height = 200; + @Input({ transform: numberAttribute }) width = 0; + @Input({ transform: numberAttribute }) height = 200; @Input() padding: number | number[] | 'auto' = 0; @Input() data: G2TagCloudData[] = []; @Output() readonly clickItem = new EventEmitter(); diff --git a/packages/chart/timeline/timeline.component.ts b/packages/chart/timeline/timeline.component.ts index 2688d115d..b2d883322 100644 --- a/packages/chart/timeline/timeline.component.ts +++ b/packages/chart/timeline/timeline.component.ts @@ -6,7 +6,9 @@ import { Output, SimpleChanges, TemplateRef, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + numberAttribute } from '@angular/core'; import type { Chart, Event, Types } from '@antv/g2'; @@ -14,7 +16,6 @@ import { format } from 'date-fns'; import { G2BaseComponent, G2Time } from '@delon/chart/core'; import { toDate } from '@delon/util/date-time'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzSkeletonComponent } from 'ng-zorro-antd/skeleton'; @@ -81,25 +82,20 @@ export interface G2TimelineClickItem { imports: [NzStringTemplateOutletDirective, NzSkeletonComponent] }) export class G2TimelineComponent extends G2BaseComponent { - static ngAcceptInputType_height: NumberInput; - static ngAcceptInputType_maxAxis: NumberInput; - static ngAcceptInputType_borderWidth: NumberInput; - static ngAcceptInputType_slider: BooleanInput; - // #region fields @Input() title?: string | TemplateRef | null; - @Input() @InputNumber() maxAxis = 2; + @Input({ transform: numberAttribute }) maxAxis = 2; @Input() data: G2TimelineData[] = []; @Input() titleMap?: G2TimelineMap | null; @Input() colorMap: G2TimelineMap = { y1: '#5B8FF9', y2: '#5AD8A6', y3: '#5D7092', y4: '#F6BD16', y5: '#E86452' }; @Input() mask: string = 'HH:mm'; @Input() maskSlider: string = 'HH:mm'; @Input() position: 'top' | 'right' | 'bottom' | 'left' = 'top'; - @Input() @InputNumber() height = 450; + @Input({ transform: numberAttribute }) height = 450; @Input() padding: number[] = [40, 8, 64, 40]; - @Input() @InputNumber() borderWidth = 2; - @Input() @InputBoolean() slider = true; + @Input({ transform: numberAttribute }) borderWidth = 2; + @Input({ transform: booleanAttribute }) slider = true; @Output() readonly clickItem = new EventEmitter(); // #endregion diff --git a/packages/chart/trend/trend.component.ts b/packages/chart/trend/trend.component.ts index 83cb7e0bb..22bcfd7bd 100644 --- a/packages/chart/trend/trend.component.ts +++ b/packages/chart/trend/trend.component.ts @@ -1,6 +1,5 @@ -import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation, booleanAttribute } from '@angular/core'; -import { BooleanInput, InputBoolean } from '@delon/util/decorator'; import { NzIconDirective } from 'ng-zorro-antd/icon'; @Component({ @@ -25,13 +24,10 @@ import { NzIconDirective } from 'ng-zorro-antd/icon'; imports: [NzIconDirective] }) export class TrendComponent { - static ngAcceptInputType_colorful: BooleanInput; - static ngAcceptInputType_reverseColor: BooleanInput; - /** 上升下降标识 */ @Input() flag?: 'up' | 'down'; /** 是否彩色标记 */ - @Input() @InputBoolean() colorful = true; + @Input({ transform: booleanAttribute }) colorful = true; /** 颜色反转 */ - @Input() @InputBoolean() reverseColor = false; + @Input({ transform: booleanAttribute }) reverseColor = false; } diff --git a/packages/chart/water-wave/water-wave.component.ts b/packages/chart/water-wave/water-wave.component.ts index 7917280f4..8dec42e5f 100644 --- a/packages/chart/water-wave/water-wave.component.ts +++ b/packages/chart/water-wave/water-wave.component.ts @@ -13,11 +13,13 @@ import { Renderer2, TemplateRef, ViewChild, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import { fromEvent, Subscription, debounceTime } from 'rxjs'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util/decorator'; import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; @Component({ @@ -32,10 +34,11 @@ import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; imports: [NgStyle, NzStringTemplateOutletDirective] }) export class G2WaterWaveComponent implements OnDestroy, OnChanges, OnInit { - static ngAcceptInputType_animate: BooleanInput; - static ngAcceptInputType_delay: NumberInput; - static ngAcceptInputType_height: NumberInput; - static ngAcceptInputType_percent: NumberInput; + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly renderer = inject(Renderer2); + private readonly ngZone = inject(NgZone); + private readonly cdr = inject(ChangeDetectorRef); + private readonly platform = inject(Platform); private resize$: Subscription | null = null; @ViewChild('container', { static: true }) private node!: ElementRef; @@ -43,23 +46,15 @@ export class G2WaterWaveComponent implements OnDestroy, OnChanges, OnInit { // #region fields - @Input() @InputBoolean() animate = true; - @Input() @InputNumber() delay = 0; + @Input({ transform: booleanAttribute }) animate = true; + @Input({ transform: numberAttribute }) delay = 0; @Input() title?: string | TemplateRef | null; @Input() color = '#1890FF'; - @Input() @InputNumber() height = 160; - @Input() @InputNumber() percent?: number; + @Input({ transform: numberAttribute }) height = 160; + @Input({ transform: numberAttribute }) percent?: number; // #endregion - constructor( - private el: ElementRef, - private renderer: Renderer2, - private ngZone: NgZone, - private cdr: ChangeDetectorRef, - private platform: Platform - ) {} - private renderChart(isUpdate: boolean): void { if (!this.resize$) return; @@ -207,9 +202,9 @@ export class G2WaterWaveComponent implements OnDestroy, OnChanges, OnInit { } private updateRadio(): void { - const { offsetWidth } = this.el.nativeElement.parentNode; + const { offsetWidth } = this.el.parentNode! as HTMLElement; const radio = offsetWidth < this.height ? offsetWidth / this.height : 1; - this.renderer.setStyle(this.el.nativeElement, 'transform', `scale(${radio})`); + this.renderer.setStyle(this.el, 'transform', `scale(${radio})`); } render(): void { diff --git a/packages/chart/water-wave/water-wave.spec.ts b/packages/chart/water-wave/water-wave.spec.ts index 5a77a80e0..8d9d73b0e 100644 --- a/packages/chart/water-wave/water-wave.spec.ts +++ b/packages/chart/water-wave/water-wave.spec.ts @@ -25,7 +25,7 @@ describe('chart: water-wave', () => { const styleSpy = spyOn(page.comp.renderer, 'setStyle'); page.context.animate = false; page.context.height = 100; - spyOnProperty(page.comp.el.nativeElement.parentNode, 'offsetWidth').and.returnValue(50); + spyOnProperty(page.comp.el.parentNode, 'offsetWidth').and.returnValue(50); page.dcFirst(); expect(styleSpy.calls.mostRecent().args[2]).toBe('scale(0.5)'); })); diff --git a/packages/form/src/sf-fixed.directive.ts b/packages/form/src/sf-fixed.directive.ts index c37814ddf..63623c0aa 100644 --- a/packages/form/src/sf-fixed.directive.ts +++ b/packages/form/src/sf-fixed.directive.ts @@ -1,16 +1,26 @@ -import { AfterViewInit, Directive, ElementRef, Input, OnChanges, Renderer2 } from '@angular/core'; - -import { InputNumber } from '@delon/util/decorator'; +import { + AfterViewInit, + Directive, + ElementRef, + Input, + OnChanges, + Renderer2, + inject, + numberAttribute +} from '@angular/core'; @Directive({ selector: '[fixed-label]' }) export class SFFixedDirective implements AfterViewInit, OnChanges { + private readonly el: HTMLElement = inject(ElementRef).nativeElement; + private readonly render = inject(Renderer2); + private _inited = false; - @Input('fixed-label') @InputNumber() num?: number | null; + @Input({ alias: 'fixed-label', transform: (v: unknown) => numberAttribute(v, 0) }) num?: number | null; private init(): void { if (!this._inited || this.num == null || this.num <= 0) return; - const el = this.el.nativeElement; + const el = this.el; const widgetEl = el.querySelector('.ant-row') || el; this.render.addClass(widgetEl, 'sf__fixed'); const labelEl = widgetEl.querySelector('.ant-form-item-label'); @@ -24,11 +34,6 @@ export class SFFixedDirective implements AfterViewInit, OnChanges { } } - constructor( - private el: ElementRef, - private render: Renderer2 - ) {} - ngAfterViewInit(): void { this._inited = true; this.init(); diff --git a/packages/form/src/sf-item-wrap.component.ts b/packages/form/src/sf-item-wrap.component.ts index 1bda78eab..2df27c787 100644 --- a/packages/form/src/sf-item-wrap.component.ts +++ b/packages/form/src/sf-item-wrap.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges, ViewEncapsulation } from '@angular/core'; +import { Component, Input, OnChanges, ViewEncapsulation, inject } from '@angular/core'; import { helpMotion } from 'ng-zorro-antd/core/animation'; import { NzFormStatusService } from 'ng-zorro-antd/core/form'; @@ -14,6 +14,8 @@ import type { SFOptionalHelp, SFUISchemaItem } from './schema/ui'; encapsulation: ViewEncapsulation.None }) export class SFItemWrapComponent implements OnChanges { + private readonly statusSrv = inject(NzFormStatusService); + _showTitle: boolean = false; @Input() id?: string; @Input() schema!: SFSchema; @@ -34,8 +36,6 @@ export class SFItemWrapComponent implements OnChanges { return this.ui.optionalHelp as SFOptionalHelp; } - constructor(private statusSrv: NzFormStatusService) {} - ngOnChanges(): void { const hasError = !!this.error; this.statusSrv.formStatusChanges.next({ status: hasError ? 'error' : '', hasFeedback: !!this.ui.feedback }); diff --git a/packages/form/src/sf-item.component.ts b/packages/form/src/sf-item.component.ts index 3c8e9bd43..3c5ee58f8 100644 --- a/packages/form/src/sf-item.component.ts +++ b/packages/form/src/sf-item.component.ts @@ -8,7 +8,8 @@ import { TemplateRef, ViewChild, ViewContainerRef, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { Subject } from 'rxjs'; @@ -35,6 +36,9 @@ let nextUniqueId = 0; providers: [NzFormStatusService] }) export class SFItemComponent implements OnInit, OnChanges, OnDestroy { + private readonly widgetFactory = inject(WidgetFactory); + private readonly terminator = inject(TerminatorService); + private ref!: ComponentRef>; readonly destroy$ = new Subject(); widget: Widget | null = null; @@ -45,11 +49,6 @@ export class SFItemComponent implements OnInit, OnChanges, OnDestroy { @ViewChild('target', { read: ViewContainerRef, static: true }) private container!: ViewContainerRef; - constructor( - private widgetFactory: WidgetFactory, - private terminator: TerminatorService - ) {} - onWidgetInstanciated(widget: Widget): void { this.widget = widget; const id = `_sf-${nextUniqueId++}`; diff --git a/packages/form/src/sf.component.ts b/packages/form/src/sf.component.ts index 46b4589be..2283c3043 100644 --- a/packages/form/src/sf.component.ts +++ b/packages/form/src/sf.component.ts @@ -4,27 +4,26 @@ import { ChangeDetectorRef, Component, EventEmitter, - Inject, Injector, Input, OnChanges, OnDestroy, OnInit, - Optional, Output, SimpleChange, SimpleChanges, TemplateRef, - ViewEncapsulation + ViewEncapsulation, + booleanAttribute, + inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { DomSanitizer } from '@angular/platform-browser'; import { merge, Observable, filter } from 'rxjs'; import { ACLService } from '@delon/acl'; -import { AlainI18NService, ALAIN_I18N_TOKEN, DelonLocaleService, LocaleData } from '@delon/theme'; +import { ALAIN_I18N_TOKEN, DelonLocaleService, LocaleData } from '@delon/theme'; import { AlainConfigService, AlainSFConfig } from '@delon/util/config'; -import { BooleanInput, InputBoolean } from '@delon/util/decorator'; import { deepCopy } from '@delon/util/other'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import type { NzFormControlStatusType } from 'ng-zorro-antd/form'; @@ -78,15 +77,14 @@ export function useFactory( encapsulation: ViewEncapsulation.None }) export class SFComponent implements OnInit, OnChanges, OnDestroy { - static ngAcceptInputType_liveValidate: BooleanInput; - static ngAcceptInputType_firstVisual: BooleanInput; - static ngAcceptInputType_onlyVisual: BooleanInput; - static ngAcceptInputType_compact: BooleanInput; - static ngAcceptInputType_loading: BooleanInput; - static ngAcceptInputType_disabled: BooleanInput; - static ngAcceptInputType_noColon: BooleanInput; - static ngAcceptInputType_cleanValue: BooleanInput; - static ngAcceptInputType_delay: BooleanInput; + private readonly formPropertyFactory = inject(FormPropertyFactory); + private readonly terminator = inject(TerminatorService); + private readonly dom = inject(DomSanitizer); + private readonly cdr = inject(ChangeDetectorRef); + private readonly localeSrv = inject(DelonLocaleService); + private readonly aclSrv = inject(ACLService, { optional: true }); + private readonly i18nSrv = inject(ALAIN_I18N_TOKEN, { optional: true }); + private readonly platform = inject(Platform); private _renders = new Map>(); private _item!: Record; @@ -127,7 +125,7 @@ export class SFComponent implements OnInit, OnChanges, OnDestroy { * - `true` 每一次都校验 * - `false` 提交时校验 */ - @Input() @InputBoolean() liveValidate = true; + @Input({ transform: booleanAttribute }) liveValidate = true; /** 指定表单 `autocomplete` 值 */ @Input() autocomplete: 'on' | 'off'; /** @@ -135,14 +133,14 @@ export class SFComponent implements OnInit, OnChanges, OnDestroy { * * 是否立即显示错误视觉 */ - @Input() @InputBoolean() firstVisual = true; + @Input({ transform: booleanAttribute }) firstVisual = true; /** * Whether to only display error visuals but not error text * * 是否只展示错误视觉不显示错误文本 */ - @Input() @InputBoolean() onlyVisual = false; - @Input() @InputBoolean() compact = false; + @Input({ transform: booleanAttribute }) onlyVisual = false; + @Input({ transform: booleanAttribute }) compact = false; /** * Form default mode, will force override `layout`, `firstVisual`, `liveValidate` parameters * @@ -177,11 +175,11 @@ export class SFComponent implements OnInit, OnChanges, OnDestroy { /** * Whether to load status,when `true` reset button is disabled status, submit button is loading status */ - @Input() @InputBoolean() loading = false; - @Input() @InputBoolean() disabled = false; - @Input() @InputBoolean() noColon = false; - @Input() @InputBoolean() cleanValue = false; - @Input() @InputBoolean() delay = false; + @Input({ transform: booleanAttribute }) loading = false; + @Input({ transform: booleanAttribute }) disabled = false; + @Input({ transform: booleanAttribute }) noColon = false; + @Input({ transform: booleanAttribute }) cleanValue = false; + @Input({ transform: booleanAttribute }) delay = false; @Output() readonly formValueChange = new EventEmitter(); @Output() readonly formChange = new EventEmitter>(); @Output() readonly formSubmit = new EventEmitter>(); @@ -305,17 +303,7 @@ export class SFComponent implements OnInit, OnChanges, OnDestroy { this.formSubmit.emit(this.value); } - constructor( - private formPropertyFactory: FormPropertyFactory, - private terminator: TerminatorService, - private dom: DomSanitizer, - private cdr: ChangeDetectorRef, - private localeSrv: DelonLocaleService, - @Optional() private aclSrv: ACLService, - @Optional() @Inject(ALAIN_I18N_TOKEN) private i18nSrv: AlainI18NService, - cogSrv: AlainConfigService, - private platform: Platform - ) { + constructor(cogSrv: AlainConfigService) { this.options = mergeConfig(cogSrv); this.liveValidate = this.options.liveValidate as boolean; this.firstVisual = this.options.firstVisual as boolean; diff --git a/packages/form/src/utils.ts b/packages/form/src/utils.ts index 3267fa310..702053f63 100644 --- a/packages/form/src/utils.ts +++ b/packages/form/src/utils.ts @@ -1,6 +1,5 @@ import { Observable, of, map } from 'rxjs'; -import { toBoolean } from '@delon/util/decorator'; import { deepCopy } from '@delon/util/other'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzI18nService } from 'ng-zorro-antd/i18n'; @@ -15,7 +14,7 @@ export function isBlank(o: NzSafeAny): boolean { } export function toBool(value: NzSafeAny, defaultValue: boolean): boolean { - return toBoolean(value, defaultValue)!!; + return value == null ? defaultValue : `${value}` !== 'false'; } export function di(ui: SFUISchema, ...args: NzSafeAny[]): void { diff --git a/packages/form/src/validator.factory.ts b/packages/form/src/validator.factory.ts index 0876605d6..27004a242 100644 --- a/packages/form/src/validator.factory.ts +++ b/packages/form/src/validator.factory.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable, NgZone } from '@angular/core'; +import { Injectable, NgZone, inject } from '@angular/core'; import Ajv from 'ajv'; import addFormats from 'ajv-formats'; @@ -22,18 +22,18 @@ export abstract class SchemaValidatorFactory { @Injectable() export class AjvSchemaValidatorFactory extends SchemaValidatorFactory { + private readonly ngZone = inject(NgZone); + private readonly cogSrv = inject(AlainConfigService); + protected ajv!: Ajv; protected options!: AlainSFConfig; - constructor( - @Inject(AlainConfigService) cogSrv: AlainConfigService, - private ngZone: NgZone - ) { + constructor() { super(); if (!(typeof document === 'object' && !!document)) { return; } - this.options = mergeConfig(cogSrv); + this.options = mergeConfig(this.cogSrv); const customOptions = this.options.ajv || {}; this.ngZone.runOutsideAngular(() => { this.ajv = new Ajv({ diff --git a/packages/form/src/widget.factory.ts b/packages/form/src/widget.factory.ts index 6af8478b1..cb8c9b42a 100644 --- a/packages/form/src/widget.factory.ts +++ b/packages/form/src/widget.factory.ts @@ -1,4 +1,4 @@ -import { ComponentRef, Injectable, ViewContainerRef } from '@angular/core'; +import { ComponentRef, Injectable, ViewContainerRef, inject } from '@angular/core'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -37,7 +37,7 @@ export class WidgetRegistry { @Injectable() export class WidgetFactory { - constructor(private registry: WidgetRegistry) {} + private readonly registry = inject(WidgetRegistry); createWidget(container: ViewContainerRef, type: string): ComponentRef> { if (!this.registry.has(type)) { diff --git a/packages/form/src/widget.ts b/packages/form/src/widget.ts index 1a3f7e8f9..6f1925589 100644 --- a/packages/form/src/widget.ts +++ b/packages/form/src/widget.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, ChangeDetectorRef, Directive, HostBinding, Inject, Injector } from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, Directive, HostBinding, Injector, inject } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { takeUntil } from 'rxjs'; @@ -19,6 +19,11 @@ import type { SFArrayWidgetSchema, SFObjectWidgetSchema } from './widgets'; @Directive() export abstract class Widget implements AfterViewInit { + readonly cd = inject(ChangeDetectorRef); + readonly injector = inject(Injector); + readonly sfItemComp = inject(SFItemComponent); + readonly sfComp = inject(SFComponent); + formProperty!: T; error?: string; showError = false; @@ -55,13 +60,6 @@ export abstract class Widget return this.sfComp?.cleanValue!; } - constructor( - @Inject(ChangeDetectorRef) public readonly cd: ChangeDetectorRef, - @Inject(Injector) public readonly injector: Injector, - @Inject(SFItemComponent) public readonly sfItemComp?: SFItemComponent, - @Inject(SFComponent) public readonly sfComp?: SFComponent - ) {} - ngAfterViewInit(): void { this.formProperty.errorsChanges .pipe(takeUntil(this.sfItemComp!.destroy$)) diff --git a/packages/form/src/widgets/custom/sf-template.directive.ts b/packages/form/src/widgets/custom/sf-template.directive.ts index c4dbf5b8e..5e03c8b6f 100644 --- a/packages/form/src/widgets/custom/sf-template.directive.ts +++ b/packages/form/src/widgets/custom/sf-template.directive.ts @@ -1,4 +1,4 @@ -import { Directive, Input, OnInit, TemplateRef } from '@angular/core'; +import { Directive, Input, OnInit, TemplateRef, inject } from '@angular/core'; import { SF_SEQ } from '../../const'; import { SFComponent } from '../../sf.component'; @@ -7,12 +7,10 @@ import { SFComponent } from '../../sf.component'; selector: '[sf-template]' }) export class SFTemplateDirective implements OnInit { - @Input('sf-template') path!: string; + private readonly table = inject(SFComponent); + private readonly templateRef = inject(TemplateRef); - constructor( - private templateRef: TemplateRef, - private table: SFComponent - ) {} + @Input('sf-template') path!: string; ngOnInit(): void { this.table._addTpl(this.path.startsWith(SF_SEQ) ? this.path : SF_SEQ + this.path, this.templateRef); diff --git a/packages/theme/layout-default/layout-header.component.ts b/packages/theme/layout-default/layout-header.component.ts index 7e398730a..4ece252d3 100644 --- a/packages/theme/layout-default/layout-header.component.ts +++ b/packages/theme/layout-default/layout-header.component.ts @@ -71,7 +71,10 @@ interface LayoutDefaultHeaderItem { changeDetection: ChangeDetectionStrategy.OnPush }) export class LayoutDefaultHeaderComponent implements AfterViewInit { - private destroy$ = inject(DestroyRef); + private readonly settings = inject(SettingsService); + private readonly srv = inject(LayoutDefaultService); + private readonly cdr = inject(ChangeDetectorRef); + private readonly destroy$ = inject(DestroyRef); @Input() items!: QueryList; @@ -95,12 +98,6 @@ export class LayoutDefaultHeaderComponent implements AfterViewInit { return this.srv.collapsedIcon; } - constructor( - private srv: LayoutDefaultService, - private settings: SettingsService, - private cdr: ChangeDetectorRef - ) {} - private refresh(): void { const arr = this.items.toArray(); this.left = arr.filter(i => i.direction === 'left'); diff --git a/packages/theme/layout-default/layout-nav.component.ts b/packages/theme/layout-default/layout-nav.component.ts index ded2965b3..b432f2882 100644 --- a/packages/theme/layout-default/layout-nav.component.ts +++ b/packages/theme/layout-default/layout-nav.component.ts @@ -6,16 +6,16 @@ import { Component, DestroyRef, EventEmitter, - Inject, Input, NgZone, OnDestroy, OnInit, - Optional, Output, Renderer2, ViewEncapsulation, - inject + booleanAttribute, + inject, + numberAttribute } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; @@ -23,9 +23,8 @@ import { NavigationEnd, Router } from '@angular/router'; import { filter } from 'rxjs'; import { Menu, MenuIcon, MenuInner, MenuService, SettingsService } from '@delon/theme'; -import { BooleanInput, InputBoolean, InputNumber, NumberInput, ZoneOutside } from '@delon/util/decorator'; +import { ZoneOutside } from '@delon/util/decorator'; import { WINDOW } from '@delon/util/token'; -import type { NzSafeAny } from 'ng-zorro-antd/core/types'; export interface Nav extends MenuInner { _needIcon?: boolean; @@ -48,48 +47,38 @@ const FLOATINGCLS = 'sidebar-nav__floating'; encapsulation: ViewEncapsulation.None }) export class LayoutDefaultNavComponent implements OnInit, OnDestroy { - static ngAcceptInputType_disabledAcl: BooleanInput; - static ngAcceptInputType_autoCloseUnderPad: BooleanInput; - static ngAcceptInputType_recursivePath: BooleanInput; - static ngAcceptInputType_hideEmptyChildren: BooleanInput; - static ngAcceptInputType_openStrictly: BooleanInput; - static ngAcceptInputType_maxLevelIcon: NumberInput; + private readonly doc = inject(DOCUMENT); + private readonly win = inject(WINDOW); + private readonly router = inject(Router); + private readonly render = inject(Renderer2); + private readonly menuSrv = inject(MenuService); + private readonly settings = inject(SettingsService); + private readonly cdr = inject(ChangeDetectorRef); + private readonly ngZone = inject(NgZone); + private readonly sanitizer = inject(DomSanitizer); + private readonly directionality = inject(Directionality, { optional: true }); private bodyEl!: HTMLBodyElement; private destroy$ = inject(DestroyRef); private floatingEl!: HTMLDivElement; - dir: Direction = 'ltr'; + dir?: Direction = 'ltr'; list: Nav[] = []; - @Input() @InputBoolean() disabledAcl = false; - @Input() @InputBoolean() autoCloseUnderPad = true; - @Input() @InputBoolean() recursivePath = true; - @Input() @InputBoolean() hideEmptyChildren = true; - @Input() - @InputBoolean() + @Input({ transform: booleanAttribute }) disabledAcl = false; + @Input({ transform: booleanAttribute }) autoCloseUnderPad = true; + @Input({ transform: booleanAttribute }) recursivePath = true; + @Input({ transform: booleanAttribute }) hideEmptyChildren = true; + @Input({ transform: booleanAttribute }) set openStrictly(value: boolean) { this.menuSrv.openStrictly = value; } - @Input() @InputNumber() maxLevelIcon = 3; + @Input({ transform: numberAttribute }) maxLevelIcon = 3; @Output() readonly select = new EventEmitter(); get collapsed(): boolean { return this.settings.layout.collapsed; } - constructor( - private menuSrv: MenuService, - private settings: SettingsService, - private router: Router, - private render: Renderer2, - private cdr: ChangeDetectorRef, - private ngZone: NgZone, - private sanitizer: DomSanitizer, - @Inject(DOCUMENT) private doc: NzSafeAny, - @Inject(WINDOW) private win: NzSafeAny, - @Optional() private directionality: Directionality - ) {} - private getLinkNode(node: HTMLElement): HTMLElement | null { node = node.nodeName === 'A' ? node : (node.parentNode as HTMLElement); return node.nodeName !== 'A' ? null : node; @@ -234,7 +223,7 @@ export class LayoutDefaultNavComponent implements OnInit, OnDestroy { ngOnInit(): void { const { doc, router, menuSrv, settings, cdr } = this; - this.bodyEl = doc.querySelector('body'); + this.bodyEl = doc.querySelector('body')!; menuSrv.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(data => { menuSrv.visit(data, (i: Nav, _p, depth) => { i._text = this.sanitizer.bypassSecurityTrustHtml(i.text!); @@ -270,8 +259,8 @@ export class LayoutDefaultNavComponent implements OnInit, OnDestroy { .subscribe(() => this.clearFloating()); this.underPad(); - this.dir = this.directionality.value; - this.directionality.change?.pipe(takeUntilDestroyed(this.destroy$)).subscribe((direction: Direction) => { + this.dir = this.directionality?.value; + this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { this.dir = direction; this.cdr.detectChanges(); }); diff --git a/packages/theme/layout-default/layout-top-menu-item.ts b/packages/theme/layout-default/layout-top-menu-item.ts index 37893e16b..ffe87cec8 100644 --- a/packages/theme/layout-default/layout-top-menu-item.ts +++ b/packages/theme/layout-default/layout-top-menu-item.ts @@ -1,6 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation } from '@angular/core'; - -import { BooleanInput, InputBoolean } from '@delon/util/decorator'; +import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation, booleanAttribute } from '@angular/core'; @Component({ selector: 'layout-default-top-menu-item', @@ -16,9 +14,6 @@ import { BooleanInput, InputBoolean } from '@delon/util/decorator'; encapsulation: ViewEncapsulation.None }) export class LayoutDefaultTopMenuItemComponent { - static ngAcceptInputType_selected: BooleanInput; - static ngAcceptInputType_disabled: BooleanInput; - - @Input() @InputBoolean() selected = false; - @Input() @InputBoolean() disabled = false; + @Input({ transform: booleanAttribute }) selected = false; + @Input({ transform: booleanAttribute }) disabled = false; } diff --git a/packages/theme/layout-default/layout.component.ts b/packages/theme/layout-default/layout.component.ts index 7ce18e789..ce8d72016 100644 --- a/packages/theme/layout-default/layout.component.ts +++ b/packages/theme/layout-default/layout.component.ts @@ -7,7 +7,8 @@ import { Input, QueryList, Renderer2, - TemplateRef + TemplateRef, + booleanAttribute } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { @@ -23,7 +24,6 @@ import { filter } from 'rxjs'; import { SettingsService } from '@delon/theme'; import { updateHostClass } from '@delon/util/browser'; -import { BooleanInput, InputBoolean } from '@delon/util/decorator'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzMessageService } from 'ng-zorro-antd/message'; @@ -72,9 +72,6 @@ import { LayoutDefaultOptions } from './types'; ` }) export class LayoutDefaultComponent { - static ngAcceptInputType_fetchingStrictly: BooleanInput; - static ngAcceptInputType_fetching: BooleanInput; - @ContentChildren(LayoutDefaultHeaderItemComponent, { descendants: false }) headerItems!: QueryList; @@ -91,8 +88,8 @@ export class LayoutDefaultComponent { @Input() nav: TemplateRef | null = null; @Input() content: TemplateRef | null = null; @Input() customError?: string | null; - @Input() @InputBoolean() fetchingStrictly = false; - @Input() @InputBoolean() fetching = false; + @Input({ transform: booleanAttribute }) fetchingStrictly = false; + @Input({ transform: booleanAttribute }) fetching = false; private isFetching = false; diff --git a/packages/theme/layout-default/layout.service.ts b/packages/theme/layout-default/layout.service.ts index e788040fa..ffb7129ac 100644 --- a/packages/theme/layout-default/layout.service.ts +++ b/packages/theme/layout-default/layout.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { SettingsService } from '@delon/theme'; @@ -17,6 +17,7 @@ const DEFAULT: LayoutDefaultOptions = { @Injectable({ providedIn: 'root' }) export class LayoutDefaultService { + private readonly settings = inject(SettingsService); private _options$ = new BehaviorSubject(DEFAULT); private _options: LayoutDefaultOptions = DEFAULT; @@ -37,8 +38,6 @@ export class LayoutDefaultService { return `menu-${type}`; } - constructor(private settings: SettingsService) {} - private notify(): void { this._options$.next(this._options); } diff --git a/packages/theme/setting-drawer/setting-drawer-item.component.html b/packages/theme/setting-drawer/setting-drawer-item.component.html index ed924ca80..fbfbdb0e5 100644 --- a/packages/theme/setting-drawer/setting-drawer-item.component.html +++ b/packages/theme/setting-drawer/setting-drawer-item.component.html @@ -4,16 +4,10 @@ @switch (i.type) { @case ('color') { - + } @case ('input') { - + } @case ('px') { { + this.dir = this.directionality?.value; + this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(direction => { this.dir = direction; this.cdr.detectChanges(); }); diff --git a/packages/theme/src/locale/locale.tokens.ts b/packages/theme/src/locale/locale.tokens.ts index e2c19a9cd..97beea2db 100644 --- a/packages/theme/src/locale/locale.tokens.ts +++ b/packages/theme/src/locale/locale.tokens.ts @@ -1,3 +1,5 @@ import { InjectionToken } from '@angular/core'; -export const DELON_LOCALE = new InjectionToken('delon-locale'); +import type { FullLocaleData } from './locale.types'; + +export const DELON_LOCALE = new InjectionToken('delon-locale'); diff --git a/packages/theme/src/pipes/safe/html.pipe.ts b/packages/theme/src/pipes/safe/html.pipe.ts index 5ffa95b8b..8d1e35a24 100644 --- a/packages/theme/src/pipes/safe/html.pipe.ts +++ b/packages/theme/src/pipes/safe/html.pipe.ts @@ -1,9 +1,9 @@ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; @Pipe({ name: 'html', standalone: true }) export class HTMLPipe implements PipeTransform { - constructor(private dom: DomSanitizer) {} + private readonly dom = inject(DomSanitizer); transform(html: string): string | SafeHtml { return html ? this.dom.bypassSecurityTrustHtml(html) : ''; diff --git a/packages/theme/src/pipes/safe/url.pipe.ts b/packages/theme/src/pipes/safe/url.pipe.ts index 45393e2c8..a32d0c266 100644 --- a/packages/theme/src/pipes/safe/url.pipe.ts +++ b/packages/theme/src/pipes/safe/url.pipe.ts @@ -1,9 +1,9 @@ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; @Pipe({ name: 'url', standalone: true }) export class URLPipe implements PipeTransform { - constructor(private dom: DomSanitizer) {} + private readonly dom = inject(DomSanitizer); transform(url: string): string | SafeUrl { return url ? this.dom.bypassSecurityTrustUrl(url) : ''; diff --git a/packages/theme/src/pipes/yn/yn.pipe.ts b/packages/theme/src/pipes/yn/yn.pipe.ts index 85be8d90d..335f5321d 100644 --- a/packages/theme/src/pipes/yn/yn.pipe.ts +++ b/packages/theme/src/pipes/yn/yn.pipe.ts @@ -1,4 +1,4 @@ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; export type YNMode = 'full' | 'icon' | 'text'; @@ -37,7 +37,7 @@ export function yn(value: boolean, opt?: YNOptions): string { @Pipe({ name: 'yn', standalone: true }) export class YNPipe implements PipeTransform { - constructor(private dom: DomSanitizer) {} + private readonly dom = inject(DomSanitizer); transform(value: boolean, yes?: string, no?: string, mode?: YNMode, isSafeHtml: boolean = true): SafeHtml { const html = yn(value, { yes, no, mode }); diff --git a/packages/theme/src/services/drawer/drawer.helper.ts b/packages/theme/src/services/drawer/drawer.helper.ts index 5a916f0ff..43c3b89b4 100644 --- a/packages/theme/src/services/drawer/drawer.helper.ts +++ b/packages/theme/src/services/drawer/drawer.helper.ts @@ -1,4 +1,4 @@ -import { Injectable, Optional, SkipSelf, TemplateRef, Type } from '@angular/core'; +import { Injectable, TemplateRef, Type, inject } from '@angular/core'; import { Observable, Observer } from 'rxjs'; import { deepMerge } from '@delon/util/other'; @@ -50,16 +50,13 @@ export interface DrawerHelperOptions { */ @Injectable({ providedIn: 'root' }) export class DrawerHelper { + private readonly srv = inject(NzDrawerService); + private readonly parentDrawer = inject(DrawerHelper, { optional: true, skipSelf: true }); private openDrawersAtThisLevel: NzDrawerRef[] = []; get openDrawers(): NzDrawerRef[] { return this.parentDrawer ? this.parentDrawer.openDrawers : this.openDrawersAtThisLevel; } - constructor( - private srv: NzDrawerService, - @Optional() @SkipSelf() private parentDrawer: DrawerHelper - ) {} - /** * 构建一个抽屉 */ diff --git a/packages/theme/src/services/http/http.client.ts b/packages/theme/src/services/http/http.client.ts index 66f8a4a13..a2240bfb9 100644 --- a/packages/theme/src/services/http/http.client.ts +++ b/packages/theme/src/services/http/http.client.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { HttpClient, HttpContext, HttpEvent, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, of, delay, finalize, switchMap, tap } from 'rxjs'; import { AlainConfigService, AlainThemeHttpClientConfig } from '@delon/util/config'; @@ -17,11 +17,9 @@ export type HttpObserve = 'body' | 'events' | 'response'; */ @Injectable({ providedIn: 'root' }) export class _HttpClient { + private readonly http = inject(HttpClient); private cog: AlainThemeHttpClientConfig; - constructor( - private http: HttpClient, - cogSrv: AlainConfigService - ) { + constructor(cogSrv: AlainConfigService) { this.cog = cogSrv.merge('themeHttp', { nullValueHandling: 'include', dateValueHandling: 'timestamp' diff --git a/packages/theme/src/services/http/http.decorator.spec.ts b/packages/theme/src/services/http/http.decorator.spec.ts index 8d580559c..9446f822e 100644 --- a/packages/theme/src/services/http/http.decorator.spec.ts +++ b/packages/theme/src/services/http/http.decorator.spec.ts @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { HttpParams } from '@angular/common/http'; +import { Injectable, Injector } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; import { Observable } from 'rxjs'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -27,6 +29,7 @@ import { @BaseUrl('/user') @BaseHeaders({ bh: 'a' }) +@Injectable() class MockService extends BaseApi { @GET() query(@Query('pi') _pi: number, @Query('ps') _ps: number, @Headers('mh') _mh: string): Observable { @@ -124,6 +127,7 @@ class MockService extends BaseApi { } } +@Injectable() class MockEmptyService extends BaseApi { @GET() GET(): Observable { @@ -138,7 +142,6 @@ class MockEmptyService extends BaseApi { describe('theme: http.decorator', () => { let request: jasmine.Spy; let srv: MockService; - let injector: MockInjector; let tokens: any; class MockInjector { @@ -162,8 +165,10 @@ describe('theme: http.decorator', () => { jasmine.createSpyObj('http', { request }); - injector = new MockInjector(); - srv = new MockService(injector as any); + TestBed.configureTestingModule({ + providers: [{ provide: Injector, useClass: MockInjector }, MockService, MockEmptyService] + }); + srv = TestBed.inject(MockService); }); it('should working', () => { @@ -193,7 +198,7 @@ describe('theme: http.decorator', () => { }); it('should be ingore url & base url', () => { - const srvEmpty = new MockEmptyService(injector as any); + const srvEmpty = TestBed.inject(MockEmptyService); srvEmpty.GET(); expect(request).toHaveBeenCalled(); @@ -209,7 +214,7 @@ describe('theme: http.decorator', () => { describe('should be join baseUrl & url of method', () => { it('when without baseUrl', () => { - const srv2 = new MockEmptyService(injector as any); + const srv2 = TestBed.inject(MockEmptyService); srv2.A(); expect(request).toHaveBeenCalled(); diff --git a/packages/theme/src/services/http/http.decorator.ts b/packages/theme/src/services/http/http.decorator.ts index cdbe4ea87..30acf3cf1 100644 --- a/packages/theme/src/services/http/http.decorator.ts +++ b/packages/theme/src/services/http/http.decorator.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { HttpHeaders, HttpContext } from '@angular/common/http'; -import { Inject, Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { Observable, throwError } from 'rxjs'; import { ACLService, ACLCanType } from '@delon/acl'; @@ -17,7 +17,7 @@ import { _HttpClient } from './http.client'; */ @Injectable() export abstract class BaseApi { - constructor(@Inject(Injector) protected injector: Injector) {} + protected readonly injector = inject(Injector); } export interface HttpOptions { diff --git a/packages/theme/src/services/i18n/i18n-url.guard.ts b/packages/theme/src/services/i18n/i18n-url.guard.ts index 21c4b6345..445e4d9d7 100644 --- a/packages/theme/src/services/i18n/i18n-url.guard.ts +++ b/packages/theme/src/services/i18n/i18n-url.guard.ts @@ -1,24 +1,20 @@ -import { Inject, Injectable, Optional, inject } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivateChildFn, CanActivateFn } from '@angular/router'; import { Observable, of } from 'rxjs'; import { AlainConfigService } from '@delon/util/config'; -import { AlainI18NService, ALAIN_I18N_TOKEN } from './i18n'; +import { ALAIN_I18N_TOKEN } from './i18n'; @Injectable({ providedIn: 'root' }) export class AlainI18NGuardService { - constructor( - @Optional() - @Inject(ALAIN_I18N_TOKEN) - private i18nSrv: AlainI18NService, - private cogSrv: AlainConfigService - ) {} + private readonly i18nSrv = inject(ALAIN_I18N_TOKEN, { optional: true }); + private readonly cogSrv = inject(AlainConfigService); process(route: ActivatedRouteSnapshot): Observable { const lang = route.params && route.params[this.cogSrv.get('themeI18n')?.paramNameOfUrlGuard ?? 'i18n']; if (lang != null) { - this.i18nSrv.use(lang); + this.i18nSrv?.use(lang); } return of(true); } diff --git a/packages/theme/src/services/menu/menu.service.ts b/packages/theme/src/services/menu/menu.service.ts index 162cb4c60..36e8cefc3 100644 --- a/packages/theme/src/services/menu/menu.service.ts +++ b/packages/theme/src/services/menu/menu.service.ts @@ -1,32 +1,29 @@ -import { Inject, Injectable, OnDestroy, Optional } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { BehaviorSubject, Observable, Subscription, share } from 'rxjs'; import { ACLService } from '@delon/acl'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { Menu, MenuIcon, MenuInner } from './interface'; -import { AlainI18NService, ALAIN_I18N_TOKEN } from '../i18n/i18n'; +import { ALAIN_I18N_TOKEN } from '../i18n/i18n'; /** * 菜单服务,[在线文档](https://ng-alain.com/theme/menu) */ @Injectable({ providedIn: 'root' }) export class MenuService implements OnDestroy { + private readonly i18nSrv = inject(ALAIN_I18N_TOKEN, { optional: true }); + private readonly aclService = inject(ACLService, { optional: true }); private _change$: BehaviorSubject = new BehaviorSubject([]); - private i18n$: Subscription; + private i18n$?: Subscription; private data: Menu[] = []; /** * 是否完全受控菜单打开状态,默认:`false` */ openStrictly = false; - constructor( - @Optional() - @Inject(ALAIN_I18N_TOKEN) - private i18nSrv: AlainI18NService, - @Optional() private aclService: ACLService - ) { - this.i18n$ = this.i18nSrv.change.subscribe(() => this.resume()); + constructor() { + this.i18n$ = this.i18nSrv?.change.subscribe(() => this.resume()); } get change(): Observable { @@ -348,6 +345,6 @@ export class MenuService implements OnDestroy { ngOnDestroy(): void { this._change$.unsubscribe(); - this.i18n$.unsubscribe(); + this.i18n$?.unsubscribe(); } } diff --git a/packages/theme/src/services/modal/modal.helper.ts b/packages/theme/src/services/modal/modal.helper.ts index 8ea063c08..c2fca011c 100644 --- a/packages/theme/src/services/modal/modal.helper.ts +++ b/packages/theme/src/services/modal/modal.helper.ts @@ -1,6 +1,6 @@ import { DragDrop, DragRef } from '@angular/cdk/drag-drop'; import { DOCUMENT } from '@angular/common'; -import { Inject, Injectable, TemplateRef, Type } from '@angular/core'; +import { Injectable, TemplateRef, Type, inject } from '@angular/core'; import { Observable, Observer, filter, take } from 'rxjs'; import { deepMerge } from '@delon/util/other'; @@ -40,18 +40,12 @@ export interface ModalHelperDragOptions { */ @Injectable({ providedIn: 'root' }) export class ModalHelper { - private document: Document; - - constructor( - private srv: NzModalService, - private drag: DragDrop, - @Inject(DOCUMENT) doc: NzSafeAny - ) { - this.document = doc; - } + private readonly srv = inject(NzModalService); + private readonly drag = inject(DragDrop); + private readonly doc = inject(DOCUMENT); private createDragRef(options: ModalHelperDragOptions, wrapCls: string): DragRef { - const wrapEl = this.document.querySelector(wrapCls) as HTMLDivElement; + const wrapEl = this.doc.querySelector(wrapCls) as HTMLDivElement; const modalEl = wrapEl.firstChild as HTMLDivElement; const handelEl = options.handleCls ? wrapEl.querySelector(options.handleCls) : null; if (handelEl) { diff --git a/packages/theme/src/services/rtl/rtl.service.ts b/packages/theme/src/services/rtl/rtl.service.ts index 08626963b..ae1993e5c 100644 --- a/packages/theme/src/services/rtl/rtl.service.ts +++ b/packages/theme/src/services/rtl/rtl.service.ts @@ -1,7 +1,7 @@ import { Direction, Directionality } from '@angular/cdk/bidi'; import { Platform } from '@angular/cdk/platform'; import { DOCUMENT } from '@angular/common'; -import { Inject, Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, filter, map } from 'rxjs'; import { AlainConfigService } from '@delon/util/config'; @@ -19,6 +19,13 @@ export const RTL = 'rtl'; @Injectable({ providedIn: 'root' }) export class RTLService { + private readonly d = inject(Directionality); + private readonly nz = inject(NzConfigService); + private readonly delon = inject(AlainConfigService); + private readonly platform = inject(Platform); + private readonly doc = inject(DOCUMENT); + private readonly srv = inject(SettingsService); + private _dir: Direction = LTR; /** * Get or Set the current text direction @@ -61,15 +68,8 @@ export class RTLService { ); } - constructor( - private d: Directionality, - private srv: SettingsService, - private nz: NzConfigService, - private delon: AlainConfigService, - private platform: Platform, - @Inject(DOCUMENT) private doc: NzSafeAny - ) { - this.dir = srv.layout.direction === RTL ? RTL : LTR; + constructor() { + this.dir = this.srv.layout.direction === RTL ? RTL : LTR; } /** diff --git a/packages/theme/src/services/settings/settings.service.ts b/packages/theme/src/services/settings/settings.service.ts index 54747e97d..4baed24b4 100644 --- a/packages/theme/src/services/settings/settings.service.ts +++ b/packages/theme/src/services/settings/settings.service.ts @@ -1,5 +1,5 @@ import { Platform } from '@angular/cdk/platform'; -import { Inject, Injectable, InjectionToken, Provider } from '@angular/core'; +import { Injectable, InjectionToken, Provider, inject } from '@angular/core'; import { Observable, Subject } from 'rxjs'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -27,16 +27,14 @@ export const ALAIN_SETTING_DEFAULT: Provider = { @Injectable({ providedIn: 'root' }) export class SettingsService { + private readonly KEYS = inject(ALAIN_SETTING_KEYS); + private readonly platform = inject(Platform); + private notify$ = new Subject(); private _app: A | null = null; private _user: U | null = null; private _layout: L | null = null; - constructor( - private platform: Platform, - @Inject(ALAIN_SETTING_KEYS) private KEYS: SettingsKeys - ) {} - getData(key: string): NzSafeAny { if (!this.platform.isBrowser) { return null; diff --git a/packages/theme/src/services/title/title.service.ts b/packages/theme/src/services/title/title.service.ts index 0d6dbead6..8c55b5686 100644 --- a/packages/theme/src/services/title/title.service.ts +++ b/packages/theme/src/services/title/title.service.ts @@ -1,13 +1,11 @@ import { DOCUMENT } from '@angular/common'; -import { DestroyRef, Inject, Injectable, Injector, OnDestroy, Optional, inject } from '@angular/core'; +import { DestroyRef, Injectable, Injector, OnDestroy, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { Title } from '@angular/platform-browser'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable, of, map, delay, isObservable, switchMap, Subscription } from 'rxjs'; -import type { NzSafeAny } from 'ng-zorro-antd/core/types'; - -import { AlainI18NService, ALAIN_I18N_TOKEN } from '../i18n/i18n'; +import { ALAIN_I18N_TOKEN } from '../i18n/i18n'; import { MenuService } from '../menu/menu.service'; export interface RouteTitle { @@ -26,16 +24,14 @@ export class TitleService implements OnDestroy { readonly DELAY_TIME = 25; - constructor( - private injector: Injector, - private title: Title, - private menuSrv: MenuService, - @Optional() - @Inject(ALAIN_I18N_TOKEN) - private i18nSrv: AlainI18NService, - @Inject(DOCUMENT) private doc: NzSafeAny - ) { - i18nSrv.change.pipe(takeUntilDestroyed()).subscribe(() => this.setTitle()); + private readonly doc = inject(DOCUMENT); + private readonly injector = inject(Injector); + private readonly title = inject(Title); + private readonly menuSrv = inject(MenuService); + private readonly i18nSrv = inject(ALAIN_I18N_TOKEN, { optional: true }); + + constructor() { + this.i18nSrv?.change.pipe(takeUntilDestroyed()).subscribe(() => this.setTitle()); } /** @@ -161,7 +157,7 @@ export class TitleService implements OnDestroy { * Set i18n key of the document title */ setTitleByI18n(key: string, params?: unknown): void { - this.setTitle(this.i18nSrv.fanyi(key, params)); + this.setTitle(this.i18nSrv?.fanyi(key, params)); } ngOnDestroy(): void { diff --git a/packages/theme/theme-btn/theme-btn.component.ts b/packages/theme/theme-btn/theme-btn.component.ts index 9ecf31f48..4f9388b30 100644 --- a/packages/theme/theme-btn/theme-btn.component.ts +++ b/packages/theme/theme-btn/theme-btn.component.ts @@ -5,21 +5,20 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + DestroyRef, EventEmitter, - Inject, + inject, InjectionToken, Input, isDevMode, OnDestroy, OnInit, - Optional, Output, Renderer2 } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { AlainConfigService } from '@delon/util/config'; -import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzDropDownDirective, NzDropdownMenuComponent } from 'ng-zorro-antd/dropdown'; import { NzMenuDirective, NzMenuItemComponent } from 'ng-zorro-antd/menu'; import { NzTooltipDirective } from 'ng-zorro-antd/tooltip'; @@ -43,6 +42,14 @@ export const ALAIN_THEME_BTN_KEYS = new InjectionToken('ALAIN_THEME_BTN_ imports: [NzDropDownDirective, NzDropdownMenuComponent, NzMenuDirective, NzMenuItemComponent, NzTooltipDirective] }) export class ThemeBtnComponent implements OnInit, OnDestroy { + private readonly doc = inject(DOCUMENT); + private readonly platform = inject(Platform); + private readonly renderer = inject(Renderer2); + private readonly configSrv = inject(AlainConfigService); + private readonly directionality = inject(Directionality, { optional: true }); + private readonly cdr = inject(ChangeDetectorRef); + private readonly destroy$ = inject(DestroyRef); + private theme = 'default'; isDev = isDevMode(); @Input() types: ThemeBtnType[] = [ @@ -53,25 +60,12 @@ export class ThemeBtnComponent implements OnInit, OnDestroy { @Input() devTips = `When the dark.css file can't be found, you need to run it once: npm run theme`; @Input() deployUrl = ''; @Output() readonly themeChange = new EventEmitter(); - private dir$ = this.directionality.change?.pipe(takeUntilDestroyed()); - dir: Direction = 'ltr'; - private key = ''; - - constructor( - private renderer: Renderer2, - private configSrv: AlainConfigService, - private platform: Platform, - @Inject(DOCUMENT) private doc: NzSafeAny, - @Optional() private directionality: Directionality, - @Optional() @Inject(ALAIN_THEME_BTN_KEYS) KEYS: string, - private cdr: ChangeDetectorRef - ) { - this.key = KEYS ?? 'site-theme'; - } + dir?: Direction = 'ltr'; + private key = inject(ALAIN_THEME_BTN_KEYS, { optional: true }) ?? 'site-theme'; ngOnInit(): void { - this.dir = this.directionality.value; - this.dir$.subscribe((direction: Direction) => { + this.dir = this.directionality?.value; + this.directionality?.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe((direction: Direction) => { this.dir = direction; this.cdr.detectChanges(); }); diff --git a/packages/util/browser/cookie.service.ts b/packages/util/browser/cookie.service.ts index b53438ce8..4dab41efb 100644 --- a/packages/util/browser/cookie.service.ts +++ b/packages/util/browser/cookie.service.ts @@ -1,6 +1,6 @@ import { Platform } from '@angular/cdk/platform'; import { DOCUMENT } from '@angular/common'; -import { Inject, Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -25,6 +25,9 @@ export interface CookieOptions { */ @Injectable({ providedIn: 'root' }) export class CookieService { + private readonly _doc = inject(DOCUMENT); + private readonly platform = inject(Platform); + private get doc(): Document { return this._doc || document; } @@ -38,11 +41,6 @@ export class CookieService { return this.platform.isBrowser ? this.doc.cookie : ''; } - constructor( - @Inject(DOCUMENT) private _doc: NzSafeAny, - private platform: Platform - ) {} - /** * Get all cookie key-value pairs * diff --git a/packages/util/browser/scroll.service.ts b/packages/util/browser/scroll.service.ts index 6462913d7..42faa75dc 100644 --- a/packages/util/browser/scroll.service.ts +++ b/packages/util/browser/scroll.service.ts @@ -1,11 +1,11 @@ import { Platform } from '@angular/cdk/platform'; import { DOCUMENT } from '@angular/common'; -import { Inject, Injectable } from '@angular/core'; - -import type { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { Injectable, inject } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class ScrollService { + private readonly _doc = inject(DOCUMENT); + private readonly platform = inject(Platform); private _getDoc(): Document { return this._doc || document; } @@ -15,17 +15,12 @@ export class ScrollService { return doc.defaultView || window; } - constructor( - @Inject(DOCUMENT) private _doc: NzSafeAny, - private platform: Platform - ) {} - /** * 获取滚动条位置 * * @param element 指定元素,默认 `window` */ - getScrollPosition(element?: Element | Window): [number, number] { + getScrollPosition(element?: Element | Window | null): [number, number] { if (!this.platform.isBrowser) { return [0, 0]; } diff --git a/packages/util/decorator/convert.ts b/packages/util/decorator/convert.ts index fa4fa2d19..29b1e45fb 100644 --- a/packages/util/decorator/convert.ts +++ b/packages/util/decorator/convert.ts @@ -53,6 +53,10 @@ export function toBoolean( } /** + * @deprecated Recommended to use the built-in `transform` and `static ngAcceptInputType_` can be removed + * - Use `@Input({ transform: booleanAttribute })` instead of `@InputBoolean()` + * - Use `@Input({ transform: (v: unknown) => (v == null ? null : booleanAttribute(v)) })` instead of `@InputBoolean(null)` + * * Input decorator that handle a prop to do get/set automatically with toBoolean * * ```ts @@ -71,6 +75,10 @@ export function toNumber(value: unknown, fallbackValue: number = 0): number { } /** + * @deprecated Recommended to use the built-in `transform` and `static ngAcceptInputType_` can be removed + * - Use `@Input({ transform: numberAttribute })` instead of `@InputNumber()` + * - Use `@Input({ transform: (v: unknown) => (v == null ? null : numberAttribute(v)) })` instead of `@InputNumber(null)` + * * Input decorator that handle a prop to do get/set automatically with toNumber * * ```ts diff --git a/packages/util/decorator/index.en-US.md b/packages/util/decorator/index.en-US.md index e24939add..137c71e2c 100644 --- a/packages/util/decorator/index.en-US.md +++ b/packages/util/decorator/index.en-US.md @@ -6,6 +6,8 @@ type: Tools ## toBoolean, @InputBoolean +> Recommended to use the built-in `transform` and `static ngAcceptInputType_` can be removed + Convert to `boolean`, `@InputBoolean()` can enhance the experience of binding number attributes, for example: ```ts @@ -24,6 +26,8 @@ The following calling methods will all be considered effective: ## toNumber, @InputNumber +> Recommended to use the built-in `transform` and `static ngAcceptInputType_` can be removed + Convert to `number`, `@InputNumber()` can enhance the experience of binding number attributes, for example: ```ts @@ -61,4 +65,4 @@ class MockClass { @ZoneRun() run(): void {} } -``` \ No newline at end of file +``` diff --git a/packages/util/decorator/index.zh-CN.md b/packages/util/decorator/index.zh-CN.md index 0023a08a9..5f708a44f 100644 --- a/packages/util/decorator/index.zh-CN.md +++ b/packages/util/decorator/index.zh-CN.md @@ -6,6 +6,8 @@ type: Tools ## toBoolean, @InputBoolean +> 建议使用内置的 `transform` 来替代,并且不再需要 `static ngAcceptInputType_` + 转换 `boolean` 属性,其中 `@InputBoolean()` 可以强化布尔属性绑定的体验,例如: ```ts @@ -24,6 +26,8 @@ type: Tools ## toNumber, @InputNumber +> 建议使用内置的 `transform` 来替代,并且不再需要 `static ngAcceptInputType_` + 转换 `number` 属性,其中 `@InputNumber()` 可以强化数字属性绑定的体验,例如: ```ts @@ -61,4 +65,4 @@ class MockClass { @ZoneRun() run(): void {} } -``` \ No newline at end of file +``` diff --git a/packages/util/decorator/zone-outside.ts b/packages/util/decorator/zone-outside.ts index aae24726d..f5d29219a 100644 --- a/packages/util/decorator/zone-outside.ts +++ b/packages/util/decorator/zone-outside.ts @@ -37,7 +37,7 @@ function makeFn(type: 'runOutsideAngular' | 'run', options?: ZoneOptions): Decor * * ```ts * class MockClass { - * constructor(public ngZone: NgZone) {} + * readonly ngZone = inject(NgZone); * * {AT}ZoneOutside() * runOutsideAngular(): void {} @@ -55,7 +55,7 @@ export function ZoneOutside(options?: ZoneOptions): DecoratorType { * * ```ts * class MockClass { - * constructor(public ngZone: NgZone) {} + * readonly ngZone = inject(NgZone); * * {AT}ZoneRun() * run(): void {} diff --git a/packages/util/format/currency.service.ts b/packages/util/format/currency.service.ts index dda446d70..10f6216be 100644 --- a/packages/util/format/currency.service.ts +++ b/packages/util/format/currency.service.ts @@ -1,5 +1,5 @@ import { CurrencyPipe, formatNumber } from '@angular/common'; -import { DEFAULT_CURRENCY_CODE, Inject, Injectable, LOCALE_ID } from '@angular/core'; +import { DEFAULT_CURRENCY_CODE, Injectable, LOCALE_ID, inject } from '@angular/core'; import { AlainConfigService, AlainUtilCurrencyConfig } from '@delon/util/config'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -14,15 +14,14 @@ import { @Injectable({ providedIn: 'root' }) export class CurrencyService { + private readonly locale = inject(LOCALE_ID); + private readonly defCurrencyCode = inject(DEFAULT_CURRENCY_CODE, { optional: true }) ?? 'USD'; + private c: AlainUtilCurrencyConfig; private readonly currencyPipe: CurrencyPipe; - constructor( - cog: AlainConfigService, - @Inject(LOCALE_ID) private locale: string, - @Inject(DEFAULT_CURRENCY_CODE) _defaultCurrencyCode: string = 'USD' - ) { - this.currencyPipe = new CurrencyPipe(locale, _defaultCurrencyCode); + constructor(cog: AlainConfigService) { + this.currencyPipe = new CurrencyPipe(this.locale, this.defCurrencyCode); this.c = cog.merge('utilCurrency', { startingUnit: 'yuan', megaUnit: { Q: '京', T: '兆', B: '亿', M: '万', K: '千' }, diff --git a/packages/util/other/lazy.service.ts b/packages/util/other/lazy.service.ts index 6133f9e1d..7cabc1f63 100644 --- a/packages/util/other/lazy.service.ts +++ b/packages/util/other/lazy.service.ts @@ -1,5 +1,5 @@ import { DOCUMENT } from '@angular/common'; -import { Inject, Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { BehaviorSubject, Observable, filter, share } from 'rxjs'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -28,12 +28,12 @@ export interface LazyLoadOptions { */ @Injectable({ providedIn: 'root' }) export class LazyService { + private readonly doc = inject(DOCUMENT); + private list: { [key: string]: boolean } = {}; private cached: { [key: string]: LazyResult } = {}; private _notify: BehaviorSubject = new BehaviorSubject([]); - constructor(@Inject(DOCUMENT) private doc: NzSafeAny) {} - get change(): Observable { return this._notify.asObservable().pipe( share(), diff --git a/packages/util/pipes/currency/cny.pipe.ts b/packages/util/pipes/currency/cny.pipe.ts index 7a1b48e96..226852ee4 100644 --- a/packages/util/pipes/currency/cny.pipe.ts +++ b/packages/util/pipes/currency/cny.pipe.ts @@ -1,16 +1,16 @@ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { CurrencyCNYOptions, CurrencyService } from '@delon/util/format'; +/** + * Converted into RMB notation. + * + * 转化成人民币表示法 + */ @Pipe({ name: 'cny', standalone: true }) export class CurrencyCNYPipe implements PipeTransform { - constructor(private srv: CurrencyService) {} + private readonly srv = inject(CurrencyService); - /** - * Converted into RMB notation. - * - * 转化成人民币表示法 - */ transform(value: number | string, options?: CurrencyCNYOptions): string { return this.srv.cny(value, options); } diff --git a/packages/util/pipes/currency/mega.pipe.ts b/packages/util/pipes/currency/mega.pipe.ts index 02dcfd77d..3be4853fb 100644 --- a/packages/util/pipes/currency/mega.pipe.ts +++ b/packages/util/pipes/currency/mega.pipe.ts @@ -1,22 +1,17 @@ -import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core'; +import { LOCALE_ID, Pipe, PipeTransform, inject } from '@angular/core'; import { CurrencyMegaOptions, CurrencyService } from '@delon/util/format'; +/** + * Large number format filter + * + * 大数据格式化 + */ @Pipe({ name: 'mega', standalone: true }) export class CurrencyMegaPipe implements PipeTransform { - private isCN = false; - constructor( - private srv: CurrencyService, - @Inject(LOCALE_ID) locale: string - ) { - this.isCN = locale.startsWith('zh'); - } + private readonly srv = inject(CurrencyService); + private isCN = inject(LOCALE_ID).startsWith('zh'); - /** - * Large number format filter - * - * 大数据格式化 - */ transform(value: number | string, options?: CurrencyMegaOptions): string { const res = this.srv.mega(value, options); return res.value + (this.isCN ? res.unitI18n : res.unit); diff --git a/packages/util/pipes/currency/price.pipe.ts b/packages/util/pipes/currency/price.pipe.ts index 35fafb29b..a2c86429c 100644 --- a/packages/util/pipes/currency/price.pipe.ts +++ b/packages/util/pipes/currency/price.pipe.ts @@ -1,19 +1,20 @@ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { CurrencyFormatOptions, CurrencyService } from '@delon/util/format'; +/** + * Format a number with commas as thousands separators + * + * 格式化货币,用逗号将数字格式化为千位分隔符 + * ```ts + * 10000 => `10,000` + * 10000.567 => `10,000.57` + * ``` + */ @Pipe({ name: 'price', standalone: true }) export class CurrencyPricePipe implements PipeTransform { - constructor(private srv: CurrencyService) {} - /** - * Format a number with commas as thousands separators - * - * 格式化货币,用逗号将数字格式化为千位分隔符 - * ```ts - * 10000 => `10,000` - * 10000.567 => `10,000.57` - * ``` - */ + private readonly srv = inject(CurrencyService); + transform(value: number | string, options?: CurrencyFormatOptions): string { return this.srv.format(value, options); } diff --git a/packages/util/pipes/filter/filter.pipe.ts b/packages/util/pipes/filter/filter.pipe.ts index 5603f97c1..340286abb 100644 --- a/packages/util/pipes/filter/filter.pipe.ts +++ b/packages/util/pipes/filter/filter.pipe.ts @@ -2,14 +2,14 @@ import { Pipe, PipeTransform } from '@angular/core'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; +/** + * Filter array + * + * 过滤数组 + */ // eslint-disable-next-line @angular-eslint/no-pipe-impure @Pipe({ name: 'filter', standalone: true, pure: false }) export class FilterPipe implements PipeTransform { - /** - * Filter array - * - * 过滤数组 - */ transform(array: readonly T[], matcher: (item: T, ...args: NzSafeAny[]) => boolean, ...args: NzSafeAny[]): T[] { return array.filter(i => matcher(i, ...args)); } diff --git a/packages/util/pipes/format/mask.pipe.ts b/packages/util/pipes/format/mask.pipe.ts index 86f38ea56..4b74af28c 100644 --- a/packages/util/pipes/format/mask.pipe.ts +++ b/packages/util/pipes/format/mask.pipe.ts @@ -2,27 +2,27 @@ import { Pipe, PipeTransform } from '@angular/core'; import { formatMask, FormatMaskOption } from '@delon/util/format'; +/** + * Format mask + * + * 格式化掩码 + * + * | 字符 | 描述 | + * | --- | --- | + * | `0` | 任意数字,若该位置字符不符合,则默认为 `0` 填充 | + * | `9` | 任意数字 | + * | `#` | 任意字符 | + * | `U` | 转换大写 | + * | `L` | 转换小写 | + * | `*` | 转换为 `*` 字符 | + * + * ```ts + * formatMask('123', '(###)') => (123) + * formatMask('15900000000', '999****9999') => 159****0000 + * ``` + */ @Pipe({ name: 'mask', standalone: true }) export class FormatMaskPipe implements PipeTransform { - /** - * Format mask - * - * 格式化掩码 - * - * | 字符 | 描述 | - * | --- | --- | - * | `0` | 任意数字,若该位置字符不符合,则默认为 `0` 填充 | - * | `9` | 任意数字 | - * | `#` | 任意字符 | - * | `U` | 转换大写 | - * | `L` | 转换小写 | - * | `*` | 转换为 `*` 字符 | - * - * ```ts - * formatMask('123', '(###)') => (123) - * formatMask('15900000000', '999****9999') => 159****0000 - * ``` - */ transform(value: string, mask: string | FormatMaskOption): string { return formatMask(value, mask); } diff --git a/schematics/edit/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template b/schematics/edit/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template index 54c1c1dbb..6ef94c2ec 100644 --- a/schematics/edit/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template +++ b/schematics/edit/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template @@ -1,4 +1,4 @@ -import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';<% if(!modal) { %> +import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%>, inject } from '@angular/core';<% if(!modal) { %> import { ActivatedRoute } from '@angular/router'; import { Location } from '@angular/common';<% } %> import { SFSchema, SFUISchema } from '@delon/form'; @@ -17,6 +17,12 @@ import { SHARED_IMPORTS } from '@shared';<%}%> changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %> }) export class <%= componentName %> implements OnInit { + private readonly http = inject(_HttpClient); + private readonly msgSrv = inject(NzMessageService);<% if(modal) { %> + private readonly modal = inject(NzModalRef);<% } else { %> + private readonly route = inject(ActivatedRoute); + readonly location = inject(Location);<% } %> + <% if(modal) { %>record: any = {};<% } else { %> id = this.route.snapshot.params.id;<% } %> i: any; @@ -47,14 +53,6 @@ export class <%= componentName %> implements OnInit { }, }; - constructor(<% if(modal) { %> - private modal: NzModalRef,<% } else { %> - private route: ActivatedRoute, - public location: Location,<% } %> - private msgSrv: NzMessageService, - public http: _HttpClient, - ) {} - ngOnInit(): void { <% if(modal) { %>if (this.record.id > 0)<% } else { %>if (this.id > 0)<% } %> this.http.get(`/user/${this.record.id}`).subscribe(res => (this.i = res)); diff --git a/schematics/list/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template b/schematics/list/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template index 463243b93..751caaf8b 100644 --- a/schematics/list/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template +++ b/schematics/list/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template @@ -1,4 +1,4 @@ -import { Component, OnInit, ViewChild<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core'; +import { Component, OnInit, ViewChild<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%>, inject } from '@angular/core'; import { STColumn, STComponent } from '@delon/abc/st'; import { SFSchema } from '@delon/form'; import { ModalHelper, _HttpClient } from '@delon/theme';<% if(standalone) {%> @@ -14,6 +14,9 @@ import { SHARED_IMPORTS } from '@shared';<%}%> changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %> }) export class <%= componentName %> implements OnInit { + private readonly http = inject(_HttpClient); + private readonly modal = inject(ModalHelper); + url = `/user`; searchSchema: SFSchema = { properties: { @@ -38,8 +41,6 @@ export class <%= componentName %> implements OnInit { } ]; - constructor(private http: _HttpClient, private modal: ModalHelper) { } - ngOnInit(): void { } add(): void { diff --git a/schematics/list/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.service.ts.template b/schematics/list/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.service.ts.template index 5a8b5036b..9e51cd73e 100644 --- a/schematics/list/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.service.ts.template +++ b/schematics/list/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.service.ts.template @@ -1,9 +1,7 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { _HttpClient } from '@delon/theme'; @Injectable(<% if(service === 'root') { %>{ providedIn: 'root' }<% } %>) export class <%= serviceName %> { - - constructor(private http: _HttpClient) { } - + private readonly http = inject(_HttpClient); } diff --git a/schematics/plugin/files/sts/swagger-edit/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template b/schematics/plugin/files/sts/swagger-edit/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template index fd708a6e4..9826fd640 100644 --- a/schematics/plugin/files/sts/swagger-edit/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template +++ b/schematics/plugin/files/sts/swagger-edit/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template @@ -1,4 +1,4 @@ -import { Component, OnInit, ViewChild<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';<% if(!modal) { %> +import { Component, OnInit, ViewChild<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%>, inject } from '@angular/core';<% if(!modal) { %> import { ActivatedRoute } from '@angular/router'; import { Location } from '@angular/common';<% } %><% if(modal) { %> import { NzModalRef } from 'ng-zorro-antd/modal';<% } %> @@ -14,6 +14,12 @@ import { Component, OnInit, ViewChild<% if(!!viewEncapsulation) { %>, ViewEncaps changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %> }) export class <%= componentName %> implements OnInit { + private readonly http = inject(_HttpClient); + private readonly msgSrv = inject(NzMessageService);<% if(modal) { %> + private readonly modal = inject(NzModalRef);<% } else { %> + private readonly route = inject(ActivatedRoute); + readonly location = inject(Location);<% } %> + <% if(modal) { %>record: any = {};<% } else { %> id = this.route.snapshot.params.id;<% } %> i: any; @@ -25,14 +31,6 @@ import { Component, OnInit, ViewChild<% if(!!viewEncapsulation) { %>, ViewEncaps }, }; - constructor(<% if(modal) { %> - private modal: NzModalRef,<% } else { %> - private route: ActivatedRoute, - public location: Location,<% } %> - private msgSrv: NzMessageService, - public http: _HttpClient, - ) {} - ngOnInit(): void { <% if(modal) { %>if (this.record.id > 0)<% } else { %>if (this.id > 0)<% } %> this.http.get(`<%=sfUrl%>${this.record.id}`).subscribe(res => (this.i = res)); diff --git a/schematics/plugin/files/sts/swagger-list/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template b/schematics/plugin/files/sts/swagger-list/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template index 1b3fbd8bf..5f9cc3d57 100644 --- a/schematics/plugin/files/sts/swagger-list/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template +++ b/schematics/plugin/files/sts/swagger-list/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template @@ -1,22 +1,24 @@ -import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core'; +import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%>, inject } from '@angular/core'; import { _HttpClient } from '@delon/theme'; import { STColumn } from '@delon/abc/st'; -import { NzMessageService } from 'ng-zorro-antd/message'; +import { NzMessageService } from 'ng-zorro-antd/message';<% if(standalone) {%> +import { SHARED_IMPORTS } from '@shared';<%}%> @Component({ - selector: '<%= selector %>', + selector: '<%= selector %>',<% if(standalone) {%> + standalone: true, + imports: [...SHARED_IMPORTS],<%}%> templateUrl: './<%= dasherize(name) %>.component.html',<% if(!inlineStyle) { %><% } else { %> styleUrls: ['./<%= dasherize(name) %>.component.<%= style %>']<% } %><% if(!!viewEncapsulation) { %>, encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>, changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %> }) export class <%= componentName %> implements OnInit { + private readonly http = inject(_HttpClient); + private readonly msgSrv = inject(NzMessageService); url = '<%=stUrl%>'; columns: STColumn[] = <%=stColumns%>; - constructor(private http: _HttpClient, private msg: NzMessageService) { } - ngOnInit() { } - } diff --git a/schematics/view/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template b/schematics/view/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template index 7f59dbaee..2c174b1c1 100644 --- a/schematics/view/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template +++ b/schematics/view/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts.template @@ -1,4 +1,4 @@ -import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';<% if(!modal) { %> +import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%>, inject } from '@angular/core';<% if(!modal) { %> import { ActivatedRoute } from '@angular/router';<% } %> import { _HttpClient } from '@delon/theme'; import { NzMessageService } from 'ng-zorro-antd/message';<% if(modal) { %> @@ -16,17 +16,15 @@ import { SHARED_IMPORTS } from '@shared';<%}%> changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %> }) export class <%= componentName %> implements OnInit { + private readonly http = inject(_HttpClient); + private readonly msgSrv = inject(NzMessageService);<% if(modal) { %> + private readonly modal = inject(NzModalRef);<% } else { %> + private readonly route = inject(ActivatedRoute);<% } %> + <% if(modal) { %>record: any = {};<% } else { %> id = this.route.snapshot.params.id;<% } %> i: any; - constructor(<% if(modal) { %> - private modal: NzModalRef,<% } else { %> - private route: ActivatedRoute,<% } %> - private msgSrv: NzMessageService, - private http: _HttpClient - ) { } - ngOnInit(): void {<% if(modal) { %> this.http.get(`/user/${this.record.id}`).subscribe(res => this.i = res);<% } else { %> this.http.get(`/user/${this.id}`).subscribe(res => this.i = res);<% } %> diff --git a/src/app/core/error-handler.ts b/src/app/core/error-handler.ts index 16c2ac0f0..82eb21992 100644 --- a/src/app/core/error-handler.ts +++ b/src/app/core/error-handler.ts @@ -1,13 +1,11 @@ import { DOCUMENT } from '@angular/common'; -import { ErrorHandler, Inject, Injectable } from '@angular/core'; +import { ErrorHandler, Injectable, inject } from '@angular/core'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @Injectable() export class CustomErrorHandler extends ErrorHandler { - constructor(@Inject(DOCUMENT) private doc: NzSafeAny) { - super(); - } + private readonly doc = inject(DOCUMENT); handleError(error: NzSafeAny): void { try { diff --git a/src/app/core/i18n/service.ts b/src/app/core/i18n/service.ts index 6744f2224..5e7f411b4 100644 --- a/src/app/core/i18n/service.ts +++ b/src/app/core/i18n/service.ts @@ -1,5 +1,5 @@ import { Platform } from '@angular/cdk/platform'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AlainI18nBaseService, DelonLocaleService, en_US as delonEnUS, zh_CN as delonZhCn } from '@delon/theme'; import { AlainConfigService } from '@delon/util/config'; @@ -13,17 +13,16 @@ export type LangType = 'en-US' | 'zh-CN'; @Injectable({ providedIn: 'root' }) export class I18NService extends AlainI18nBaseService { + private readonly zorroI18n = inject(NzI18nService); + private readonly delonI18n = inject(DelonLocaleService); + private readonly platform = inject(Platform); + private _langs = [ { code: 'en-US', text: 'English' }, { code: 'zh-CN', text: '中文' } ]; - constructor( - private zorroI18n: NzI18nService, - private delonI18n: DelonLocaleService, - private platform: Platform, - cogSrv: AlainConfigService - ) { + constructor(cogSrv: AlainConfigService) { super(cogSrv); // from browser const lang = (this.getBrowserLang() || this.defaultLang) as LangType; diff --git a/src/app/core/meta.service.ts b/src/app/core/meta.service.ts index d4642a05e..99b2f47ec 100644 --- a/src/app/core/meta.service.ts +++ b/src/app/core/meta.service.ts @@ -1,9 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Inject, Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ALAIN_I18N_TOKEN } from '@delon/theme'; -import { I18NService } from './i18n/service'; import { Meta, MetaList, MetaSearchGroup, MetaSearchGroupItem } from '../interfaces'; import { META as ACLMeta } from '../routes/gen/acl/meta'; import { META as AuthMeta } from '../routes/gen/auth/meta'; @@ -33,6 +32,7 @@ const FULLMETAS = [ @Injectable({ providedIn: 'root' }) export class MetaService { + private readonly i18n = inject(ALAIN_I18N_TOKEN); private _platMenus!: any[]; private _menus: any[] | null = null; private _type!: string; @@ -41,11 +41,11 @@ export class MetaService { next: any; prev: any; - constructor(@Inject(ALAIN_I18N_TOKEN) private i18n: I18NService) { + constructor() { // plat titles for (const g of FULLMETAS) { for (const item of g.list!) { - const curTitle = item.meta![i18n.defaultLang].title; + const curTitle = item.meta![this.i18n.defaultLang].title; item._t = typeof curTitle !== 'string' ? Object.values(curTitle!) diff --git a/src/app/core/startup.service.ts b/src/app/core/startup.service.ts index 65aa47298..722742a10 100644 --- a/src/app/core/startup.service.ts +++ b/src/app/core/startup.service.ts @@ -1,6 +1,6 @@ import { Platform } from '@angular/cdk/platform'; import { DOCUMENT } from '@angular/common'; -import { APP_INITIALIZER, Inject, Injectable, Injector, Provider } from '@angular/core'; +import { APP_INITIALIZER, Injectable, Injector, Provider, inject } from '@angular/core'; import { TitleService } from '@delon/theme'; import { LazyService } from '@delon/util/other'; @@ -23,13 +23,11 @@ export function provideStartup(): Provider[] { @Injectable() export class StartupService { - constructor( - private injector: Injector, - iconSrv: NzIconService, - @Inject(DOCUMENT) private doc: NzSafeAny, - private lazy: LazyService, - private platform: Platform - ) { + private readonly injector = inject(Injector); + private readonly doc = inject(DOCUMENT); + private readonly platform = inject(Platform); + private readonly lazy = inject(LazyService); + constructor(iconSrv: NzIconService) { iconSrv.addIcon(...ICONS); } diff --git a/src/app/layout/header/search-box.component.ts b/src/app/layout/header/search-box.component.ts index 393e90141..32f501cd5 100644 --- a/src/app/layout/header/search-box.component.ts +++ b/src/app/layout/header/search-box.component.ts @@ -1,5 +1,5 @@ import { Platform } from '@angular/cdk/platform'; -import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Inject, ViewChild } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, ViewChild, inject } from '@angular/core'; import { Router } from '@angular/router'; import { ALAIN_I18N_TOKEN, I18nPipe } from '@delon/theme'; @@ -8,8 +8,6 @@ import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzInputModule } from 'ng-zorro-antd/input'; -import { I18NService } from '@core'; - declare const docsearch: NzSafeAny; @Component({ @@ -24,16 +22,14 @@ declare const docsearch: NzSafeAny; imports: [NzIconModule, NzInputModule, I18nPipe] }) export class HeaderSearchComponent implements AfterViewInit { + private readonly i18n = inject(ALAIN_I18N_TOKEN); + private readonly platform = inject(Platform); + private readonly router = inject(Router); + private readonly lazySrv = inject(LazyService); + @ViewChild('searchInput', { static: false }) searchInput!: ElementRef; - constructor( - @Inject(ALAIN_I18N_TOKEN) private i18n: I18NService, - private platform: Platform, - private router: Router, - private lazySrv: LazyService - ) {} - ngAfterViewInit(): void { this.initDocSearch(); } diff --git a/src/app/routes/404/404.component.ts b/src/app/routes/404/404.component.ts index dbc8353a5..f68c0212d 100644 --- a/src/app/routes/404/404.component.ts +++ b/src/app/routes/404/404.component.ts @@ -1,10 +1,8 @@ -import { Component, Inject } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { RouterLink } from '@angular/router'; import { ALAIN_I18N_TOKEN } from '@delon/theme'; -import { I18NService } from '@core'; - @Component({ selector: 'not-found', templateUrl: './404.component.html', @@ -12,5 +10,5 @@ import { I18NService } from '@core'; imports: [RouterLink] }) export class NotFoundComponent { - constructor(@Inject(ALAIN_I18N_TOKEN) public i18n: I18NService) {} + readonly i18n = inject(ALAIN_I18N_TOKEN); } diff --git a/src/app/routes/home/home.component.ts b/src/app/routes/home/home.component.ts index e1003ca20..ae804f59a 100644 --- a/src/app/routes/home/home.component.ts +++ b/src/app/routes/home/home.component.ts @@ -1,6 +1,6 @@ import { Platform } from '@angular/cdk/platform'; import { DOCUMENT } from '@angular/common'; -import { AfterViewInit, Component, Inject, NgZone, OnDestroy, OnInit } from '@angular/core'; +import { AfterViewInit, Component, NgZone, OnDestroy, OnInit, inject } from '@angular/core'; import { RouterLink } from '@angular/router'; import { FooterComponent } from '@shared'; @@ -13,8 +13,6 @@ import { NzButtonModule } from 'ng-zorro-antd/button'; import { NzCarouselModule } from 'ng-zorro-antd/carousel'; import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; -import { I18NService } from '@core'; - interface ThemeItem { type: string; url: string; @@ -40,6 +38,11 @@ interface ThemeItem { ] }) export class HomeComponent implements OnInit, OnDestroy, AfterViewInit { + readonly i18n = inject(ALAIN_I18N_TOKEN); + readonly ngZone = inject(NgZone); + private readonly doc = inject(DOCUMENT); + private readonly platform = inject(Platform); + allThemes: ThemeItem[] = [ { type: 'data', url: 'https://e.ng-alain.com/theme/data', screenshot: 'data.webp', buession: true }, { type: 'basic', url: 'https://ng-alain.github.io/ng-alain', screenshot: 'basic.png', buession: false }, @@ -53,12 +56,6 @@ export class HomeComponent implements OnInit, OnDestroy, AfterViewInit { get isBrowser(): boolean { return this.platform.isBrowser; } - constructor( - @Inject(ALAIN_I18N_TOKEN) public i18n: I18NService, - public ngZone: NgZone, - @Inject(DOCUMENT) private doc: Document, - private platform: Platform - ) {} private get body(): HTMLElement { return this.doc.querySelector('body') as HTMLElement; diff --git a/src/app/shared/components/content/content.component.ts b/src/app/shared/components/content/content.component.ts index a029eb9d5..a3d46032d 100644 --- a/src/app/shared/components/content/content.component.ts +++ b/src/app/shared/components/content/content.component.ts @@ -22,16 +22,14 @@ import { MainMenuComponent } from '../main-menu/main-menu.component'; imports: [RouterOutlet, RouterLink, FooterComponent, NzAffixModule, NzIconModule, MainMenuComponent, NzGridModule] }) export class ContentComponent implements OnInit { + readonly meta = inject(MetaService); + private readonly mobileSrv = inject(MobileService); + private readonly cdr = inject(ChangeDetectorRef); + private destroy$ = inject(DestroyRef); isMobile!: boolean; opened = false; - constructor( - public meta: MetaService, - private mobileSrv: MobileService, - private cdr: ChangeDetectorRef - ) {} - ngOnInit(): void { this.mobileSrv.change.pipe(takeUntilDestroyed(this.destroy$)).subscribe(res => { this.isMobile = res; diff --git a/src/app/shared/components/dialog/drawer.component.ts b/src/app/shared/components/dialog/drawer.component.ts index 3a2aaa579..14d811c4e 100644 --- a/src/app/shared/components/dialog/drawer.component.ts +++ b/src/app/shared/components/dialog/drawer.component.ts @@ -1,5 +1,5 @@ import { JsonPipe } from '@angular/common'; -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { NzButtonModule } from 'ng-zorro-antd/button'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -19,9 +19,9 @@ import { NzDrawerRef } from 'ng-zorro-antd/drawer'; imports: [NzButtonModule, JsonPipe] }) export class DemoDrawerComponent { - @Input() record: NzSafeAny; + private readonly ref = inject(NzDrawerRef); - constructor(private ref: NzDrawerRef) {} + @Input() record: NzSafeAny; ok(): void { this.ref.close(`new time: ${+new Date()}`); diff --git a/src/app/shared/components/dialog/modal.component.ts b/src/app/shared/components/dialog/modal.component.ts index ad4a7a3ba..2663b2c29 100644 --- a/src/app/shared/components/dialog/modal.component.ts +++ b/src/app/shared/components/dialog/modal.component.ts @@ -1,5 +1,5 @@ import { JsonPipe } from '@angular/common'; -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { NzButtonModule } from 'ng-zorro-antd/button'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @@ -21,9 +21,9 @@ import { NzModalRef } from 'ng-zorro-antd/modal'; imports: [NzButtonModule, JsonPipe] }) export class DemoModalComponent { - @Input() record: NzSafeAny; + private readonly modal = inject(NzModalRef); - constructor(private modal: NzModalRef) {} + @Input() record: NzSafeAny; ok(): void { this.modal.destroy(`new time: ${+new Date()}`); diff --git a/src/app/shared/components/dialog/sf.component.ts b/src/app/shared/components/dialog/sf.component.ts index ca6d86f50..e1410e9c4 100644 --- a/src/app/shared/components/dialog/sf.component.ts +++ b/src/app/shared/components/dialog/sf.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { DelonFormModule, SFSchema } from '@delon/form'; import { NzButtonModule } from 'ng-zorro-antd/button'; @@ -24,6 +24,8 @@ import { NzModalRef } from 'ng-zorro-antd/modal'; imports: [NzButtonModule, DelonFormModule] }) export class DemoSfComponent { + private readonly modal = inject(NzModalRef); + i: NzSafeAny; schema: SFSchema = { properties: { @@ -47,8 +49,6 @@ export class DemoSfComponent { // }, // }; - constructor(private modal: NzModalRef) {} - save(value: NzSafeAny): void { this.modal.destroy(value); } diff --git a/src/app/shared/components/docs/docs.component.ts b/src/app/shared/components/docs/docs.component.ts index 164a345ac..d938bd117 100644 --- a/src/app/shared/components/docs/docs.component.ts +++ b/src/app/shared/components/docs/docs.component.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Platform } from '@angular/cdk/platform'; import { DOCUMENT } from '@angular/common'; -import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { Router } from '@angular/router'; import { Subscription, filter } from 'rxjs'; @@ -15,7 +16,7 @@ import { NzAnchorModule } from 'ng-zorro-antd/anchor'; import { NzMessageService } from 'ng-zorro-antd/message'; import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; -import { I18NService, MetaService } from '@core'; +import { MetaService } from '@core'; import { EditButtonComponent } from '../edit-button/edit-button.component'; import { RouteTransferDirective } from '../route-transfer/route-transfer.directive'; @@ -37,28 +38,31 @@ declare var hljs: any; ] }) export class DocsComponent implements OnInit, OnDestroy { + readonly meta = inject(MetaService); + private readonly i18n = inject(ALAIN_I18N_TOKEN); + private readonly msg = inject(NzMessageService); + private readonly router = inject(Router); + private readonly sanitizer = inject(DomSanitizer); + private readonly doc = inject(DOCUMENT); + private i18NChange$: Subscription; demoStr!: string; demoContent!: SafeHtml; data: any = {}; - isBrowser = true; + isBrowser = inject(Platform).isBrowser; @Input() codes!: any[]; @Input() item: any; - constructor( - public meta: MetaService, - @Inject(ALAIN_I18N_TOKEN) private i18n: I18NService, - private router: Router, - private sanitizer: DomSanitizer, - @Inject(DOCUMENT) private doc: any, - private msg: NzMessageService, - platform: Platform - ) { - this.isBrowser = platform.isBrowser; - this.i18NChange$ = this.i18n.change.pipe(filter(() => !!this.item)).subscribe(() => { - this.init(); - }); + constructor() { + this.i18NChange$ = this.i18n.change + .pipe( + takeUntilDestroyed(), + filter(() => !!this.item) + ) + .subscribe(() => { + this.init(); + }); } private genData(): void { diff --git a/src/app/shared/components/edit-button/edit-button.component.ts b/src/app/shared/components/edit-button/edit-button.component.ts index 8500bca25..bd346a544 100644 --- a/src/app/shared/components/edit-button/edit-button.component.ts +++ b/src/app/shared/components/edit-button/edit-button.component.ts @@ -1,10 +1,10 @@ -import { Component, Inject, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { ALAIN_I18N_TOKEN, I18nPipe } from '@delon/theme'; import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; -import { I18NService, MetaService } from '@core'; +import { MetaService } from '@core'; @Component({ selector: 'edit-button', @@ -23,15 +23,13 @@ import { I18NService, MetaService } from '@core'; imports: [I18nPipe, NzIconModule, NzToolTipModule] }) export class EditButtonComponent { + private readonly meta = inject(MetaService); + private readonly i18n = inject(ALAIN_I18N_TOKEN); + _full!: string; @Input() set item(data: { urls: string }) { this._full = `${this.meta.github}/edit/master/${this.i18n.get(data.urls)}`; } - - constructor( - private meta: MetaService, - @Inject(ALAIN_I18N_TOKEN) private i18n: I18NService - ) {} } diff --git a/src/app/shared/components/footer/footer.component.html b/src/app/shared/components/footer/footer.component.html index ae94e9673..7109b6c7e 100644 --- a/src/app/shared/components/footer/footer.component.html +++ b/src/app/shared/components/footer/footer.component.html @@ -61,7 +61,7 @@

{{ 'app.footer.community' | i18n }}