diff --git a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts index 549702a1ad..a1961aef4d 100644 --- a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts @@ -1,4 +1,4 @@ -import { Component, inject, isDevMode, OnInit, Optional, SkipSelf, Type } from '@angular/core'; +import { Component, inject, isDevMode, OnInit, Type } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { eLayoutType } from '../enums/common'; import { ABP } from '../models'; @@ -39,7 +39,9 @@ export class DynamicLayoutComponent implements OnInit { protected readonly routerEvents = inject(RouterEvents); protected readonly environment = inject(EnvironmentService); - constructor(@Optional() @SkipSelf() dynamicLayoutComponent: DynamicLayoutComponent) { + constructor() { + const dynamicLayoutComponent = inject(DynamicLayoutComponent, { optional: true, skipSelf: true })!; + if (dynamicLayoutComponent) { if (isDevMode()) console.warn('DynamicLayoutComponent must be used only in AppComponent.'); return; diff --git a/npm/ng-packs/packages/core/src/lib/components/replaceable-route-container.component.ts b/npm/ng-packs/packages/core/src/lib/components/replaceable-route-container.component.ts index 680c7919a8..9ddc294c2b 100644 --- a/npm/ng-packs/packages/core/src/lib/components/replaceable-route-container.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/replaceable-route-container.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Type } from '@angular/core'; +import { Component, OnInit, Type, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { distinctUntilChanged } from 'rxjs/operators'; import { ReplaceableComponents } from '../models/replaceable-components'; @@ -15,18 +15,16 @@ import { CommonModule } from '@angular/common'; imports: [CommonModule], }) export class ReplaceableRouteContainerComponent implements OnInit { + private route = inject(ActivatedRoute); + private replaceableComponents = inject(ReplaceableComponentsService); + private subscription = inject(SubscriptionService); + defaultComponent!: Type; componentKey!: string; externalComponent?: Type; - constructor( - private route: ActivatedRoute, - private replaceableComponents: ReplaceableComponentsService, - private subscription: SubscriptionService, - ) {} - ngOnInit() { this.defaultComponent = this.route.snapshot.data.replaceableComponent.defaultComponent; this.componentKey = ( diff --git a/npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts index f34d187939..398eaef9cc 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts @@ -1,9 +1,11 @@ -import { AfterViewInit, Directive, ElementRef, Input } from '@angular/core'; +import { AfterViewInit, Directive, ElementRef, Input, inject } from '@angular/core'; @Directive({ selector: '[autofocus]', }) export class AutofocusDirective implements AfterViewInit { + private elRef = inject(ElementRef); + private _delay = 0; @Input('autofocus') @@ -15,8 +17,6 @@ export class AutofocusDirective implements AfterViewInit { return this._delay; } - constructor(private elRef: ElementRef) {} - ngAfterViewInit(): void { setTimeout(() => this.elRef.nativeElement.focus(), this.delay as number); } diff --git a/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts index 204db6b418..239aed688d 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts @@ -1,4 +1,4 @@ -import { Directive, ElementRef, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Directive, ElementRef, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { fromEvent } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { SubscriptionService } from '../services/subscription.service'; @@ -8,15 +8,13 @@ import { SubscriptionService } from '../services/subscription.service'; providers: [SubscriptionService], }) export class InputEventDebounceDirective implements OnInit { + private el = inject(ElementRef); + private subscription = inject(SubscriptionService); + @Input() debounce = 300; @Output('input.debounce') readonly debounceEvent = new EventEmitter(); - constructor( - private el: ElementRef, - private subscription: SubscriptionService, - ) {} - ngOnInit(): void { const input$ = fromEvent(this.el.nativeElement, 'input').pipe( debounceTime(this.debounce), diff --git a/npm/ng-packs/packages/core/src/lib/directives/for.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/for.directive.ts index 1e37e1c908..7b3bd0319b 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/for.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/for.directive.ts @@ -1,15 +1,16 @@ -import { - Directive, - EmbeddedViewRef, - Input, - IterableChangeRecord, - IterableChanges, - IterableDiffer, - IterableDiffers, - OnChanges, - TemplateRef, - TrackByFunction, - ViewContainerRef, +import { + Directive, + EmbeddedViewRef, + Input, + IterableChangeRecord, + IterableChanges, + IterableDiffer, + IterableDiffers, + OnChanges, + TemplateRef, + TrackByFunction, + ViewContainerRef, + inject } from '@angular/core'; import clone from 'just-clone'; import compare from 'just-compare'; @@ -36,6 +37,10 @@ class RecordView { selector: '[abpFor]', }) export class ForDirective implements OnChanges { + private tempRef = inject>(TemplateRef); + private vcRef = inject(ViewContainerRef); + private differs = inject(IterableDiffers); + // eslint-disable-next-line @angular-eslint/no-input-rename @Input('abpForOf') items!: any[]; @@ -73,12 +78,6 @@ export class ForDirective implements OnChanges { return this.trackBy || ((index: number, item: any) => (item as any).id || index); } - constructor( - private tempRef: TemplateRef, - private vcRef: ViewContainerRef, - private differs: IterableDiffers, - ) {} - private iterateOverAppliedOperations(changes: IterableChanges) { const rw: RecordView[] = []; diff --git a/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts index 25972ccb71..879fbdf780 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts @@ -1,12 +1,12 @@ -import { - ChangeDetectorRef, - Directive, - ElementRef, - EventEmitter, - Input, - OnInit, - Output, - Self, +import { + ChangeDetectorRef, + Directive, + ElementRef, + EventEmitter, + Input, + OnInit, + Output, + inject } from '@angular/core'; import { FormGroupDirective, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { fromEvent } from 'rxjs'; @@ -22,6 +22,11 @@ type Controls = { [key: string]: UntypedFormControl } | UntypedFormGroup[]; providers: [SubscriptionService], }) export class FormSubmitDirective implements OnInit { + private formGroupDirective = inject(FormGroupDirective, { self: true }); + private host = inject>(ElementRef); + private cdRef = inject(ChangeDetectorRef); + private subscription = inject(SubscriptionService); + @Input() debounce = 200; @@ -36,13 +41,6 @@ export class FormSubmitDirective implements OnInit { executedNgSubmit = false; - constructor( - @Self() private formGroupDirective: FormGroupDirective, - private host: ElementRef, - private cdRef: ChangeDetectorRef, - private subscription: SubscriptionService, - ) {} - ngOnInit() { this.subscription.addOne(this.formGroupDirective.ngSubmit, () => { if (this.markAsDirtyWhenSubmit) { diff --git a/npm/ng-packs/packages/core/src/lib/directives/init.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/init.directive.ts index 293384e4e1..1271f031ba 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/init.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/init.directive.ts @@ -1,12 +1,12 @@ -import { Directive, Output, EventEmitter, ElementRef, AfterViewInit } from '@angular/core'; +import { Directive, Output, EventEmitter, ElementRef, AfterViewInit, inject } from '@angular/core'; @Directive({ selector: '[abpInit]', }) export class InitDirective implements AfterViewInit { - @Output('abpInit') readonly init = new EventEmitter>(); + private elRef = inject(ElementRef); - constructor(private elRef: ElementRef) {} + @Output('abpInit') readonly init = new EventEmitter>(); ngAfterViewInit() { this.init.emit(this.elRef); diff --git a/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts index f277bf50ee..50b805dba4 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts @@ -1,14 +1,13 @@ -import { - AfterViewInit, - ChangeDetectorRef, - Directive, - Inject, - Input, - OnChanges, - OnDestroy, - Optional, - TemplateRef, - ViewContainerRef, +import { + AfterViewInit, + ChangeDetectorRef, + Directive, + Input, + OnChanges, + OnDestroy, + TemplateRef, + ViewContainerRef, + inject } from '@angular/core'; import { ReplaySubject, Subscription } from 'rxjs'; import { distinctUntilChanged, take } from 'rxjs/operators'; @@ -20,6 +19,12 @@ import { QueueManager } from '../utils/queue'; selector: '[abpPermission]', }) export class PermissionDirective implements OnDestroy, OnChanges, AfterViewInit { + private templateRef = inject>(TemplateRef, { optional: true })!; + private vcRef = inject(ViewContainerRef); + private permissionService = inject(PermissionService); + private cdRef = inject(ChangeDetectorRef); + queue = inject(QUEUE_MANAGER); + @Input('abpPermission') condition: string | undefined; @Input('abpPermissionRunChangeDetection') runChangeDetection = true; @@ -30,14 +35,6 @@ export class PermissionDirective implements OnDestroy, OnChanges, AfterViewInit rendered = false; - constructor( - @Optional() private templateRef: TemplateRef, - private vcRef: ViewContainerRef, - private permissionService: PermissionService, - private cdRef: ChangeDetectorRef, - @Inject(QUEUE_MANAGER) public queue: QueueManager, - ) {} - private check() { if (this.subscription) { this.subscription.unsubscribe(); diff --git a/npm/ng-packs/packages/core/src/lib/directives/replaceable-template.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/replaceable-template.directive.ts index 9a1bedcd71..7e948cc20f 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/replaceable-template.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/replaceable-template.directive.ts @@ -1,13 +1,14 @@ -import { - Directive, - Injector, - Input, - OnChanges, - OnInit, - SimpleChanges, - TemplateRef, - Type, - ViewContainerRef, +import { + Directive, + Injector, + Input, + OnChanges, + OnInit, + SimpleChanges, + TemplateRef, + Type, + ViewContainerRef, + inject } from '@angular/core'; import compare from 'just-compare'; import { Subscription } from 'rxjs'; @@ -22,6 +23,12 @@ import { SubscriptionService } from '../services/subscription.service'; providers: [SubscriptionService], }) export class ReplaceableTemplateDirective implements OnInit, OnChanges { + private injector = inject(Injector); + private templateRef = inject>(TemplateRef); + private vcRef = inject(ViewContainerRef); + private replaceableComponents = inject(ReplaceableComponentsService); + private subscription = inject(SubscriptionService); + @Input('abpReplaceableTemplate') data!: ReplaceableComponents.ReplaceableTemplateDirectiveInput; @@ -40,13 +47,7 @@ export class ReplaceableTemplateDirective implements OnInit, OnChanges { initialized = false; - constructor( - private injector: Injector, - private templateRef: TemplateRef, - private vcRef: ViewContainerRef, - private replaceableComponents: ReplaceableComponentsService, - private subscription: SubscriptionService, - ) { + constructor() { this.context = { initTemplate: (ref: any) => { this.resetDefaultComponent(); diff --git a/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts index ddc92cb56b..a1af9bf07e 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts @@ -1,4 +1,4 @@ -import { Directive, ElementRef, EventEmitter, OnInit, Output } from '@angular/core'; +import { Directive, ElementRef, EventEmitter, OnInit, Output, inject } from '@angular/core'; import { fromEvent } from 'rxjs'; import { SubscriptionService } from '../services/subscription.service'; @@ -7,12 +7,10 @@ import { SubscriptionService } from '../services/subscription.service'; providers: [SubscriptionService], }) export class StopPropagationDirective implements OnInit { - @Output('click.stop') readonly stopPropEvent = new EventEmitter(); + private el = inject(ElementRef); + private subscription = inject(SubscriptionService); - constructor( - private el: ElementRef, - private subscription: SubscriptionService, - ) {} + @Output('click.stop') readonly stopPropEvent = new EventEmitter(); ngOnInit(): void { this.subscription.addOne(fromEvent(this.el.nativeElement, 'click'), event => { diff --git a/npm/ng-packs/packages/core/src/lib/handlers/routes.handler.ts b/npm/ng-packs/packages/core/src/lib/handlers/routes.handler.ts index 76dc66b3ae..d8880b4fbf 100644 --- a/npm/ng-packs/packages/core/src/lib/handlers/routes.handler.ts +++ b/npm/ng-packs/packages/core/src/lib/handlers/routes.handler.ts @@ -1,4 +1,4 @@ -import { Injectable, Optional } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Route, Router } from '@angular/router'; import { ABP } from '../models'; import { RoutesService } from '../services/routes.service'; @@ -7,10 +7,10 @@ import { RoutesService } from '../services/routes.service'; providedIn: 'root', }) export class RoutesHandler { - constructor( - private routes: RoutesService, - @Optional() private router: Router, - ) { + private routes = inject(RoutesService); + private router = inject(Router, { optional: true })!; + + constructor() { this.addRoutes(); } diff --git a/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts b/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts index 9321521159..04be80638e 100644 --- a/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts +++ b/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts @@ -1,5 +1,5 @@ import { HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { finalize } from 'rxjs/operators'; import { HttpWaitService } from '../services'; @@ -7,7 +7,8 @@ import { HttpWaitService } from '../services'; providedIn: 'root', }) export class ApiInterceptor implements IApiInterceptor { - constructor(private httpWaitService: HttpWaitService) {} + private httpWaitService = inject(HttpWaitService); + getAdditionalHeaders(existingHeaders?: HttpHeaders) { return existingHeaders || new HttpHeaders(); diff --git a/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts index 2dbac68eb7..bbb2a71752 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts @@ -1,4 +1,4 @@ -import { Injectable, Pipe, PipeTransform } from '@angular/core'; +import { Injectable, Pipe, PipeTransform, inject } from '@angular/core'; import { LocalizationWithDefault } from '../models/localization'; import { LocalizationService } from '../services/localization.service'; @@ -7,7 +7,8 @@ import { LocalizationService } from '../services/localization.service'; name: 'abpLocalization', }) export class LocalizationPipe implements PipeTransform { - constructor(private localization: LocalizationService) {} + private localization = inject(LocalizationService); + transform( value: string | LocalizationWithDefault = '', diff --git a/npm/ng-packs/packages/core/src/lib/pipes/short-date-time.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/short-date-time.pipe.ts index b72ca45dde..8ed6dc55b4 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/short-date-time.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/short-date-time.pipe.ts @@ -1,5 +1,5 @@ import { DatePipe, DATE_PIPE_DEFAULT_TIMEZONE } from '@angular/common'; -import { Inject, LOCALE_ID, Optional, Pipe, PipeTransform } from '@angular/core'; +import { LOCALE_ID, Pipe, PipeTransform, inject } from '@angular/core'; import { ConfigStateService } from '../services'; import { getShortDateShortTimeFormat } from '../utils/date-utils'; @@ -8,11 +8,12 @@ import { getShortDateShortTimeFormat } from '../utils/date-utils'; pure: true, }) export class ShortDateTimePipe extends DatePipe implements PipeTransform { - constructor( - private configStateService: ConfigStateService, - @Inject(LOCALE_ID) locale: string, - @Inject(DATE_PIPE_DEFAULT_TIMEZONE) @Optional() defaultTimezone?: string | null, - ) { + private configStateService = inject(ConfigStateService); + + constructor() { + const locale = inject(LOCALE_ID); + const defaultTimezone = inject(DATE_PIPE_DEFAULT_TIMEZONE, { optional: true }); + super(locale, defaultTimezone); } diff --git a/npm/ng-packs/packages/core/src/lib/pipes/short-date.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/short-date.pipe.ts index 26a7e1552f..9968009e49 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/short-date.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/short-date.pipe.ts @@ -1,5 +1,5 @@ import { DatePipe, DATE_PIPE_DEFAULT_TIMEZONE } from '@angular/common'; -import { Inject, LOCALE_ID, Optional, Pipe, PipeTransform } from '@angular/core'; +import { LOCALE_ID, Pipe, PipeTransform, inject } from '@angular/core'; import { ConfigStateService } from '../services'; import { getShortDateFormat } from '../utils/date-utils'; @@ -8,11 +8,12 @@ import { getShortDateFormat } from '../utils/date-utils'; pure: true, }) export class ShortDatePipe extends DatePipe implements PipeTransform { - constructor( - private configStateService: ConfigStateService, - @Inject(LOCALE_ID) locale: string, - @Inject(DATE_PIPE_DEFAULT_TIMEZONE) @Optional() defaultTimezone?: string | null, - ) { + private configStateService = inject(ConfigStateService); + + constructor() { + const locale = inject(LOCALE_ID); + const defaultTimezone = inject(DATE_PIPE_DEFAULT_TIMEZONE, { optional: true }); + super(locale, defaultTimezone); } diff --git a/npm/ng-packs/packages/core/src/lib/pipes/short-time.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/short-time.pipe.ts index 54e70802f8..fac722af5e 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/short-time.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/short-time.pipe.ts @@ -1,5 +1,5 @@ import { DatePipe, DATE_PIPE_DEFAULT_TIMEZONE } from '@angular/common'; -import { Inject, LOCALE_ID, Optional, Pipe, PipeTransform } from '@angular/core'; +import { LOCALE_ID, Pipe, PipeTransform, inject } from '@angular/core'; import { ConfigStateService } from '../services'; import { getShortTimeFormat } from '../utils/date-utils'; @@ -8,11 +8,12 @@ import { getShortTimeFormat } from '../utils/date-utils'; pure: true, }) export class ShortTimePipe extends DatePipe implements PipeTransform { - constructor( - private configStateService: ConfigStateService, - @Inject(LOCALE_ID) locale: string, - @Inject(DATE_PIPE_DEFAULT_TIMEZONE) @Optional() defaultTimezone?: string | null, - ) { + private configStateService = inject(ConfigStateService); + + constructor() { + const locale = inject(LOCALE_ID); + const defaultTimezone = inject(DATE_PIPE_DEFAULT_TIMEZONE, { optional: true }); + super(locale, defaultTimezone); } diff --git a/npm/ng-packs/packages/core/src/lib/pipes/to-injector.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/to-injector.pipe.ts index 5a1ac4df29..77663a1262 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/to-injector.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/to-injector.pipe.ts @@ -1,4 +1,4 @@ -import { InjectionToken, Injector, Pipe, PipeTransform } from '@angular/core'; +import { InjectionToken, Injector, Pipe, PipeTransform, inject } from '@angular/core'; export const INJECTOR_PIPE_DATA_TOKEN = new InjectionToken( 'INJECTOR_PIPE_DATA_TOKEN', @@ -8,7 +8,8 @@ export const INJECTOR_PIPE_DATA_TOKEN = new InjectionToken( name: 'toInjector', }) export class ToInjectorPipe implements PipeTransform { - constructor(private injector: Injector) {} + private injector = inject(Injector); + transform( value: any, token: InjectionToken = INJECTOR_PIPE_DATA_TOKEN, diff --git a/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts b/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts index 039ddc707e..d778e0a279 100644 --- a/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts +++ b/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts @@ -1,12 +1,14 @@ import { RestService } from '../../../../services'; import { Rest } from '../../../../models'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import type { FindTenantResultDto } from '../../../volo/abp/asp-net-core/mvc/multi-tenancy/models'; @Injectable({ providedIn: 'root', }) -export class AbpTenantService { +export class AbpTenantService { + private restService = inject(RestService); + apiName = 'abp'; @@ -24,6 +26,4 @@ export class AbpTenantService { url: `/api/abp/multi-tenancy/tenants/by-name/${name}`, }, { apiName: this.apiName,...config }); - - constructor(private restService: RestService) {} } diff --git a/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/api-exploring/abp-api-definition.service.ts b/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/api-exploring/abp-api-definition.service.ts index a331106214..770faca476 100644 --- a/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/api-exploring/abp-api-definition.service.ts +++ b/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/api-exploring/abp-api-definition.service.ts @@ -1,12 +1,14 @@ import { RestService } from '../../../../../../services'; import { Rest } from '../../../../../../models'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import type { ApplicationApiDescriptionModel, ApplicationApiDescriptionModelRequestDto } from '../../../http/modeling/models'; @Injectable({ providedIn: 'root', }) -export class AbpApiDefinitionService { +export class AbpApiDefinitionService { + private restService = inject(RestService); + apiName = 'abp'; @@ -17,6 +19,4 @@ export class AbpApiDefinitionService { params: { includeTypes: model.includeTypes }, }, { apiName: this.apiName,...config }); - - constructor(private restService: RestService) {} } diff --git a/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service.ts b/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service.ts index 3c04af51fc..9dc35fa7b6 100644 --- a/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service.ts +++ b/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service.ts @@ -1,12 +1,14 @@ import type { ApplicationConfigurationDto, ApplicationConfigurationRequestOptions } from './models'; import { RestService } from '../../../../../../services'; import { Rest } from '../../../../../../models'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; @Injectable({ providedIn: 'root', }) -export class AbpApplicationConfigurationService { +export class AbpApplicationConfigurationService { + private restService = inject(RestService); + apiName = 'abp'; @@ -17,6 +19,4 @@ export class AbpApplicationConfigurationService { params: { includeLocalizationResources: options.includeLocalizationResources }, }, { apiName: this.apiName, ...config }); - - constructor(private restService: RestService) { } } diff --git a/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-localization.service.ts b/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-localization.service.ts index 3b9fd80584..aa3f495b53 100644 --- a/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-localization.service.ts +++ b/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-localization.service.ts @@ -1,12 +1,14 @@ import type { ApplicationLocalizationDto, ApplicationLocalizationRequestDto } from './models'; import { RestService } from '../../../../../../services'; import { Rest } from '../../../../../../models'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; @Injectable({ providedIn: 'root', }) -export class AbpApplicationLocalizationService { +export class AbpApplicationLocalizationService { + private restService = inject(RestService); + apiName = 'abp'; @@ -17,6 +19,4 @@ export class AbpApplicationLocalizationService { params: { cultureName: input.cultureName, onlyDynamics: input.onlyDynamics }, }, { apiName: this.apiName,...config }); - - constructor(private restService: RestService) {} } diff --git a/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts b/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts index 475f6d76f3..1243e8b2ba 100644 --- a/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable, Optional } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, Subject } from 'rxjs'; import { map, switchMap, take, tap } from 'rxjs/operators'; import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service'; @@ -15,6 +15,10 @@ import { InternalStore } from '../utils/internal-store-utils'; providedIn: 'root', }) export class ConfigStateService { + private abpConfigService = inject(AbpApplicationConfigurationService); + private abpApplicationLocalizationService = inject(AbpApplicationLocalizationService); + private readonly includeLocalizationResources = inject(INCUDE_LOCALIZATION_RESOURCES_TOKEN, { optional: true }); + private updateSubject = new Subject(); private readonly store = new InternalStore({} as ApplicationConfigurationDto); @@ -27,13 +31,7 @@ export class ConfigStateService { get createOnUpdateStream() { return this.store.sliceUpdate; } - constructor( - private abpConfigService: AbpApplicationConfigurationService, - private abpApplicationLocalizationService: AbpApplicationLocalizationService, - @Optional() - @Inject(INCUDE_LOCALIZATION_RESOURCES_TOKEN) - private readonly includeLocalizationResources: boolean | null, - ) { + constructor() { this.initUpdateStream(); } diff --git a/npm/ng-packs/packages/core/src/lib/services/content-projection.service.ts b/npm/ng-packs/packages/core/src/lib/services/content-projection.service.ts index dfcdea330a..d9aac2bd48 100644 --- a/npm/ng-packs/packages/core/src/lib/services/content-projection.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/content-projection.service.ts @@ -1,9 +1,10 @@ -import { Injectable, Injector, TemplateRef, Type } from '@angular/core'; +import { Injectable, Injector, TemplateRef, Type, inject } from '@angular/core'; import { ProjectionStrategy } from '../strategies/projection.strategy'; @Injectable({ providedIn: 'root' }) export class ContentProjectionService { - constructor(private injector: Injector) {} + private injector = inject(Injector); + projectContent | TemplateRef>( projectionStrategy: ProjectionStrategy, diff --git a/npm/ng-packs/packages/core/src/lib/services/http-wait.service.ts b/npm/ng-packs/packages/core/src/lib/services/http-wait.service.ts index d80599fc25..d8073d8760 100644 --- a/npm/ng-packs/packages/core/src/lib/services/http-wait.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/http-wait.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { HttpRequest } from '@angular/common/http'; import { InternalStore } from '../utils/internal-store-utils'; import { getPathName } from '../utils/http-utils'; @@ -26,7 +26,9 @@ export class HttpWaitService { private delay: number; private destroy$ = new Subject(); - constructor(injector: Injector) { + constructor() { + const injector = inject(Injector); + this.delay = injector.get(LOADER_DELAY, 500); } diff --git a/npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts b/npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts index f50102e15a..8358570db9 100644 --- a/npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { concat, Observable, of, pipe, throwError } from 'rxjs'; import { delay, retryWhen, shareReplay, take, tap } from 'rxjs/operators'; import { LoadingStrategy } from '../strategies'; @@ -8,9 +8,9 @@ import { ResourceWaitService } from './resource-wait.service'; providedIn: 'root', }) export class LazyLoadService { - readonly loaded = new Map(); + private resourceWaitService = inject(ResourceWaitService); - constructor(private resourceWaitService: ResourceWaitService) {} + readonly loaded = new Map(); load(strategy: LoadingStrategy, retryTimes?: number, retryDelay?: number): Observable { if (this.loaded.has(strategy.path)) return of(new CustomEvent('load')); diff --git a/npm/ng-packs/packages/core/src/lib/services/list.service.ts b/npm/ng-packs/packages/core/src/lib/services/list.service.ts index 204833a66d..3cfc0ecac9 100644 --- a/npm/ng-packs/packages/core/src/lib/services/list.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/list.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Injector, OnDestroy } from '@angular/core'; +import { Injectable, Injector, OnDestroy, inject } from '@angular/core'; import { EMPTY, BehaviorSubject, @@ -119,7 +119,9 @@ export class ListService implements this.next(); }; - constructor(injector: Injector) { + constructor() { + const injector = inject(Injector); + const delay = injector.get(LIST_QUERY_DEBOUNCE_TIME, 300); this.delay = delay ? debounceTime(delay) : tap(); this.get(); diff --git a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts index 7b474e7881..dd6def0c24 100644 --- a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts @@ -1,5 +1,5 @@ import { registerLocaleData } from '@angular/common'; -import { Injectable, Injector, isDevMode, Optional, SkipSelf } from '@angular/core'; +import { Injectable, Injector, isDevMode, inject } from '@angular/core'; import { BehaviorSubject, combineLatest, from, Observable, Subject } from 'rxjs'; import { filter, map, switchMap } from 'rxjs/operators'; import { ABP } from '../models/common'; @@ -17,6 +17,10 @@ import { SessionStateService } from './session-state.service'; @Injectable({ providedIn: 'root' }) export class LocalizationService { + private sessionState = inject(SessionStateService); + private injector = inject(Injector); + private configState = inject(ConfigStateService); + private latestLang = this.sessionState.getLanguage(); private _languageChange$ = new Subject(); @@ -44,14 +48,9 @@ export class LocalizationService { return this._languageChange$.asObservable(); } - constructor( - private sessionState: SessionStateService, - private injector: Injector, - @Optional() - @SkipSelf() - otherInstance: LocalizationService, - private configState: ConfigStateService, - ) { + constructor() { + const otherInstance = inject(LocalizationService, { optional: true, skipSelf: true })!; + if (otherInstance) throw new Error('LocalizationService should have only one instance.'); this.listenToSetLanguage(); diff --git a/npm/ng-packs/packages/core/src/lib/services/multi-tenancy.service.ts b/npm/ng-packs/packages/core/src/lib/services/multi-tenancy.service.ts index 8fbde5cc8e..92c6e43a44 100644 --- a/npm/ng-packs/packages/core/src/lib/services/multi-tenancy.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/multi-tenancy.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { map, switchMap } from 'rxjs/operators'; import { AbpTenantService } from '../proxy/pages/abp/multi-tenancy'; import { @@ -12,6 +12,12 @@ import { SessionStateService } from './session-state.service'; @Injectable({ providedIn: 'root' }) export class MultiTenancyService { + private restService = inject(RestService); + private sessionState = inject(SessionStateService); + private tenantService = inject(AbpTenantService); + private configStateService = inject(ConfigStateService); + tenantKey = inject(TENANT_KEY); + domainTenant: CurrentTenantDto | null = null; isTenantBoxVisible = true; @@ -23,14 +29,6 @@ export class MultiTenancyService { return this.configStateService.refreshAppState().pipe(map(_ => tenant)); }; - constructor( - private restService: RestService, - private sessionState: SessionStateService, - private tenantService: AbpTenantService, - private configStateService: ConfigStateService, - @Inject(TENANT_KEY) public tenantKey: string, - ) { } - setTenantByName(tenantName: string) { return this.tenantService .findTenantByName(tenantName) diff --git a/npm/ng-packs/packages/core/src/lib/services/permission.service.ts b/npm/ng-packs/packages/core/src/lib/services/permission.service.ts index 70724ac814..b45b2a36f5 100644 --- a/npm/ng-packs/packages/core/src/lib/services/permission.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/permission.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { map } from 'rxjs/operators'; import { ABP } from '../models/common'; import { ApplicationConfigurationDto } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/models'; @@ -6,7 +6,8 @@ import { ConfigStateService } from './config-state.service'; @Injectable({ providedIn: 'root' }) export class PermissionService { - constructor(protected configState: ConfigStateService) {} + protected configState = inject(ConfigStateService); + getGrantedPolicy$(key: string) { return this.getStream().pipe( diff --git a/npm/ng-packs/packages/core/src/lib/services/replaceable-components.service.ts b/npm/ng-packs/packages/core/src/lib/services/replaceable-components.service.ts index 1f02daa389..c156a237aa 100644 --- a/npm/ng-packs/packages/core/src/lib/services/replaceable-components.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/replaceable-components.service.ts @@ -1,4 +1,4 @@ -import { Injectable, NgZone } from '@angular/core'; +import { Injectable, NgZone, inject } from '@angular/core'; import { Router } from '@angular/router'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -8,6 +8,9 @@ import { reloadRoute } from '../utils/route-utils'; @Injectable({ providedIn: 'root' }) export class ReplaceableComponentsService { + private ngZone = inject(NgZone); + private router = inject(Router); + private readonly store: InternalStore; get replaceableComponents$(): Observable { @@ -22,7 +25,7 @@ export class ReplaceableComponentsService { return this.store.sliceUpdate(state => state); } - constructor(private ngZone: NgZone, private router: Router) { + constructor() { this.store = new InternalStore([] as ReplaceableComponents.ReplaceableComponent[]); } diff --git a/npm/ng-packs/packages/core/src/lib/services/rest.service.ts b/npm/ng-packs/packages/core/src/lib/services/rest.service.ts index 83f91777ca..a331b6c742 100644 --- a/npm/ng-packs/packages/core/src/lib/services/rest.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/rest.service.ts @@ -1,5 +1,5 @@ import { HttpClient, HttpParameterCodec, HttpParams, HttpRequest } from '@angular/common/http'; -import { Inject, Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { ExternalHttpClient } from '../clients/http.client'; @@ -14,13 +14,12 @@ import { HttpErrorReporterService } from './http-error-reporter.service'; providedIn: 'root', }) export class RestService { - constructor( - @Inject(CORE_OPTIONS) protected options: ABP.Root, - protected http: HttpClient, - protected externalHttp: ExternalHttpClient, - protected environment: EnvironmentService, - protected httpErrorReporter: HttpErrorReporterService, - ) { } + protected options = inject(CORE_OPTIONS); + protected http = inject(HttpClient); + protected externalHttp = inject(ExternalHttpClient); + protected environment = inject(EnvironmentService); + protected httpErrorReporter = inject(HttpErrorReporterService); + protected getApiFromStore(apiName: string | undefined): string { return this.environment.getApiUrl(apiName); diff --git a/npm/ng-packs/packages/core/src/lib/services/router-wait.service.ts b/npm/ng-packs/packages/core/src/lib/services/router-wait.service.ts index 96e66a00e3..f68f6d065a 100644 --- a/npm/ng-packs/packages/core/src/lib/services/router-wait.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/router-wait.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { NavigationStart } from '@angular/router'; import { of, Subject, timer } from 'rxjs'; import { map, mapTo, switchMap, takeUntil, tap } from 'rxjs/operators'; @@ -14,10 +14,14 @@ export interface RouterWaitState { providedIn: 'root', }) export class RouterWaitService { + private routerEvents = inject(RouterEvents); + private store = new InternalStore({ loading: false }); private destroy$ = new Subject(); private delay: number; - constructor(private routerEvents: RouterEvents, injector: Injector) { + constructor() { + const injector = inject(Injector); + this.delay = injector.get(LOADER_DELAY, 500); this.updateLoadingStatusOnNavigationEvents(); } diff --git a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts index 393c724423..175bf76446 100644 --- a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Injector, OnDestroy } from '@angular/core'; +import { Injectable, Injector, OnDestroy, inject } from '@angular/core'; import { BehaviorSubject, Observable, Subscription, map } from 'rxjs'; import { ABP } from '../models/common'; import { OTHERS_GROUP } from '../tokens'; @@ -201,6 +201,8 @@ export abstract class AbstractNavTreeService extends AbstractTreeService implements OnDestroy { + protected injector = inject(Injector); + private subscription: Subscription; private permissionService: PermissionService; private compareFunc; @@ -211,8 +213,10 @@ export abstract class AbstractNavTreeService return this.compareFunc(a, b); }; - constructor(protected injector: Injector) { + constructor() { super(); + const injector = this.injector; + const configState = this.injector.get(ConfigStateService); this.subscription = configState .createOnUpdateStream(state => state) diff --git a/npm/ng-packs/packages/core/src/lib/services/session-state.service.ts b/npm/ng-packs/packages/core/src/lib/services/session-state.service.ts index d6f423983c..7e519dc742 100644 --- a/npm/ng-packs/packages/core/src/lib/services/session-state.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/session-state.service.ts @@ -12,6 +12,9 @@ import { AbpLocalStorageService } from './local-storage.service'; providedIn: 'root', }) export class SessionStateService { + private configState = inject(ConfigStateService); + private localStorageService = inject(AbpLocalStorageService); + private readonly store = new InternalStore({} as Session.State); protected readonly document = inject(DOCUMENT); @@ -19,10 +22,7 @@ export class SessionStateService { this.localStorageService.setItem('abpSession', JSON.stringify(this.store.state)); }; - constructor( - private configState: ConfigStateService, - private localStorageService: AbpLocalStorageService, - ) { + constructor() { this.init(); this.setInitialLanguage(); } diff --git a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts index 411a9e8041..aac6c41fcd 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts @@ -1,201 +1,201 @@ -import { HttpClient } from '@angular/common/http'; -import { Component, NgModule } from '@angular/core'; -import { ActivatedRoute, RouterModule } from '@angular/router'; -import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; -import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; -import { eLayoutType } from '../enums/common'; -import { ABP } from '../models'; -import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service'; -import { ReplaceableComponentsService, RoutesService } from '../services'; -import { mockRoutesService } from './routes.service.spec'; - -@Component({ - selector: 'abp-layout-application', - template: '', -}) -class DummyApplicationLayoutComponent {} - -@Component({ - selector: 'abp-layout-account', - template: '', -}) -class DummyAccountLayoutComponent {} - -@Component({ - selector: 'abp-layout-empty', - template: '', -}) -class DummyEmptyLayoutComponent {} - -const LAYOUTS = [ - DummyApplicationLayoutComponent, - DummyAccountLayoutComponent, - DummyEmptyLayoutComponent, -]; - -@NgModule({ - imports: [RouterModule], - declarations: [...LAYOUTS], -}) -class DummyLayoutModule {} - -@Component({ - selector: 'abp-dummy', - template: '{{route.snapshot.data?.name}} works!', -}) -class DummyComponent { - constructor(public route: ActivatedRoute) {} -} - -const routes: ABP.Route[] = [ - { - path: '', - name: 'Root', - }, - { - path: '/parentWithLayout', - name: 'ParentWithLayout', - parentName: 'Root', - layout: eLayoutType.application, - }, - { - path: '/parentWithLayout/childWithoutLayout', - name: 'ChildWithoutLayout', - parentName: 'ParentWithLayout', - }, - { - path: '/parentWithLayout/childWithLayout', - name: 'ChildWithLayout', - parentName: 'ParentWithLayout', - layout: eLayoutType.account, - }, - { - path: '/withData', - name: 'WithData', - layout: eLayoutType.application, - }, -]; - -describe('DynamicLayoutComponent', () => { - const createComponent = createRoutingFactory({ - component: RouterOutletComponent, - stubsEnabled: false, - declarations: [DummyComponent, DynamicLayoutComponent], - mocks: [AbpApplicationConfigurationService, HttpClient], - providers: [ - { - provide: RoutesService, - useFactory: () => mockRoutesService(), - }, - ReplaceableComponentsService, - ], - imports: [RouterModule, DummyLayoutModule], - routes: [ - { path: '', component: RouterOutletComponent }, - { - path: 'parentWithLayout', - component: DynamicLayoutComponent, - children: [ - { - path: 'childWithoutLayout', - component: DummyComponent, - data: { name: 'childWithoutLayout' }, - }, - { - path: 'childWithLayout', - component: DummyComponent, - data: { name: 'childWithLayout' }, - }, - ], - }, - { - path: 'withData', - component: DynamicLayoutComponent, - children: [ - { - path: '', - component: DummyComponent, - data: { name: 'withData' }, - }, - ], - data: { layout: eLayoutType.empty }, - }, - { - path: 'withoutLayout', - component: DynamicLayoutComponent, - children: [ - { - path: '', - component: DummyComponent, - data: { name: 'withoutLayout' }, - }, - ], - data: { layout: null }, - }, - ], - }); - - let spectator: SpectatorRouting; - let replaceableComponents: ReplaceableComponentsService; - - beforeEach(async () => { - spectator = createComponent(); - replaceableComponents = spectator.inject(ReplaceableComponentsService); - const routesService = spectator.inject(RoutesService); - routesService.add(routes); - - replaceableComponents.add({ - key: 'Theme.ApplicationLayoutComponent', - component: DummyApplicationLayoutComponent, - }); - replaceableComponents.add({ - key: 'Theme.AccountLayoutComponent', - component: DummyAccountLayoutComponent, - }); - replaceableComponents.add({ - key: 'Theme.EmptyLayoutComponent', - component: DummyEmptyLayoutComponent, - }); - }); - - it('should handle application layout from parent abp route and display it', async () => { - spectator.router.navigateByUrl('/parentWithLayout/childWithoutLayout'); - await spectator.fixture.whenStable(); - spectator.detectComponentChanges(); - expect(spectator.query('abp-dynamic-layout')).toBeTruthy(); - expect(spectator.query('abp-layout-application')).toBeTruthy(); - }); - - it('should handle account layout from own property and display it', async () => { - spectator.router.navigateByUrl('/parentWithLayout/childWithLayout'); - await spectator.fixture.whenStable(); - spectator.detectComponentChanges(); - expect(spectator.query('abp-layout-account')).toBeTruthy(); - }); - - it('should handle empty layout from route data and display it', async () => { - spectator.router.navigateByUrl('/withData'); - await spectator.fixture.whenStable(); - spectator.detectComponentChanges(); - expect(spectator.query('abp-layout-empty')).toBeTruthy(); - }); - - it('should display empty layout when layout is null', async () => { - spectator.router.navigateByUrl('/withoutLayout'); - await spectator.fixture.whenStable(); - spectator.detectComponentChanges(); - expect(spectator.query('abp-layout-empty')).toBeTruthy(); - }); - - it('should not display any layout when layouts are empty', async () => { - const spy = jest.spyOn(replaceableComponents, 'get'); - spy.mockReturnValue(null); - spectator.detectChanges(); - - spectator.router.navigateByUrl('/withoutLayout'); - await spectator.fixture.whenStable(); - spectator.detectComponentChanges(); - - expect(spectator.query('abp-layout-empty')).toBeFalsy(); - }); -}); +import { HttpClient } from '@angular/common/http'; +import { Component, NgModule, inject as inject_1 } from '@angular/core'; +import { ActivatedRoute, RouterModule } from '@angular/router'; +import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; +import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; +import { eLayoutType } from '../enums/common'; +import { ABP } from '../models'; +import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service'; +import { ReplaceableComponentsService, RoutesService } from '../services'; +import { mockRoutesService } from './routes.service.spec'; + +@Component({ + selector: 'abp-layout-application', + template: '', +}) +class DummyApplicationLayoutComponent {} + +@Component({ + selector: 'abp-layout-account', + template: '', +}) +class DummyAccountLayoutComponent {} + +@Component({ + selector: 'abp-layout-empty', + template: '', +}) +class DummyEmptyLayoutComponent {} + +const LAYOUTS = [ + DummyApplicationLayoutComponent, + DummyAccountLayoutComponent, + DummyEmptyLayoutComponent, +]; + +@NgModule({ + imports: [RouterModule], + declarations: [...LAYOUTS], +}) +class DummyLayoutModule {} + +@Component({ + selector: 'abp-dummy', + template: '{{route.snapshot.data?.name}} works!', +}) +class DummyComponent { route = inject_1(ActivatedRoute); + +} + +const routes: ABP.Route[] = [ + { + path: '', + name: 'Root', + }, + { + path: '/parentWithLayout', + name: 'ParentWithLayout', + parentName: 'Root', + layout: eLayoutType.application, + }, + { + path: '/parentWithLayout/childWithoutLayout', + name: 'ChildWithoutLayout', + parentName: 'ParentWithLayout', + }, + { + path: '/parentWithLayout/childWithLayout', + name: 'ChildWithLayout', + parentName: 'ParentWithLayout', + layout: eLayoutType.account, + }, + { + path: '/withData', + name: 'WithData', + layout: eLayoutType.application, + }, +]; + +describe('DynamicLayoutComponent', () => { + const createComponent = createRoutingFactory({ + component: RouterOutletComponent, + stubsEnabled: false, + declarations: [DummyComponent, DynamicLayoutComponent], + mocks: [AbpApplicationConfigurationService, HttpClient], + providers: [ + { + provide: RoutesService, + useFactory: () => mockRoutesService(), + }, + ReplaceableComponentsService, + ], + imports: [RouterModule, DummyLayoutModule], + routes: [ + { path: '', component: RouterOutletComponent }, + { + path: 'parentWithLayout', + component: DynamicLayoutComponent, + children: [ + { + path: 'childWithoutLayout', + component: DummyComponent, + data: { name: 'childWithoutLayout' }, + }, + { + path: 'childWithLayout', + component: DummyComponent, + data: { name: 'childWithLayout' }, + }, + ], + }, + { + path: 'withData', + component: DynamicLayoutComponent, + children: [ + { + path: '', + component: DummyComponent, + data: { name: 'withData' }, + }, + ], + data: { layout: eLayoutType.empty }, + }, + { + path: 'withoutLayout', + component: DynamicLayoutComponent, + children: [ + { + path: '', + component: DummyComponent, + data: { name: 'withoutLayout' }, + }, + ], + data: { layout: null }, + }, + ], + }); + + let spectator: SpectatorRouting; + let replaceableComponents: ReplaceableComponentsService; + + beforeEach(async () => { + spectator = createComponent(); + replaceableComponents = spectator.inject(ReplaceableComponentsService); + const routesService = spectator.inject(RoutesService); + routesService.add(routes); + + replaceableComponents.add({ + key: 'Theme.ApplicationLayoutComponent', + component: DummyApplicationLayoutComponent, + }); + replaceableComponents.add({ + key: 'Theme.AccountLayoutComponent', + component: DummyAccountLayoutComponent, + }); + replaceableComponents.add({ + key: 'Theme.EmptyLayoutComponent', + component: DummyEmptyLayoutComponent, + }); + }); + + it('should handle application layout from parent abp route and display it', async () => { + spectator.router.navigateByUrl('/parentWithLayout/childWithoutLayout'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + expect(spectator.query('abp-dynamic-layout')).toBeTruthy(); + expect(spectator.query('abp-layout-application')).toBeTruthy(); + }); + + it('should handle account layout from own property and display it', async () => { + spectator.router.navigateByUrl('/parentWithLayout/childWithLayout'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + expect(spectator.query('abp-layout-account')).toBeTruthy(); + }); + + it('should handle empty layout from route data and display it', async () => { + spectator.router.navigateByUrl('/withData'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + expect(spectator.query('abp-layout-empty')).toBeTruthy(); + }); + + it('should display empty layout when layout is null', async () => { + spectator.router.navigateByUrl('/withoutLayout'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + expect(spectator.query('abp-layout-empty')).toBeTruthy(); + }); + + it('should not display any layout when layouts are empty', async () => { + const spy = jest.spyOn(replaceableComponents, 'get'); + spy.mockReturnValue(null); + spectator.detectChanges(); + + spectator.router.navigateByUrl('/withoutLayout'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + + expect(spectator.query('abp-layout-empty')).toBeFalsy(); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts index 10e59c86f6..94fdf8a527 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/replaceable-template.directive.spec.ts @@ -1,178 +1,174 @@ -import { Component, EventEmitter, Inject, Input, Optional, Output } from '@angular/core'; -import { Router } from '@angular/router'; -import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/jest'; -import { BehaviorSubject } from 'rxjs'; -import { ReplaceableTemplateDirective } from '../directives/replaceable-template.directive'; -import { ReplaceableComponents } from '../models/replaceable-components'; -import { ReplaceableComponentsService } from '../services/replaceable-components.service'; - -@Component({ - selector: 'abp-default-component', - template: '

default

', - exportAs: 'abpDefaultComponent', -}) -class DefaultComponent { - @Input() - oneWay; - - @Input() - twoWay: boolean; - - @Output() - readonly twoWayChange = new EventEmitter(); - - @Output() - readonly someOutput = new EventEmitter(); - - setTwoWay(value) { - this.twoWay = value; - this.twoWayChange.emit(value); - } -} - -@Component({ - selector: 'abp-external-component', - template: '

external

', -}) -class ExternalComponent { - constructor( - @Optional() - @Inject('REPLACEABLE_DATA') - public data: ReplaceableComponents.ReplaceableTemplateData, - ) {} -} - -describe('ReplaceableTemplateDirective', () => { - let spectator: SpectatorDirective; - const get$Res = new BehaviorSubject(undefined); - - const createDirective = createDirectiveFactory({ - directive: ReplaceableTemplateDirective, - declarations: [DefaultComponent, ExternalComponent], - entryComponents: [ExternalComponent], - mocks: [Router], - providers: [{ provide: ReplaceableComponentsService, useValue: { get$: () => get$Res } }], - }); - - describe('without external component', () => { - const twoWayChange = jest.fn(a => a); - const someOutput = jest.fn(a => a); - - beforeEach(() => { - spectator = createDirective( - ` -
- -
- `, - { - hostProps: { - oneWay: { label: 'Test' }, - twoWay: false, - twoWayChange, - someOutput, - }, - }, - ); - - const component = spectator.query(DefaultComponent); - spectator.directive.context.initTemplate(component); - spectator.detectChanges(); - }); - - afterEach(() => twoWayChange.mockClear()); - - it('should display the default template when store response is undefined', () => { - expect(spectator.query('abp-default-component')).toBeTruthy(); - }); - - it('should be setted inputs and outputs', () => { - const component = spectator.query(DefaultComponent); - expect(component.oneWay).toEqual({ label: 'Test' }); - expect(component.twoWay).toEqual(false); - }); - - it('should change the component inputs', () => { - const component = spectator.query(DefaultComponent); - spectator.setHostInput({ oneWay: 'test' }); - component.setTwoWay(true); - component.someOutput.emit('someOutput emitted'); - expect(component.oneWay).toBe('test'); - expect(twoWayChange).toHaveBeenCalledWith(true); - expect(someOutput).toHaveBeenCalledWith('someOutput emitted'); - }); - }); - - describe('with external component', () => { - const twoWayChange = jest.fn(a => a); - const someOutput = jest.fn(a => a); - - beforeEach(() => { - spectator = createDirective( - ` -
- -
- `, - { hostProps: { oneWay: { label: 'Test' }, twoWay: false, twoWayChange, someOutput } }, - ); - - get$Res.next({ component: ExternalComponent, key: 'TestModule.TestComponent' }); - }); - - afterEach(() => twoWayChange.mockClear()); - - it('should display the external component', () => { - expect(spectator.query('p')).toHaveText('external'); - }); - - it('should be injected the data object', () => { - const externalComponent = spectator.query(ExternalComponent); - expect(externalComponent.data).toEqual({ - componentKey: 'TestModule.TestComponent', - inputs: { oneWay: { label: 'Test' }, twoWay: false }, - outputs: { someOutput, twoWayChange }, - }); - }); - - it('should be worked all data properties', () => { - const externalComponent = spectator.query(ExternalComponent); - spectator.setHostInput({ oneWay: 'test' }); - externalComponent.data.inputs.twoWay = true; - externalComponent.data.outputs.someOutput('someOutput emitted'); - expect(externalComponent.data.inputs.oneWay).toBe('test'); - expect(twoWayChange).toHaveBeenCalledWith(true); - expect(someOutput).toHaveBeenCalledWith('someOutput emitted'); - - spectator.setHostInput({ twoWay: 'twoWay test' }); - expect(externalComponent.data.inputs.twoWay).toBe('twoWay test'); - }); - - it('should be worked correctly the default component when the external component has been removed from store', () => { - expect(spectator.query('p')).toHaveText('external'); - const externalComponent = spectator.query(ExternalComponent); - spectator.setHostInput({ oneWay: 'test' }); - externalComponent.data.inputs.twoWay = true; - get$Res.next({ component: null, key: 'TestModule.TestComponent' }); - spectator.detectChanges(); - const component = spectator.query(DefaultComponent); - spectator.directive.context.initTemplate(component); - expect(spectator.query('abp-default-component')).toBeTruthy(); - - expect(component.oneWay).toEqual('test'); - expect(component.twoWay).toEqual(true); - }); - - it('should reset default component subscriptions', () => { - get$Res.next({ component: null, key: 'TestModule.TestComponent' }); - const component = spectator.query(DefaultComponent); - spectator.directive.context.initTemplate(component); - spectator.detectChanges(); - const unsubscribe = jest.fn(() => {}); - spectator.directive.defaultComponentSubscriptions.twoWayChange.unsubscribe = unsubscribe; - - get$Res.next({ component: ExternalComponent, key: 'TestModule.TestComponent' }); - expect(unsubscribe).toHaveBeenCalled(); - }); - }); -}); +import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; +import { Router } from '@angular/router'; +import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/jest'; +import { BehaviorSubject } from 'rxjs'; +import { ReplaceableTemplateDirective } from '../directives/replaceable-template.directive'; +import { ReplaceableComponents } from '../models/replaceable-components'; +import { ReplaceableComponentsService } from '../services/replaceable-components.service'; + +@Component({ + selector: 'abp-default-component', + template: '

default

', + exportAs: 'abpDefaultComponent', +}) +class DefaultComponent { + @Input() + oneWay; + + @Input() + twoWay: boolean; + + @Output() + readonly twoWayChange = new EventEmitter(); + + @Output() + readonly someOutput = new EventEmitter(); + + setTwoWay(value) { + this.twoWay = value; + this.twoWayChange.emit(value); + } +} + +@Component({ + selector: 'abp-external-component', + template: '

external

', +}) +class ExternalComponent { data = inject>('REPLACEABLE_DATA' as any, { optional: true })!; + +} + +describe('ReplaceableTemplateDirective', () => { + let spectator: SpectatorDirective; + const get$Res = new BehaviorSubject(undefined); + + const createDirective = createDirectiveFactory({ + directive: ReplaceableTemplateDirective, + declarations: [DefaultComponent, ExternalComponent], + entryComponents: [ExternalComponent], + mocks: [Router], + providers: [{ provide: ReplaceableComponentsService, useValue: { get$: () => get$Res } }], + }); + + describe('without external component', () => { + const twoWayChange = jest.fn(a => a); + const someOutput = jest.fn(a => a); + + beforeEach(() => { + spectator = createDirective( + ` +
+ +
+ `, + { + hostProps: { + oneWay: { label: 'Test' }, + twoWay: false, + twoWayChange, + someOutput, + }, + }, + ); + + const component = spectator.query(DefaultComponent); + spectator.directive.context.initTemplate(component); + spectator.detectChanges(); + }); + + afterEach(() => twoWayChange.mockClear()); + + it('should display the default template when store response is undefined', () => { + expect(spectator.query('abp-default-component')).toBeTruthy(); + }); + + it('should be setted inputs and outputs', () => { + const component = spectator.query(DefaultComponent); + expect(component.oneWay).toEqual({ label: 'Test' }); + expect(component.twoWay).toEqual(false); + }); + + it('should change the component inputs', () => { + const component = spectator.query(DefaultComponent); + spectator.setHostInput({ oneWay: 'test' }); + component.setTwoWay(true); + component.someOutput.emit('someOutput emitted'); + expect(component.oneWay).toBe('test'); + expect(twoWayChange).toHaveBeenCalledWith(true); + expect(someOutput).toHaveBeenCalledWith('someOutput emitted'); + }); + }); + + describe('with external component', () => { + const twoWayChange = jest.fn(a => a); + const someOutput = jest.fn(a => a); + + beforeEach(() => { + spectator = createDirective( + ` +
+ +
+ `, + { hostProps: { oneWay: { label: 'Test' }, twoWay: false, twoWayChange, someOutput } }, + ); + + get$Res.next({ component: ExternalComponent, key: 'TestModule.TestComponent' }); + }); + + afterEach(() => twoWayChange.mockClear()); + + it('should display the external component', () => { + expect(spectator.query('p')).toHaveText('external'); + }); + + it('should be injected the data object', () => { + const externalComponent = spectator.query(ExternalComponent); + expect(externalComponent.data).toEqual({ + componentKey: 'TestModule.TestComponent', + inputs: { oneWay: { label: 'Test' }, twoWay: false }, + outputs: { someOutput, twoWayChange }, + }); + }); + + it('should be worked all data properties', () => { + const externalComponent = spectator.query(ExternalComponent); + spectator.setHostInput({ oneWay: 'test' }); + externalComponent.data.inputs.twoWay = true; + externalComponent.data.outputs.someOutput('someOutput emitted'); + expect(externalComponent.data.inputs.oneWay).toBe('test'); + expect(twoWayChange).toHaveBeenCalledWith(true); + expect(someOutput).toHaveBeenCalledWith('someOutput emitted'); + + spectator.setHostInput({ twoWay: 'twoWay test' }); + expect(externalComponent.data.inputs.twoWay).toBe('twoWay test'); + }); + + it('should be worked correctly the default component when the external component has been removed from store', () => { + expect(spectator.query('p')).toHaveText('external'); + const externalComponent = spectator.query(ExternalComponent); + spectator.setHostInput({ oneWay: 'test' }); + externalComponent.data.inputs.twoWay = true; + get$Res.next({ component: null, key: 'TestModule.TestComponent' }); + spectator.detectChanges(); + const component = spectator.query(DefaultComponent); + spectator.directive.context.initTemplate(component); + expect(spectator.query('abp-default-component')).toBeTruthy(); + + expect(component.oneWay).toEqual('test'); + expect(component.twoWay).toEqual(true); + }); + + it('should reset default component subscriptions', () => { + get$Res.next({ component: null, key: 'TestModule.TestComponent' }); + const component = spectator.query(DefaultComponent); + spectator.directive.context.initTemplate(component); + spectator.detectChanges(); + const unsubscribe = jest.fn(() => {}); + spectator.directive.defaultComponentSubscriptions.twoWayChange.unsubscribe = unsubscribe; + + get$Res.next({ component: ExternalComponent, key: 'TestModule.TestComponent' }); + expect(unsubscribe).toHaveBeenCalled(); + }); + }); +}); diff --git a/npm/ng-packs/packages/core/testing/src/lib/services/mock-permission.service.ts b/npm/ng-packs/packages/core/testing/src/lib/services/mock-permission.service.ts index b4d180b17a..513d83653d 100644 --- a/npm/ng-packs/packages/core/testing/src/lib/services/mock-permission.service.ts +++ b/npm/ng-packs/packages/core/testing/src/lib/services/mock-permission.service.ts @@ -1,12 +1,18 @@ import { ConfigStateService, PermissionService } from '@abp/ng.core'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class MockPermissionService extends PermissionService { - constructor(protected configState: ConfigStateService) { + protected configState: ConfigStateService; + + constructor() { + const configState = inject(ConfigStateService); + super(configState); + this.configState = configState; + this.grantAllPolicies(); } diff --git a/npm/ng-packs/packages/core/testing/src/lib/services/mock-rest.service.ts b/npm/ng-packs/packages/core/testing/src/lib/services/mock-rest.service.ts index 7fd171c1d4..6522bc717d 100644 --- a/npm/ng-packs/packages/core/testing/src/lib/services/mock-rest.service.ts +++ b/npm/ng-packs/packages/core/testing/src/lib/services/mock-rest.service.ts @@ -7,20 +7,30 @@ import { RestService, } from '@abp/ng.core'; import { HttpClient } from '@angular/common/http'; -import { Inject, Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, throwError } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class MockRestService extends RestService { - constructor( - @Inject(CORE_OPTIONS) protected options: ABP.Root, - protected http: HttpClient, - protected externalhttp: ExternalHttpClient, - protected environment: EnvironmentService, - ) { + protected options: ABP.Root; + protected http: HttpClient; + protected externalhttp: ExternalHttpClient; + protected environment: EnvironmentService; + + constructor() { + const options = inject(CORE_OPTIONS); + const http = inject(HttpClient); + const externalhttp = inject(ExternalHttpClient); + const environment = inject(EnvironmentService); + super(options, http,externalhttp, environment, null as unknown as HttpErrorReporterService); + + this.options = options; + this.http = http; + this.externalhttp = externalhttp; + this.environment = environment; } handleError(err: any): Observable {