diff --git a/frontend/src/app/core/active-window/active-window.service.ts b/frontend/src/app/core/active-window/active-window.service.ts index b5c17117c130..d16d35c3e66e 100644 --- a/frontend/src/app/core/active-window/active-window.service.ts +++ b/frontend/src/app/core/active-window/active-window.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable, DOCUMENT } from '@angular/core'; +import { Injectable, DOCUMENT, inject } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { debugLog } from 'core-app/shared/helpers/debug_output'; @@ -6,7 +6,9 @@ import { debugLog } from 'core-app/shared/helpers/debug_output'; export class ActiveWindowService { private activeState$ = new BehaviorSubject(true); - constructor(@Inject(DOCUMENT) document:Document) { + constructor() { + const document = inject(DOCUMENT); + document.addEventListener('visibilitychange', () => { if (document.visibilityState) { debugLog(`Browser window has visibility state changed to ${document.visibilityState}`); diff --git a/frontend/src/app/core/apiv3/api-v3.service.ts b/frontend/src/app/core/apiv3/api-v3.service.ts index cf6aa288ead5..dc39e5742c3d 100644 --- a/frontend/src/app/core/apiv3/api-v3.service.ts +++ b/frontend/src/app/core/apiv3/api-v3.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { ApiV3GettableResource, ApiV3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; import { Constructor } from 'core-app/core/util-types'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -65,6 +65,9 @@ import { @Injectable({ providedIn: 'root' }) export class ApiV3Service { + readonly injector = inject(Injector); + readonly pathHelper = inject(PathHelperService); + // /api/v3/attachments public readonly attachments = this.apiV3CollectionEndpoint('attachments'); @@ -173,11 +176,6 @@ export class ApiV3Service { // VIRTUAL boards are /api/v3/grids + a scope filter public readonly boards = this.apiV3CustomEndpoint(ApiV3BoardsPaths); - constructor( - readonly injector:Injector, - readonly pathHelper:PathHelperService, - ) { } - /** * Returns the part of the API that exists both * - WITHIN a project scope /api/v3/projects/* diff --git a/frontend/src/app/core/apiv3/endpoints/projects/project.cache.ts b/frontend/src/app/core/apiv3/endpoints/projects/project.cache.ts index bf76e19acd47..30f5f97fd2eb 100644 --- a/frontend/src/app/core/apiv3/endpoints/projects/project.cache.ts +++ b/frontend/src/app/core/apiv3/endpoints/projects/project.cache.ts @@ -37,8 +37,8 @@ import { ProjectResource } from 'core-app/features/hal/resources/project-resourc export class ProjectCache extends StateCacheService { @InjectField() private schemaCacheService:SchemaCacheService; - constructor(readonly injector:Injector, - state:MultiInputState) { + // eslint-disable-next-line @angular-eslint/prefer-inject -- manually instantiated, not DI-resolved + constructor(readonly injector:Injector, state:MultiInputState) { super(state); } diff --git a/frontend/src/app/core/augmenting/openproject-augmenting.module.ts b/frontend/src/app/core/augmenting/openproject-augmenting.module.ts index 31e4c8684878..5f9c0c242a84 100644 --- a/frontend/src/app/core/augmenting/openproject-augmenting.module.ts +++ b/frontend/src/app/core/augmenting/openproject-augmenting.module.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { NgModule } from '@angular/core'; +import { NgModule, inject } from '@angular/core'; import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; import { OpModalWrapperAugmentService } from 'core-app/shared/components/modal/modal-wrapper-augment.service'; @@ -34,7 +34,9 @@ import { OpModalWrapperAugmentService } from 'core-app/shared/components/modal/m imports: [OpenprojectModalModule], }) export class OpenprojectAugmentingModule { - constructor(modalWrapper:OpModalWrapperAugmentService) { + constructor() { + const modalWrapper = inject(OpModalWrapperAugmentService); + // Setup augmenting services modalWrapper.setupListener(); diff --git a/frontend/src/app/core/backup/op-backup.service.ts b/frontend/src/app/core/backup/op-backup.service.ts index e31833982302..2e5d636cdf73 100644 --- a/frontend/src/app/core/backup/op-backup.service.ts +++ b/frontend/src/app/core/backup/op-backup.service.ts @@ -26,17 +26,15 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { Observable } from 'rxjs'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; @Injectable({ providedIn: 'root' }) export class OpenProjectBackupService { - constructor( - protected apiV3Service:ApiV3Service, - ) { - } + protected apiV3Service = inject(ApiV3Service); + public triggerBackup(backupToken:string, includeAttachments = true):Observable { return this diff --git a/frontend/src/app/core/browser/browser-detector.service.ts b/frontend/src/app/core/browser/browser-detector.service.ts index 711cff515618..9076045dbbce 100644 --- a/frontend/src/app/core/browser/browser-detector.service.ts +++ b/frontend/src/app/core/browser/browser-detector.service.ts @@ -1,9 +1,9 @@ -import { Inject, Injectable, DOCUMENT } from '@angular/core'; +import { Injectable, DOCUMENT, inject } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class BrowserDetector { - constructor(@Inject(DOCUMENT) private documentElement:Document) { - } + private documentElement = inject(DOCUMENT); + /** * Detect mobile browser based on the Rails determined UA diff --git a/frontend/src/app/core/config/configuration.service.ts b/frontend/src/app/core/config/configuration.service.ts index ee186227657a..e80fd50b9db4 100644 --- a/frontend/src/app/core/config/configuration.service.ts +++ b/frontend/src/app/core/config/configuration.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import moment from 'moment'; import { ConfigurationResource } from 'core-app/features/hal/resources/configuration-resource'; @@ -35,15 +35,13 @@ import { type DurationFormat } from 'core-app/shared/helpers/chronic_duration'; @Injectable({ providedIn: 'root' }) export class ConfigurationService { + private readonly apiV3Service = inject(ApiV3Service); + // fetches configuration from the ApiV3 endpoint // TODO: this currently saves the request between page reloads, // but could easily be stored in localStorage private configuration:ConfigurationResource; - public constructor( - private readonly apiV3Service:ApiV3Service, - ) { } - public initialize():Promise { return this.loadConfiguration(); } diff --git a/frontend/src/app/core/current-project/current-project.service.spec.ts b/frontend/src/app/core/current-project/current-project.service.spec.ts index 59ada2ac2818..7b8875827b16 100644 --- a/frontend/src/app/core/current-project/current-project.service.spec.ts +++ b/frontend/src/app/core/current-project/current-project.service.spec.ts @@ -26,7 +26,9 @@ // See COPYRIGHT and LICENSE files for more details. //++ +import { TestBed } from '@angular/core/testing'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { CurrentProjectService } from './current-project.service'; describe('currentProject service', () => { @@ -40,7 +42,15 @@ describe('currentProject service', () => { }; beforeEach(() => { - currentProject = new CurrentProjectService(new PathHelperService(), apiV3Stub); + TestBed.configureTestingModule({ + providers: [ + CurrentProjectService, + PathHelperService, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + { provide: ApiV3Service, useValue: apiV3Stub }, + ], + }); + currentProject = TestBed.inject(CurrentProjectService); }); describe('with no meta present', () => { diff --git a/frontend/src/app/core/current-project/current-project.service.ts b/frontend/src/app/core/current-project/current-project.service.ts index da1425eda32a..ad54323f4e6c 100644 --- a/frontend/src/app/core/current-project/current-project.service.ts +++ b/frontend/src/app/core/current-project/current-project.service.ts @@ -26,21 +26,21 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { getMetaElement } from '../setup/globals/global-helpers'; @Injectable({ providedIn: 'root' }) export class CurrentProjectService { + private PathHelper = inject(PathHelperService); + private apiV3Service = inject(ApiV3Service); + private currentId:string|null = null; private currentName:string|null = null; private currentIdentifier:string|null = null; - constructor( - private PathHelper:PathHelperService, - private apiV3Service:ApiV3Service, - ) { + constructor() { this.detect(); } diff --git a/frontend/src/app/core/current-user/current-user.module.ts b/frontend/src/app/core/current-user/current-user.module.ts index 892368c38147..a26c8abb089f 100644 --- a/frontend/src/app/core/current-user/current-user.module.ts +++ b/frontend/src/app/core/current-user/current-user.module.ts @@ -1,4 +1,4 @@ -import { Injector, NgModule } from '@angular/core'; +import { Injector, NgModule, inject } from '@angular/core'; import { CurrentUserService } from './current-user.service'; import { CurrentUserStore } from './current-user.store'; @@ -35,7 +35,9 @@ export function bootstrapModule(injector:Injector):void { ], }) export class CurrentUserModule { - constructor(injector:Injector) { + constructor() { + const injector = inject(Injector); + bootstrapModule(injector); } } diff --git a/frontend/src/app/core/current-user/current-user.query.ts b/frontend/src/app/core/current-user/current-user.query.ts index dee37f6f36a1..48e4bbddb6fd 100644 --- a/frontend/src/app/core/current-user/current-user.query.ts +++ b/frontend/src/app/core/current-user/current-user.query.ts @@ -1,11 +1,17 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Query } from '@datorama/akita'; import { CurrentUserState, CurrentUserStore } from './current-user.store'; @Injectable() export class CurrentUserQuery extends Query { - constructor(protected store:CurrentUserStore) { + protected store:CurrentUserStore; + + constructor() { + const store = inject(CurrentUserStore); + super(store); + + this.store = store; } isLoggedIn$ = this.select((state) => !!state.loggedIn); diff --git a/frontend/src/app/core/current-user/current-user.service.ts b/frontend/src/app/core/current-user/current-user.service.ts index af5eeb3781a0..ccdad8f88dc8 100644 --- a/frontend/src/app/core/current-user/current-user.service.ts +++ b/frontend/src/app/core/current-user/current-user.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { ApiV3ListFilter } from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; import { CapabilitiesResourceService } from 'core-app/core/state/capabilities/capabilities.service'; @@ -38,12 +38,12 @@ import { CurrentUser, CurrentUserStore } from './current-user.store'; @Injectable({ providedIn: 'root' }) export class CurrentUserService { - constructor( - private apiV3Service:ApiV3Service, - private currentUserStore:CurrentUserStore, - private currentUserQuery:CurrentUserQuery, - private capabilitiesService:CapabilitiesResourceService, - ) { + private apiV3Service = inject(ApiV3Service); + private currentUserStore = inject(CurrentUserStore); + private currentUserQuery = inject(CurrentUserQuery); + private capabilitiesService = inject(CapabilitiesResourceService); + + constructor() { this.setupLegacyDataListeners(); } diff --git a/frontend/src/app/core/datetime/timezone.service.ts b/frontend/src/app/core/datetime/timezone.service.ts index a703ce1c6160..d8c4b2bb3988 100644 --- a/frontend/src/app/core/datetime/timezone.service.ts +++ b/frontend/src/app/core/datetime/timezone.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import moment, { Moment } from 'moment-timezone'; @@ -34,10 +34,9 @@ import { outputChronicDuration } from '../../shared/helpers/chronic_duration'; @Injectable({ providedIn: 'root' }) export class TimezoneService { - constructor( - readonly configurationService:ConfigurationService, - readonly I18n:I18nService, - ) { } + readonly configurationService = inject(ConfigurationService); + readonly I18n = inject(I18nService); + /** * Returns the user's configured timezone or guesses it through moment diff --git a/frontend/src/app/core/days/weekday.service.ts b/frontend/src/app/core/days/weekday.service.ts index ab91eb3cde53..2f5643b1fdbe 100644 --- a/frontend/src/app/core/days/weekday.service.ts +++ b/frontend/src/app/core/days/weekday.service.ts @@ -26,10 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - Injectable, - Injector, -} from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import moment, { Moment } from 'moment'; import { take, @@ -45,14 +42,12 @@ import { @Injectable({ providedIn: 'root' }) export class WeekdayService { + readonly injector = inject(Injector); + @InjectField() weekdaysService:WeekdayResourceService; private weekdays:IWeekday[]; - constructor( - readonly injector:Injector, - ) {} - /** * @param date The iso day number (1-7) or a date instance * @return {boolean} whether the given iso day is working or not diff --git a/frontend/src/app/core/enterprise/banners.service.ts b/frontend/src/app/core/enterprise/banners.service.ts index a8c39e57b33c..92c06a214b5f 100644 --- a/frontend/src/app/core/enterprise/banners.service.ts +++ b/frontend/src/app/core/enterprise/banners.service.ts @@ -26,18 +26,20 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Inject, Injectable, DOCUMENT } from '@angular/core'; +import { Injectable, DOCUMENT, inject } from '@angular/core'; import { enterpriseEditionUrl } from 'core-app/core/setup/globals/constants.const'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; @Injectable({ providedIn: 'root' }) export class BannersService { + protected documentElement = inject(DOCUMENT); + protected configuration = inject(ConfigurationService); + private readonly _bannersHidden:boolean = true; - constructor( - @Inject(DOCUMENT) protected documentElement:Document, - protected configuration:ConfigurationService, - ) { + constructor() { + const documentElement = this.documentElement; + this._bannersHidden = documentElement.body.classList.contains('ee-banners-hidden'); } diff --git a/frontend/src/app/core/global_search/input/global-search-input.component.ts b/frontend/src/app/core/global_search/input/global-search-input.component.ts index 83c2b43deeb3..5f693bd17280 100644 --- a/frontend/src/app/core/global_search/input/global-search-input.component.ts +++ b/frontend/src/app/core/global_search/input/global-search-input.component.ts @@ -1,16 +1,5 @@ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - HostListener, - Input, - OnDestroy, - ViewChild, - ViewEncapsulation, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostListener, Input, OnDestroy, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { first, map, switchMap, tap } from 'rxjs/operators'; import { GlobalSearchService } from 'core-app/core/global_search/services/global-search.service'; @@ -74,6 +63,18 @@ interface SearchResultItems { standalone: false, }) export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { + readonly elementRef = inject(ElementRef); + readonly I18n = inject(I18nService); + readonly apiV3Service = inject(ApiV3Service); + readonly pathHelperService = inject(PathHelperService); + readonly halResourceService = inject(HalResourceService); + readonly globalSearchService = inject(GlobalSearchService); + readonly currentProjectService = inject(CurrentProjectService); + readonly deviceService = inject(DeviceService); + readonly cdRef = inject(ChangeDetectorRef); + readonly halNotification = inject(HalResourceNotificationService); + readonly recentItemsService = inject(RecentItemsService); + @Input() public placeholder:string; @ViewChild('btn', { static: true }) btn:ElementRef; @@ -131,19 +132,7 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { search: this.I18n.t('js.autocompleter.search'), }; - constructor( - readonly elementRef:ElementRef, - readonly I18n:I18nService, - readonly apiV3Service:ApiV3Service, - readonly pathHelperService:PathHelperService, - readonly halResourceService:HalResourceService, - readonly globalSearchService:GlobalSearchService, - readonly currentProjectService:CurrentProjectService, - readonly deviceService:DeviceService, - readonly cdRef:ChangeDetectorRef, - readonly halNotification:HalResourceNotificationService, - readonly recentItemsService:RecentItemsService, - ) { + constructor() { populateInputsFromDataset(this); } diff --git a/frontend/src/app/core/global_search/openproject-global-search.module.ts b/frontend/src/app/core/global_search/openproject-global-search.module.ts index f8b8ebbfd562..259e73946427 100644 --- a/frontend/src/app/core/global_search/openproject-global-search.module.ts +++ b/frontend/src/app/core/global_search/openproject-global-search.module.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injector, NgModule } from '@angular/core'; +import { Injector, NgModule, inject } from '@angular/core'; import { OpenprojectWorkPackagesModule } from 'core-app/features/work-packages/openproject-work-packages.module'; import { GlobalSearchInputComponent } from 'core-app/core/global_search/input/global-search-input.component'; import { GlobalSearchWorkPackagesComponent } from 'core-app/core/global_search/global-search-work-packages.component'; @@ -53,6 +53,5 @@ import { RecentItemsService } from 'core-app/core/recent-items.service'; ], }) export class OpenprojectGlobalSearchModule { - constructor(readonly injector:Injector) { - } + readonly injector = inject(Injector); } diff --git a/frontend/src/app/core/global_search/services/global-search.service.ts b/frontend/src/app/core/global_search/services/global-search.service.ts index 38f17e17697e..7ded727590b9 100644 --- a/frontend/src/app/core/global_search/services/global-search.service.ts +++ b/frontend/src/app/core/global_search/services/global-search.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @@ -34,13 +34,11 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service @Injectable() export class GlobalSearchService { - constructor( - protected I18n:I18nService, - protected injector:Injector, - protected PathHelper:PathHelperService, - protected currentProjectService:CurrentProjectService, - ) { - } + protected I18n = inject(I18nService); + protected injector = inject(Injector); + protected PathHelper = inject(PathHelperService); + protected currentProjectService = inject(CurrentProjectService); + public submitSearch(query:string, scope:string):void { const path = this.searchPath(scope); diff --git a/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts b/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts index 4c1392c51437..ee600bddab12 100644 --- a/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts +++ b/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts @@ -26,8 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnDestroy, OnInit } from '@angular/core'; -import { StateService } from '@uirouter/core'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { Subscription } from 'rxjs'; import { GlobalSearchService } from 'core-app/core/global_search/services/global-search.service'; import { ScrollableTabsComponent } from 'core-app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component'; @@ -40,20 +39,13 @@ import { TabDefinition } from 'core-app/shared/components/tabs/tab.interface'; standalone: false, }) export class GlobalSearchTabsComponent extends ScrollableTabsComponent implements OnInit, OnDestroy { + readonly globalSearchService = inject(GlobalSearchService); + private currentTabSub:Subscription; private tabsSub:Subscription; - public classes:string[] = ['global-search--tabs', 'scrollable-tabs']; - - constructor( - readonly globalSearchService:GlobalSearchService, - protected readonly $state:StateService, - public injector:Injector, - cdRef:ChangeDetectorRef, - ) { - super($state, cdRef, injector); - } + public override classes:string[] = ['global-search--tabs', 'scrollable-tabs']; ngOnInit():void { this.currentTabSub = this.globalSearchService diff --git a/frontend/src/app/core/html-sanitize/html-sanitize.service.ts b/frontend/src/app/core/html-sanitize/html-sanitize.service.ts index f6007f4e02bd..237539d18f83 100644 --- a/frontend/src/app/core/html-sanitize/html-sanitize.service.ts +++ b/frontend/src/app/core/html-sanitize/html-sanitize.service.ts @@ -26,12 +26,13 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, SecurityContext } from '@angular/core'; +import { Injectable, SecurityContext, inject } from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; @Injectable({ providedIn: 'root' }) export class HTMLSanitizeService { - public constructor(readonly sanitizer:DomSanitizer) { } + readonly sanitizer = inject(DomSanitizer); + public sanitize(string:string):SafeHtml { return this.sanitizer.sanitize(SecurityContext.HTML, string) || ''; diff --git a/frontend/src/app/core/html/op-title.service.ts b/frontend/src/app/core/html/op-title.service.ts index fc6a500ecb2a..5c56c9b920d6 100644 --- a/frontend/src/app/core/html/op-title.service.ts +++ b/frontend/src/app/core/html/op-title.service.ts @@ -1,13 +1,13 @@ import { Title } from '@angular/platform-browser'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { getMetaContent } from '../setup/globals/global-helpers'; const titlePartsSeparator = ' | '; @Injectable({ providedIn: 'root' }) export class OpTitleService { - constructor(private titleService:Title) { - } + private titleService = inject(Title); + public get current():string { return this.titleService.getTitle(); diff --git a/frontend/src/app/core/i18n/i18n.service.ts b/frontend/src/app/core/i18n/i18n.service.ts index 76cdf244165b..5c97407f5433 100644 --- a/frontend/src/app/core/i18n/i18n.service.ts +++ b/frontend/src/app/core/i18n/i18n.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { NgSelectConfig } from '@ng-select/ng-select'; import { I18n } from 'i18n-js'; import { FormatNumberOptions, TranslateOptions } from 'i18n-js/src/typing'; @@ -6,12 +6,12 @@ import { getMetaValue } from '../setup/globals/global-helpers'; @Injectable({ providedIn: 'root' }) export class I18nService { + private config = inject(NgSelectConfig); + private i18n:I18n = window.I18n; private instanceLocale:string; - constructor( - private config:NgSelectConfig, - ) { + constructor() { this.instanceLocale = getMetaValue('openproject_initializer', 'instanceLocale', 'en'); this.config.addTagText = this.t('js.autocomplete_ng_select.add_tag'); diff --git a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts index 3e7fbee0bce2..4c17bc260eba 100644 --- a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts +++ b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { DeviceService } from 'core-app/core/browser/device.service'; @@ -35,6 +35,9 @@ import { queryVisible } from 'core-app/shared/helpers/dom-helpers'; @Injectable({ providedIn: 'root' }) export class MainMenuToggleService { + injector = inject(Injector); + readonly deviceService = inject(DeviceService); + private elementWidth:number; private elementMinWidth = 11; @@ -61,10 +64,7 @@ export class MainMenuToggleService { private wasCollapsedByUser = false; - constructor( - public injector:Injector, - readonly deviceService:DeviceService, - ) { + constructor() { this.initializeMenu(); // Add resize event listener window.addEventListener('resize', this.onWindowResize.bind(this)); diff --git a/frontend/src/app/core/main-menu/submenu.service.ts b/frontend/src/app/core/main-menu/submenu.service.ts index 5ad6678b2607..ce4f97f081b8 100644 --- a/frontend/src/app/core/main-menu/submenu.service.ts +++ b/frontend/src/app/core/main-menu/submenu.service.ts @@ -1,10 +1,11 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { type FrameElement } from '@hotwired/turbo'; import { StateService } from '@uirouter/core'; @Injectable({ providedIn: 'root' }) export class SubmenuService { - constructor(protected $state:StateService) {} + protected $state = inject(StateService); + reloadSubmenu(selectedQueryId:string|null, sidemenuId?:string):void { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment diff --git a/frontend/src/app/core/navigation/url-params.service.ts b/frontend/src/app/core/navigation/url-params.service.ts index 7cf07168c82e..007a6d0b6965 100644 --- a/frontend/src/app/core/navigation/url-params.service.ts +++ b/frontend/src/app/core/navigation/url-params.service.ts @@ -26,15 +26,15 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { map, shareReplay, startWith } from 'rxjs/operators'; import { NavigationService } from 'core-app/core/navigation/navigation.service'; @Injectable({ providedIn: 'root' }) export class UrlParamsService { - constructor(private navigation:NavigationService) { - } + private navigation = inject(NavigationService); + public get(key:string):string|null { return this.searchParams.get(key); diff --git a/frontend/src/app/core/routing/openproject-router.module.ts b/frontend/src/app/core/routing/openproject-router.module.ts index 23e76ba35f76..33c6024ce702 100644 --- a/frontend/src/app/core/routing/openproject-router.module.ts +++ b/frontend/src/app/core/routing/openproject-router.module.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injector, NgModule } from '@angular/core'; +import { Injector, NgModule, inject } from '@angular/core'; import { FirstRouteService } from 'core-app/core/routing/first-route-service'; import { UIRouterModule } from '@uirouter/angular'; import { ApplicationBaseComponent } from 'core-app/core/routing/base/application-base.component'; @@ -52,7 +52,9 @@ import { ], }) export class OpenprojectRouterModule { - constructor(injector:Injector) { + constructor() { + const injector = inject(Injector); + initializeUiRouterListeners(injector); } } diff --git a/frontend/src/app/core/schemas/schema-cache.service.ts b/frontend/src/app/core/schemas/schema-cache.service.ts index c7583d096d79..6bd5a4e6be95 100644 --- a/frontend/src/app/core/schemas/schema-cache.service.ts +++ b/frontend/src/app/core/schemas/schema-cache.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ import { State } from '@openproject/reactivestates'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; import { firstValueFrom, Observable } from 'rxjs'; import { take } from 'rxjs/operators'; @@ -41,13 +41,17 @@ export const fallbackSchemaId = '__fallback'; @Injectable() export class SchemaCacheService extends StateCacheService { + readonly states:States; + readonly halResourceService = inject(HalResourceService); + fallbackSchema = this.halResourceService.createHalResourceOfClass(SchemaResource, {}, true); - constructor( - readonly states:States, - readonly halResourceService:HalResourceService, - ) { + constructor() { + const states = inject(States); + super(states.schemas); + this.states = states; + this.putValue(fallbackSchemaId, this.fallbackSchema); } diff --git a/frontend/src/app/core/setup/globals/components/admin/backup.component.ts b/frontend/src/app/core/setup/globals/components/admin/backup.component.ts index 46526cbc238a..2120d61fcf2a 100644 --- a/frontend/src/app/core/setup/globals/components/admin/backup.component.ts +++ b/frontend/src/app/core/setup/globals/components/admin/backup.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Injector, ViewChild } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Injector, ViewChild, inject } from '@angular/core'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; @@ -43,6 +43,13 @@ import { JobStatusModalService } from 'core-app/features/job-status/job-status-m standalone: false, }) export class BackupComponent implements AfterViewInit { + readonly elementRef = inject>(ElementRef); + injector = inject(Injector); + protected i18n = inject(I18nService); + protected toastService = inject(ToastService); + protected pathHelper = inject(PathHelperService); + protected jobStatusModalService = inject(JobStatusModalService); + public text = { info: this.i18n.t('js.backup.info'), note: this.i18n.t('js.backup.note'), @@ -72,14 +79,7 @@ export class BackupComponent implements AfterViewInit { @ViewChild('backupTokenInput') backupTokenInput:ElementRef; - constructor( - readonly elementRef:ElementRef, - public injector:Injector, - protected i18n:I18nService, - protected toastService:ToastService, - protected pathHelper:PathHelperService, - protected jobStatusModalService:JobStatusModalService, - ) { + constructor() { this.includeAttachments = this.mayIncludeAttachments; } diff --git a/frontend/src/app/core/state/resource-store.service.ts b/frontend/src/app/core/state/resource-store.service.ts index 144beb682819..629a745cd215 100644 --- a/frontend/src/app/core/state/resource-store.service.ts +++ b/frontend/src/app/core/state/resource-store.service.ts @@ -58,10 +58,7 @@ import { HttpClient, HttpErrorResponse, } from '@angular/common/http'; -import { - Injectable, - Injector, -} from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import idFromLink from 'core-app/features/hal/helpers/id-from-link'; @@ -76,18 +73,15 @@ export type ResourceKeyInput = ApiV3ListParameters|string; @Injectable() export abstract class ResourceStoreService { + readonly injector = inject(Injector); + readonly http = inject(HttpClient); + readonly apiV3Service = inject(ApiV3Service); + readonly toastService = inject(ToastService); + protected store:ResourceStore = this.createStore(); protected query = new QueryEntity(this.store); - constructor( - readonly injector:Injector, - readonly http:HttpClient, - readonly apiV3Service:ApiV3Service, - readonly toastService:ToastService, - ) { - } - /** * Require the results for the given filter params * Returns a cached set if it was loaded already. diff --git a/frontend/src/app/core/state/storage-files/storage-files.service.ts b/frontend/src/app/core/state/storage-files/storage-files.service.ts index dd8718b7bde9..15c652243620 100644 --- a/frontend/src/app/core/state/storage-files/storage-files.service.ts +++ b/frontend/src/app/core/state/storage-files/storage-files.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { combineLatest, Observable } from 'rxjs'; import { filter, map, take, tap, @@ -44,15 +44,13 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; @Injectable() export class StorageFilesResourceService { + private readonly httpClient = inject(HttpClient); + private readonly apiV3Service = inject(ApiV3Service); + private readonly store:StorageFilesStore = new StorageFilesStore(); private readonly query = new QueryEntity(this.store); - constructor( - private readonly httpClient:HttpClient, - private readonly apiV3Service:ApiV3Service, - ) {} - files(link:IHalResourceLink):Observable { const value = this.store.getValue().files[link.href]; if (value !== undefined) { diff --git a/frontend/src/app/core/top-menu/top-menu.service.ts b/frontend/src/app/core/top-menu/top-menu.service.ts index d05a13645aa4..283fcc34f268 100644 --- a/frontend/src/app/core/top-menu/top-menu.service.ts +++ b/frontend/src/app/core/top-menu/top-menu.service.ts @@ -26,10 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - Inject, - Injectable, -} from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { DOCUMENT } from '@angular/common'; import { isVisible } from 'core-app/shared/helpers/dom-helpers'; @@ -37,8 +34,8 @@ export const ANIMATION_RATE_MS = 100; @Injectable({ providedIn: 'root' }) export class TopMenuService { - constructor(@Inject(DOCUMENT) private document:Document) { - } + private document = inject(DOCUMENT); + register():void { this.skipContentClickListener(); diff --git a/frontend/src/app/core/turbo/turbo-requests.service.ts b/frontend/src/app/core/turbo/turbo-requests.service.ts index a1a40087ddcd..5afe6bbd1f06 100644 --- a/frontend/src/app/core/turbo/turbo-requests.service.ts +++ b/frontend/src/app/core/turbo/turbo-requests.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { renderStreamMessage } from '@hotwired/turbo'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { debugLog } from 'core-app/shared/helpers/debug_output'; @@ -7,13 +7,9 @@ import { getMetaContent } from '../setup/globals/global-helpers'; @Injectable({ providedIn: 'root' }) export class TurboRequestsService { - #controllers = new Map(); - - constructor( - private toast:ToastService, - ) { + private toast = inject(ToastService); - } + #controllers = new Map(); public request( url:string, diff --git a/frontend/src/app/features/admin/editable-query-props/editable-query-props.component.ts b/frontend/src/app/features/admin/editable-query-props/editable-query-props.component.ts index 07f0895d492d..18bc34b56499 100644 --- a/frontend/src/app/features/admin/editable-query-props/editable-query-props.component.ts +++ b/frontend/src/app/features/admin/editable-query-props/editable-query-props.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ExternalQueryConfigurationService, @@ -11,6 +11,11 @@ import { standalone: false, }) export class EditableQueryPropsComponent implements OnInit { + private elementRef = inject>(ElementRef); + private I18n = inject(I18nService); + private cdRef = inject(ChangeDetectorRef); + private externalQuery = inject(ExternalQueryConfigurationService); + id:string|null; name:string|null; @@ -23,14 +28,6 @@ export class EditableQueryPropsComponent implements OnInit { edit_query: this.I18n.t('js.admin.type_form.edit_query'), }; - constructor( - private elementRef:ElementRef, - private I18n:I18nService, - private cdRef:ChangeDetectorRef, - private externalQuery:ExternalQueryConfigurationService, - ) { - } - ngOnInit() { const element = this.elementRef.nativeElement; this.id = element.dataset.id!; diff --git a/frontend/src/app/features/bim/bcf/api/bcf-api.service.ts b/frontend/src/app/features/bim/bcf/api/bcf-api.service.ts index e2a4240ec594..0d07696892aa 100644 --- a/frontend/src/app/features/bim/bcf/api/bcf-api.service.ts +++ b/frontend/src/app/features/bim/bcf/api/bcf-api.service.ts @@ -26,12 +26,14 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { BcfResourceCollectionPath } from 'core-app/features/bim/bcf/api/bcf-path-resources'; import { BcfProjectPaths } from 'core-app/features/bim/bcf/api/projects/bcf-project.paths'; @Injectable({ providedIn: 'root' }) export class BcfApiService { + readonly injector = inject(Injector); + public readonly bcfApiVersion = '2.1'; public readonly appBasePath = window.appBasePath || ''; @@ -41,9 +43,6 @@ export class BcfApiService { // /api/bcf/:version/projects public readonly projects = new BcfResourceCollectionPath(this.injector, this.bcfApiBase, 'projects', BcfProjectPaths); - constructor(readonly injector:Injector) { - } - /** * Parse the given string into a BCF resource path * diff --git a/frontend/src/app/features/bim/bcf/api/bcf-authorization.service.ts b/frontend/src/app/features/bim/bcf/api/bcf-authorization.service.ts index 9c61b30fbfaf..aa35331f88b9 100644 --- a/frontend/src/app/features/bim/bcf/api/bcf-authorization.service.ts +++ b/frontend/src/app/features/bim/bcf/api/bcf-authorization.service.ts @@ -6,18 +6,17 @@ import { Observable, } from 'rxjs'; import { map } from 'rxjs/operators'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; export type AllowedExtensionKey = keyof BcfExtensionResource; @Injectable({ providedIn: 'root' }) export class BcfAuthorizationService { + readonly bcfApi = inject(BcfApiService); + // Poor mans caching to avoid repeatedly fetching from the backend. protected authorizationMap = multiInput(); - constructor(readonly bcfApi:BcfApiService) { - } - /** * Returns an observable boolean whether the given action * is authorized in the project by using the project extensions. diff --git a/frontend/src/app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service.ts b/frontend/src/app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service.ts index 8bf75d3e58b5..959341f36533 100644 --- a/frontend/src/app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service.ts +++ b/frontend/src/app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; @@ -7,6 +7,8 @@ import { CreateBcfViewpointData } from 'core-app/features/bim/bcf/api/bcf-api.mo @Injectable() export abstract class ViewerBridgeService { + readonly injector = inject(Injector); + @InjectField() state:StateService; /** @@ -14,8 +16,6 @@ export abstract class ViewerBridgeService { */ abstract shouldShowViewer:boolean; - protected constructor(readonly injector:Injector) {} - /** * Get a viewpoint from the viewer */ diff --git a/frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component.ts b/frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component.ts index 063c98c7dbd0..96f0a1bec561 100644 --- a/frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component.ts +++ b/frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component.ts @@ -26,17 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - Input, - OnDestroy, - OnInit, - Optional, - ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; import { StateService } from '@uirouter/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { NgxGalleryComponent, NgxGalleryOptions } from '@kolkov/ngx-gallery'; @@ -62,6 +52,17 @@ import { filter, take } from 'rxjs/operators'; standalone: false, }) export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements AfterViewInit, OnDestroy, OnInit { + readonly state = inject(StateService); + readonly bcfAuthorization = inject(BcfAuthorizationService); + readonly viewerBridge = inject(ViewerBridgeService); + readonly apiV3Service = inject(ApiV3Service); + readonly wpCreate = inject(WorkPackageCreateService); + readonly toastService = inject(ToastService); + readonly bcfViewer = inject(BcfViewService, { optional: true }); + readonly cdRef = inject(ChangeDetectorRef); + readonly I18n = inject(I18nService); + readonly viewpointsService = inject(ViewpointsService); + @Input() workPackage:WorkPackageResource; @ViewChild(NgxGalleryComponent) gallery:NgxGalleryComponent; @@ -146,19 +147,6 @@ export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements projectId:string; - constructor(readonly state:StateService, - readonly bcfAuthorization:BcfAuthorizationService, - readonly viewerBridge:ViewerBridgeService, - readonly apiV3Service:ApiV3Service, - readonly wpCreate:WorkPackageCreateService, - readonly toastService:ToastService, - @Optional() readonly bcfViewer:BcfViewService, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - readonly viewpointsService:ViewpointsService) { - super(); - } - ngAfterViewInit():void { // Observe changes on the work package to update the viewpoints this.observeChanges(); diff --git a/frontend/src/app/features/bim/bcf/helper/bcf-detector.service.ts b/frontend/src/app/features/bim/bcf/helper/bcf-detector.service.ts index 145649f21db5..5132a8a122d3 100644 --- a/frontend/src/app/features/bim/bcf/helper/bcf-detector.service.ts +++ b/frontend/src/app/features/bim/bcf/helper/bcf-detector.service.ts @@ -1,9 +1,9 @@ -import { Inject, Injectable, DOCUMENT } from '@angular/core'; +import { Injectable, DOCUMENT, inject } from '@angular/core'; @Injectable() export class BcfDetectorService { - constructor(@Inject(DOCUMENT) private documentElement:Document) { - } + private documentElement = inject(DOCUMENT); + /** * Detect whether the BCF module was activated, diff --git a/frontend/src/app/features/bim/bcf/helper/bcf-path-helper.service.ts b/frontend/src/app/features/bim/bcf/helper/bcf-path-helper.service.ts index dbfb644e2ccc..48df84990087 100644 --- a/frontend/src/app/features/bim/bcf/helper/bcf-path-helper.service.ts +++ b/frontend/src/app/features/bim/bcf/helper/bcf-path-helper.service.ts @@ -26,14 +26,14 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { HalLink } from 'core-app/features/hal/hal-link/hal-link'; @Injectable() export class BcfPathHelperService { - constructor(readonly pathHelper:PathHelperService) { - } + readonly pathHelper = inject(PathHelperService); + public projectImportIssuePath(projectIdentifier:string) { return `${this.pathHelper.projectPath(projectIdentifier)}/issues/upload`; diff --git a/frontend/src/app/features/bim/bcf/helper/viewpoints.service.ts b/frontend/src/app/features/bim/bcf/helper/viewpoints.service.ts index 1abc9b947ff9..0710f819261a 100644 --- a/frontend/src/app/features/bim/bcf/helper/viewpoints.service.ts +++ b/frontend/src/app/features/bim/bcf/helper/viewpoints.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { BcfApiService } from 'core-app/features/bim/bcf/api/bcf-api.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @@ -42,6 +42,8 @@ import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Injectable() export class ViewpointsService { + readonly injector = inject(Injector); + topicUUID:string|number|null = null; @InjectField() bcfApi:BcfApiService; @@ -50,8 +52,6 @@ export class ViewpointsService { @InjectField() apiV3Service:ApiV3Service; - constructor(readonly injector:Injector) { } - public getViewPointResource(workPackage:WorkPackageResource, index:number):BcfViewpointPaths { const viewpointHref = (workPackage.bcfViewpoints as HalResource[])[index].href!; diff --git a/frontend/src/app/features/bim/bcf/openproject-bcf.module.spec.ts b/frontend/src/app/features/bim/bcf/openproject-bcf.module.spec.ts new file mode 100644 index 000000000000..03da3a557143 --- /dev/null +++ b/frontend/src/app/features/bim/bcf/openproject-bcf.module.spec.ts @@ -0,0 +1,40 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See COPYRIGHT and LICENSE files for more details. +//++ + +import { Injector } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { IFCViewerService } from 'core-app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service'; +import { viewerBridgeServiceFactory } from 'core-app/features/bim/bcf/openproject-bcf.module'; + +describe('viewerBridgeServiceFactory', () => { + it('falls back to an IFC viewer service when none is registered', () => { + const injector = TestBed.inject(Injector); + + expect(viewerBridgeServiceFactory(injector)).toBeInstanceOf(IFCViewerService); + }); +}); diff --git a/frontend/src/app/features/bim/bcf/openproject-bcf.module.ts b/frontend/src/app/features/bim/bcf/openproject-bcf.module.ts index 970eff0859e4..a9b06546912f 100644 --- a/frontend/src/app/features/bim/bcf/openproject-bcf.module.ts +++ b/frontend/src/app/features/bim/bcf/openproject-bcf.module.ts @@ -29,6 +29,8 @@ import { Injector, NgModule, + inject, + runInInjectionContext, } from '@angular/core'; import { OpSharedModule } from 'core-app/shared/shared.module'; import { NgxGalleryModule } from '@kolkov/ngx-gallery'; @@ -57,9 +59,12 @@ import { RefreshButtonComponent } from 'core-app/features/bim/ifc_models/toolbar */ export const viewerBridgeServiceFactory = (injector:Injector) => { if (window.navigator.userAgent.search('Revit') > -1) { - return new RevitBridgeService(injector); + return runInInjectionContext(injector, () => new RevitBridgeService()); } - return injector.get(IFCViewerService, new IFCViewerService(injector)); + + const ifcViewerService = injector.get(IFCViewerService, null); + + return ifcViewerService ?? runInInjectionContext(injector, () => new IFCViewerService()); }; @NgModule({ @@ -93,7 +98,9 @@ export const viewerBridgeServiceFactory = (injector:Injector) => { export class OpenprojectBcfModule { static bootstrapCalled = false; - constructor(injector:Injector) { + constructor() { + const injector = inject(Injector); + OpenprojectBcfModule.bootstrap(injector); } diff --git a/frontend/src/app/features/bim/ifc_models/bcf/split/left/bcf-split-left.component.ts b/frontend/src/app/features/bim/ifc_models/bcf/split/left/bcf-split-left.component.ts index 1e4323d9d6bb..b7d7090cb83f 100644 --- a/frontend/src/app/features/bim/ifc_models/bcf/split/left/bcf-split-left.component.ts +++ b/frontend/src/app/features/bim/ifc_models/bcf/split/left/bcf-split-left.component.ts @@ -26,11 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { BcfViewService } from 'core-app/features/bim/ifc_models/pages/viewer/bcf-view.service'; @@ -42,9 +38,9 @@ import { BcfViewService } from 'core-app/features/bim/ifc_models/pages/viewer/bc standalone: false, }) export class BcfSplitLeftComponent implements OnInit { - showViewer$:Observable; + private readonly bcfView = inject(BcfViewService); - constructor(private readonly bcfView:BcfViewService) {} + showViewer$:Observable; ngOnInit():void { this.showViewer$ = this.bcfView.live$() diff --git a/frontend/src/app/features/bim/ifc_models/bcf/split/right/bcf-split-right.component.ts b/frontend/src/app/features/bim/ifc_models/bcf/split/right/bcf-split-right.component.ts index 9efdbacf640c..3e589d9ca3f0 100644 --- a/frontend/src/app/features/bim/ifc_models/bcf/split/right/bcf-split-right.component.ts +++ b/frontend/src/app/features/bim/ifc_models/bcf/split/right/bcf-split-right.component.ts @@ -26,11 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { BcfViewService } from 'core-app/features/bim/ifc_models/pages/viewer/bcf-view.service'; import { map } from 'rxjs/operators'; @@ -42,9 +38,9 @@ import { map } from 'rxjs/operators'; standalone: false, }) export class BcfSplitRightComponent implements OnInit { - showWorkPackages$:Observable; + private readonly bcfView = inject(BcfViewService); - constructor(private readonly bcfView:BcfViewService) {} + showWorkPackages$:Observable; ngOnInit():void { this.showWorkPackages$ = this.bcfView.live$() diff --git a/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.component.ts b/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.component.ts index 30ec7a409f18..a09018887e1b 100644 --- a/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.component.ts +++ b/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.component.ts @@ -26,16 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - Component, - ElementRef, - HostListener, - OnDestroy, - OnInit, - ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, HostListener, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; import { IFCViewerService } from 'core-app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service'; import { IfcModelsDataService } from 'core-app/features/bim/ifc_models/pages/viewer/ifc-models-data.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -56,6 +47,12 @@ import { filter, take } from 'rxjs/operators'; standalone: false, }) export class IFCViewerComponent implements OnInit, OnDestroy, AfterViewInit { + ifcData = inject(IfcModelsDataService); + private I18n = inject(I18nService); + private ifcViewerService = inject(IFCViewerService); + private currentUserService = inject(CurrentUserService); + private currentProjectService = inject(CurrentProjectService); + private viewInitialized$ = new Subject(); modelCount:number = this.ifcData.models.length; @@ -86,14 +83,6 @@ export class IFCViewerComponent implements OnInit, OnDestroy, AfterViewInit { @ViewChild('xeokitToolbarIcons') xeokitToolbarIcons:ElementRef; - constructor( - public ifcData:IfcModelsDataService, - private I18n:I18nService, - private ifcViewerService:IFCViewerService, - private currentUserService:CurrentUserService, - private currentProjectService:CurrentProjectService, - ) { } - ngOnInit():void { if (this.modelCount === 0) { return; diff --git a/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service.ts b/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service.ts index e7823f88ccc1..cc5fb8360ca2 100644 --- a/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service.ts +++ b/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable } from '@angular/core'; import { XeokitServer } from 'core-app/features/bim/ifc_models/xeokit/xeokit-server'; import { ViewerBridgeService } from 'core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service'; import { BehaviorSubject, Observable, of } from 'rxjs'; @@ -123,10 +123,6 @@ export class IFCViewerService extends ViewerBridgeService { @InjectField() httpClient:HttpClient; - constructor(readonly injector:Injector) { - super(injector); - } - public newViewer(elements:XeokitElements, projects:IfcProjectDefinition[]):void { const server = new XeokitServer(this.pathHelper, this.ifcModelsDataService); // eslint-disable-next-line @typescript-eslint/no-unsafe-call diff --git a/frontend/src/app/features/bim/ifc_models/pages/viewer/bcf-view.service.ts b/frontend/src/app/features/bim/ifc_models/pages/viewer/bcf-view.service.ts index 296358ff4435..119c1f63b649 100644 --- a/frontend/src/app/features/bim/ifc_models/pages/viewer/bcf-view.service.ts +++ b/frontend/src/app/features/bim/ifc_models/pages/viewer/bcf-view.service.ts @@ -26,10 +26,9 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageQueryStateService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { ViewerBridgeService } from 'core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service'; @@ -43,6 +42,9 @@ export type BcfViewState = 'cards'|'viewer'|'splitTable'|'splitCards'|'table'; @Injectable() export class BcfViewService extends WorkPackageQueryStateService { + private readonly I18n = inject(I18nService); + private readonly viewerBridgeService = inject(ViewerBridgeService); + public text:Record = { cards: this.I18n.t('js.views.card'), viewer: this.I18n.t('js.ifc_models.views.viewer'), @@ -59,14 +61,6 @@ export class BcfViewService extends WorkPackageQueryStateService { table: 'icon-view-list', }; - constructor( - private readonly I18n:I18nService, - private readonly viewerBridgeService:ViewerBridgeService, - protected readonly querySpace:IsolatedQuerySpace, - ) { - super(querySpace); - } - hasChanged(query:QueryResource):boolean { return this.current !== query.displayRepresentation; } diff --git a/frontend/src/app/features/bim/ifc_models/pages/viewer/ifc-viewer-page.component.ts b/frontend/src/app/features/bim/ifc_models/pages/viewer/ifc-viewer-page.component.ts index 1353dca3c8e8..d0ad8893cdbd 100644 --- a/frontend/src/app/features/bim/ifc_models/pages/viewer/ifc-viewer-page.component.ts +++ b/frontend/src/app/features/bim/ifc_models/pages/viewer/ifc-viewer-page.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Injector, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, OnDestroy, ViewEncapsulation, inject } from '@angular/core'; import { PartitionedQuerySpacePageComponent, @@ -94,6 +94,10 @@ import { export class IFCViewerPageComponent extends PartitionedQuerySpacePageComponent implements UntilDestroyedMixin, OnInit, OnDestroy { + readonly ifcData = inject(IfcModelsDataService); + readonly bcfView = inject(BcfViewService); + readonly viewerBridgeService = inject(ViewerBridgeService); + text = { title: this.I18n.t('js.bcf.management'), delete: this.I18n.t('js.button_delete'), @@ -157,15 +161,6 @@ export class IFCViewerPageComponent // eslint-disable-next-line @typescript-eslint/ban-types private removeSubscription:Function; - constructor( - readonly ifcData:IfcModelsDataService, - readonly bcfView:BcfViewService, - readonly injector:Injector, - readonly viewerBridgeService:ViewerBridgeService, - ) { - super(injector); - } - ngOnInit():void { super.ngOnInit(); diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component.ts b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component.ts index 1bbb9f851899..192ceef3c21c 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component.ts @@ -26,9 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnDestroy, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnDestroy, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { BcfPathHelperService } from 'core-app/features/bim/bcf/helper/bcf-path-helper.service'; @@ -59,6 +57,18 @@ import { JobStatusModalService } from 'core-app/features/job-status/job-status-m changeDetection: ChangeDetectionStrategy.Default, }) export class BcfExportButtonComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { + readonly I18n = inject(I18nService); + readonly currentProject = inject(CurrentProjectService); + readonly bcfPathHelper = inject(BcfPathHelperService); + readonly querySpace = inject(IsolatedQuerySpace); + readonly queryUrlParamsHelper = inject(UrlParamsHelperService); + readonly jobStatusModalService = inject(JobStatusModalService); + readonly httpClient = inject(HttpClient); + readonly injector = inject(Injector); + readonly toastService = inject(ToastService); + readonly state = inject(StateService); + readonly cdRef = inject(ChangeDetectorRef); + public text = { export: this.I18n.t('js.bcf.export'), export_hover: this.I18n.t('js.bcf.export_bcf_xml_file'), @@ -68,20 +78,6 @@ export class BcfExportButtonComponent extends UntilDestroyedMixin implements OnI public exportLink:string; - constructor(readonly I18n:I18nService, - readonly currentProject:CurrentProjectService, - readonly bcfPathHelper:BcfPathHelperService, - readonly querySpace:IsolatedQuerySpace, - readonly queryUrlParamsHelper:UrlParamsHelperService, - readonly jobStatusModalService:JobStatusModalService, - readonly httpClient:HttpClient, - readonly injector:Injector, - readonly toastService:ToastService, - readonly state:StateService, - readonly cdRef:ChangeDetectorRef) { - super(); - } - ngOnInit() { this.querySpace.query .values$() diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component.ts b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component.ts index b320357f6ba0..fcc7f775053c 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { BcfPathHelperService } from 'core-app/features/bim/bcf/helper/bcf-path-helper.service'; @@ -48,16 +48,15 @@ import { BcfPathHelperService } from 'core-app/features/bim/bcf/helper/bcf-path- changeDetection: ChangeDetectionStrategy.Default, }) export class BcfImportButtonComponent { + readonly I18n = inject(I18nService); + readonly currentProject = inject(CurrentProjectService); + readonly bcfPathHelper = inject(BcfPathHelperService); + public text = { import: this.I18n.t('js.bcf.import'), import_hover: this.I18n.t('js.bcf.import_bcf_xml_file'), }; - constructor(readonly I18n:I18nService, - readonly currentProject:CurrentProjectService, - readonly bcfPathHelper:BcfPathHelperService) { - } - public handleClick() { const projectIdentifier = this.currentProject.identifier; if (projectIdentifier) { diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/refresh-button.component.ts b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/refresh-button.component.ts index daee1e4ca5b5..668d5baaae7c 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/refresh-button.component.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/refresh-button.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { StateService } from '@uirouter/core'; @@ -43,15 +43,14 @@ import { StateService } from '@uirouter/core'; standalone: false, }) export class RefreshButtonComponent { + readonly I18n = inject(I18nService); + readonly state = inject(StateService); + public text = { refresh: this.I18n.t('js.bcf.refresh'), refresh_hover: this.I18n.t('js.bcf.refresh_work_package'), }; - constructor(readonly I18n:I18nService, - readonly state:StateService) { - } - refresh() { void this.state.go('.', {}, { reload: true }); } diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/manage-ifc-models-button/bim-manage-ifc-models-button.component.ts b/frontend/src/app/features/bim/ifc_models/toolbar/manage-ifc-models-button/bim-manage-ifc-models-button.component.ts index e030998bc457..dbd75aed06c6 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/manage-ifc-models-button/bim-manage-ifc-models-button.component.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/manage-ifc-models-button/bim-manage-ifc-models-button.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { IfcModelsDataService } from 'core-app/features/bim/ifc_models/pages/viewer/ifc-models-data.service'; @@ -49,6 +49,9 @@ import { IfcModelsDataService } from 'core-app/features/bim/ifc_models/pages/vie standalone: false, }) export class BimManageIfcModelsButtonComponent { + readonly I18n = inject(I18nService); + readonly ifcData = inject(IfcModelsDataService); + text = { manage: this.I18n.t('js.ifc_models.models.ifc_models'), }; @@ -56,8 +59,4 @@ export class BimManageIfcModelsButtonComponent { manageAllowed = this.ifcData.allowed('manage_ifc_models'); manageIFCPath = this.ifcData.manageIFCPath; - - constructor(readonly I18n:I18nService, - readonly ifcData:IfcModelsDataService) { - } } diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bcf-view-toggle-button.component.ts b/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bcf-view-toggle-button.component.ts index 30dc8d31100e..511675ecaf28 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bcf-view-toggle-button.component.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bcf-view-toggle-button.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { BcfViewService } from 'core-app/features/bim/ifc_models/pages/viewer/bcf-view.service'; @@ -50,7 +50,8 @@ import { BcfViewService } from 'core-app/features/bim/ifc_models/pages/viewer/bc standalone: false, }) export class BcfViewToggleButtonComponent { - view$ = this.bcfView.live$(); + readonly I18n = inject(I18nService); + readonly bcfView = inject(BcfViewService); - constructor(readonly I18n:I18nService, readonly bcfView:BcfViewService) { } + view$ = this.bcfView.live$(); } diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bcf-view-toggle-dropdown.directive.ts b/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bcf-view-toggle-dropdown.directive.ts index 3778371b6880..c67ea712bddf 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bcf-view-toggle-dropdown.directive.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bcf-view-toggle-dropdown.directive.ts @@ -26,8 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; -import { Directive, ElementRef } from '@angular/core'; +import { Directive, inject } from '@angular/core'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { StateService } from '@uirouter/core'; @@ -48,15 +47,11 @@ import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op standalone: false, }) export class BcfViewToggleDropdownDirective extends OpContextMenuTrigger { - constructor(readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly bcfView:BcfViewService, - readonly I18n:I18nService, - readonly state:StateService, - readonly wpFiltersService:WorkPackageFiltersService, - readonly viewerBridgeService:ViewerBridgeService) { - super(elementRef, opContextMenu); - } + readonly bcfView = inject(BcfViewService); + readonly I18n = inject(I18nService); + readonly state = inject(StateService); + readonly wpFiltersService = inject(WorkPackageFiltersService); + readonly viewerBridgeService = inject(ViewerBridgeService); protected open(evt:Event):void { this.buildItems(); diff --git a/frontend/src/app/features/bim/revit_add_in/revit-add-in-settings-button.service.ts b/frontend/src/app/features/bim/revit_add_in/revit-add-in-settings-button.service.ts index 82622e630fca..1c4ca9dc4f1d 100644 --- a/frontend/src/app/features/bim/revit_add_in/revit-add-in-settings-button.service.ts +++ b/frontend/src/app/features/bim/revit_add_in/revit-add-in-settings-button.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -36,13 +36,15 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; */ @Injectable() export class RevitAddInSettingsButtonService { + private readonly i18n = inject(I18nService); + private readonly labelText:string; private readonly groupLabelText:string; - constructor( - private readonly i18n:I18nService, - ) { + constructor() { + const i18n = this.i18n; + const onRevitAddInEnvironment = window.navigator.userAgent.search('Revit') > -1; if (onRevitAddInEnvironment) { diff --git a/frontend/src/app/features/bim/revit_add_in/revit-bridge.service.ts b/frontend/src/app/features/bim/revit_add_in/revit-bridge.service.ts index 554797b49d89..0af2f06a801a 100644 --- a/frontend/src/app/features/bim/revit_add_in/revit-bridge.service.ts +++ b/frontend/src/app/features/bim/revit_add_in/revit-bridge.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { distinctUntilChanged, filter, first, map, @@ -66,8 +66,8 @@ export class RevitBridgeService extends ViewerBridgeService { revitMessageReceived$ = this.revitMessageReceivedSource.asObservable(); - constructor(readonly injector:Injector) { - super(injector); + constructor() { + super(); if (window.RevitBridge) { this.hookUpRevitListener(); diff --git a/frontend/src/app/features/boards/board/add-card-dropdown/add-card-dropdown-menu.directive.ts b/frontend/src/app/features/boards/board/add-card-dropdown/add-card-dropdown-menu.directive.ts index 7ca7cdeac5c9..db05068e6865 100644 --- a/frontend/src/app/features/boards/board/add-card-dropdown/add-card-dropdown-menu.directive.ts +++ b/frontend/src/app/features/boards/board/add-card-dropdown/add-card-dropdown-menu.directive.ts @@ -26,13 +26,10 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectorRef, Directive, ElementRef, Injector, -} from '@angular/core'; +import { ChangeDetectorRef, Directive, Injector, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; @@ -43,18 +40,14 @@ import { BoardListComponent } from 'core-app/features/boards/board/board-list/bo standalone: false, }) export class AddCardDropdownMenuDirective extends OpContextMenuTrigger { - constructor(readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly opModalService:OpModalService, - readonly authorisationService:AuthorisationService, - readonly wpInlineCreate:WorkPackageInlineCreateService, - readonly boardList:BoardListComponent, - readonly injector:Injector, - readonly querySpace:IsolatedQuerySpace, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService) { - super(elementRef, opContextMenu); - } + readonly opModalService = inject(OpModalService); + readonly authorisationService = inject(AuthorisationService); + readonly wpInlineCreate = inject(WorkPackageInlineCreateService); + readonly boardList = inject(BoardListComponent); + readonly injector = inject(Injector); + readonly querySpace = inject(IsolatedQuerySpace); + readonly cdRef = inject(ChangeDetectorRef); + readonly I18n = inject(I18nService); protected open(evt:Event) { this.items = this.buildItems(); diff --git a/frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.ts b/frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.ts index 5c477b04f120..96d9a9d900c1 100644 --- a/frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.ts +++ b/frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.ts @@ -26,12 +26,8 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnInit, ViewChild, -} from '@angular/core'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { ChangeDetectionStrategy, Component, OnInit, ViewChild, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { Board } from 'core-app/features/boards/board/board'; import { BoardService } from 'core-app/features/boards/board/board.service'; @@ -58,6 +54,14 @@ import { HalResource } from 'core-app/features/hal/resources/hal-resource'; changeDetection: ChangeDetectionStrategy.Default, }) export class AddListModalComponent extends OpModalComponent implements OnInit { + readonly boardActions = inject(BoardActionsRegistryService); + readonly halNotification = inject(HalResourceNotificationService); + readonly boardService = inject(BoardService); + readonly I18n = inject(I18nService); + readonly apiV3Service = inject(ApiV3Service); + readonly currentProject = inject(CurrentProjectService); + readonly pathHelper = inject(PathHelperService); + @ViewChild(OpAutocompleterComponent, { static: true }) public ngSelectComponent:OpAutocompleterComponent; getAutocompleterData = (searchTerm:string):Observable => { @@ -112,19 +116,6 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { /** Whether the no results warning is displayed */ showWarning = false; - constructor(readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly boardActions:BoardActionsRegistryService, - readonly halNotification:HalResourceNotificationService, - readonly boardService:BoardService, - readonly I18n:I18nService, - readonly apiV3Service:ApiV3Service, - readonly currentProject:CurrentProjectService, - readonly pathHelper:PathHelperService) { - super(locals, cdRef, elementRef); - } - ngOnInit() { super.ngOnInit(); this.board = this.locals.board; diff --git a/frontend/src/app/features/boards/board/board-actions/assignee/assignee-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/assignee/assignee-board-header.component.ts index 7321bcf61b47..710fa05af140 100644 --- a/frontend/src/app/features/boards/board/board-actions/assignee/assignee-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/assignee/assignee-board-header.component.ts @@ -25,7 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { UserResource } from 'core-app/features/hal/resources/user-resource'; @@ -41,13 +41,12 @@ import { UserResource } from 'core-app/features/hal/resources/user-resource'; changeDetection: ChangeDetectionStrategy.Default, }) export class AssigneeBoardHeaderComponent { + readonly pathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); + @Input('resource') public user:UserResource; text = { assignee: this.I18n.t('js.work_packages.properties.assignee'), }; - - constructor(readonly pathHelper:PathHelperService, - readonly I18n:I18nService) { - } } diff --git a/frontend/src/app/features/boards/board/board-actions/board-action.service.ts b/frontend/src/app/features/boards/board/board-actions/board-action.service.ts index ef1e4f0e361a..1d285a6db2dd 100644 --- a/frontend/src/app/features/boards/board/board-actions/board-action.service.ts +++ b/frontend/src/app/features/boards/board/board-actions/board-action.service.ts @@ -7,7 +7,7 @@ import { BoardListsService } from 'core-app/features/boards/board/board-list/boa import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { map } from 'rxjs/operators'; import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; @@ -25,15 +25,15 @@ import idFromLink from 'core-app/features/hal/helpers/id-from-link'; @Injectable() export abstract class BoardActionService { - constructor(readonly injector:Injector, - protected boardListsService:BoardListsService, - protected I18n:I18nService, - protected halResourceService:HalResourceService, - protected pathHelper:PathHelperService, - protected currentProject:CurrentProjectService, - protected apiV3Service:ApiV3Service, - protected schemaCache:SchemaCacheService) { - } + readonly injector = inject(Injector); + protected boardListsService = inject(BoardListsService); + protected I18n = inject(I18nService); + protected halResourceService = inject(HalResourceService); + protected pathHelper = inject(PathHelperService); + protected currentProject = inject(CurrentProjectService); + protected apiV3Service = inject(ApiV3Service); + protected schemaCache = inject(SchemaCacheService); + /** * Get the attribute name diff --git a/frontend/src/app/features/boards/board/board-actions/status/status-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/status/status-board-header.component.ts index cfdab8c28fc0..ff98979b178e 100644 --- a/frontend/src/app/features/boards/board/board-actions/status/status-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/status/status-board-header.component.ts @@ -25,7 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { StatusResource } from 'core-app/features/hal/resources/status-resource'; @@ -40,12 +40,11 @@ import { StatusResource } from 'core-app/features/hal/resources/status-resource' changeDetection: ChangeDetectionStrategy.Default, }) export class StatusBoardHeaderComponent { + readonly I18n = inject(I18nService); + @Input('resource') public status:StatusResource; text = { status: this.I18n.t('js.work_packages.properties.status'), }; - - constructor(readonly I18n:I18nService) { - } } diff --git a/frontend/src/app/features/boards/board/board-actions/subproject/subproject-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/subproject/subproject-board-header.component.ts index 27fe9a979fc6..3ca13c191aee 100644 --- a/frontend/src/app/features/boards/board/board-actions/subproject/subproject-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/subproject/subproject-board-header.component.ts @@ -25,7 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @@ -42,6 +42,9 @@ import idFromLink from 'core-app/features/hal/helpers/id-from-link'; changeDetection: ChangeDetectionStrategy.Default, }) export class SubprojectBoardHeaderComponent { + readonly pathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); + @Input() public resource:HalResource; idFromLink = idFromLink; @@ -49,8 +52,4 @@ export class SubprojectBoardHeaderComponent { text = { project: this.I18n.t('js.label_project'), }; - - constructor(readonly pathHelper:PathHelperService, - readonly I18n:I18nService) { - } } diff --git a/frontend/src/app/features/boards/board/board-actions/subtasks/subtasks-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/subtasks/subtasks-board-header.component.ts index 04040a176233..52253bd44141 100644 --- a/frontend/src/app/features/boards/board/board-actions/subtasks/subtasks-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/subtasks/subtasks-board-header.component.ts @@ -25,7 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @@ -43,6 +43,9 @@ import idFromLink from 'core-app/features/hal/helpers/id-from-link'; changeDetection: ChangeDetectionStrategy.Default, }) export class SubtasksBoardHeaderComponent implements OnInit { + readonly pathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); + @Input() public resource:WorkPackageResource; idFromLink = idFromLink; @@ -53,10 +56,6 @@ export class SubtasksBoardHeaderComponent implements OnInit { typeHighlightingClass:string; - constructor(readonly pathHelper:PathHelperService, - readonly I18n:I18nService) { - } - ngOnInit() { this.typeHighlightingClass = Highlighting.inlineClass('type', this.resource.type.id!); } diff --git a/frontend/src/app/features/boards/board/board-actions/version/version-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/version/version-board-header.component.ts index d57d88bc7324..5c1a79bf5a08 100644 --- a/frontend/src/app/features/boards/board/board-actions/version/version-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/version/version-board-header.component.ts @@ -25,7 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, inject } from '@angular/core'; import { VersionResource } from 'core-app/features/hal/resources/version-resource'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -41,11 +41,11 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service changeDetection: ChangeDetectionStrategy.Default, }) export class VersionBoardHeaderComponent { - @Input('resource') public version:VersionResource; + readonly I18n = inject(I18nService); + readonly pathHelper = inject(PathHelperService); - constructor(readonly I18n:I18nService, - readonly pathHelper:PathHelperService) { - } + // eslint-disable-next-line @angular-eslint/no-input-rename + @Input('resource') public version:VersionResource; public text = { isLocked: this.I18n.t('js.boards.version.is_locked'), diff --git a/frontend/src/app/features/boards/board/board-filter/board-filter.component.ts b/frontend/src/app/features/boards/board/board-filter/board-filter.component.ts index 0a3ec34be8f5..1bae1325dfbe 100644 --- a/frontend/src/app/features/boards/board/board-filter/board-filter.component.ts +++ b/frontend/src/app/features/boards/board/board-filter/board-filter.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, Input, inject } from '@angular/core'; import { Board } from 'core-app/features/boards/board/board'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { WorkPackageStatesInitializationService } from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service'; @@ -23,22 +23,20 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class BoardFilterComponent extends UntilDestroyedMixin implements AfterViewInit { + private readonly currentProjectService = inject(CurrentProjectService); + private readonly querySpace = inject(IsolatedQuerySpace); + private readonly apiV3Service = inject(ApiV3Service); + private readonly halResourceService = inject(HalResourceService); + private readonly wpStatesInitialization = inject(WorkPackageStatesInitializationService); + private readonly wpTableFilters = inject(WorkPackageViewFiltersService); + private readonly urlParamsHelper = inject(UrlParamsHelperService); + private readonly boardFilters = inject(BoardFiltersService); + /** Current active */ @Input() public board$:Observable; initialized = false; - constructor(private readonly currentProjectService:CurrentProjectService, - private readonly querySpace:IsolatedQuerySpace, - private readonly apiV3Service:ApiV3Service, - private readonly halResourceService:HalResourceService, - private readonly wpStatesInitialization:WorkPackageStatesInitializationService, - private readonly wpTableFilters:WorkPackageViewFiltersService, - private readonly urlParamsHelper:UrlParamsHelperService, - private readonly boardFilters:BoardFiltersService) { - super(); - } - ngAfterViewInit():void { if (!this.board$) { return; diff --git a/frontend/src/app/features/boards/board/board-list/board-inline-create.service.ts b/frontend/src/app/features/boards/board/board-list/board-inline-create.service.ts index cd2d0d53ffe7..410ff8ebefa8 100644 --- a/frontend/src/app/features/boards/board/board-list/board-inline-create.service.ts +++ b/frontend/src/app/features/boards/board/board-list/board-inline-create.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { WorkPackageInlineCreateService, @@ -40,13 +40,8 @@ import { Observable } from 'rxjs'; @Injectable() export class BoardInlineCreateService extends WorkPackageInlineCreateService { - constructor( - readonly injector:Injector, - protected readonly querySpace:IsolatedQuerySpace, - protected readonly halResourceService:HalResourceService, - ) { - super(injector); - } + protected readonly querySpace = inject(IsolatedQuerySpace); + protected readonly halResourceService = inject(HalResourceService); /** * A separate reference pane for the inline create component diff --git a/frontend/src/app/features/boards/board/board-list/board-list-menu.component.ts b/frontend/src/app/features/boards/board/board-list/board-list-menu.component.ts index 6c1ee33907ba..cb83ba894c2d 100644 --- a/frontend/src/app/features/boards/board/board-list/board-list-menu.component.ts +++ b/frontend/src/app/features/boards/board/board-list/board-list-menu.component.ts @@ -26,9 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, Component, EventEmitter, Input, Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; @@ -49,18 +47,17 @@ import { BoardActionService } from 'core-app/features/boards/board/board-actions changeDetection: ChangeDetectionStrategy.Default, }) export class BoardListMenuComponent { + readonly opModalService = inject(OpModalService); + readonly authorisationService = inject(AuthorisationService); + private readonly querySpace = inject(IsolatedQuerySpace); + private readonly boardService = inject(BoardService); + private readonly boardActionRegistry = inject(BoardActionsRegistryService); + readonly I18n = inject(I18nService); + @Input() board:Board; @Output() onRemove = new EventEmitter(); - constructor(readonly opModalService:OpModalService, - readonly authorisationService:AuthorisationService, - private readonly querySpace:IsolatedQuerySpace, - private readonly boardService:BoardService, - private readonly boardActionRegistry:BoardActionsRegistryService, - readonly I18n:I18nService) { - } - public get menuItems() { return async () => { const items:OpContextMenuItem[] = [ diff --git a/frontend/src/app/features/boards/board/board-list/board-list.component.ts b/frontend/src/app/features/boards/board/board-list/board-list.component.ts index cf85cae64e26..6b3df5dc4f6e 100644 --- a/frontend/src/app/features/boards/board/board-list/board-list.component.ts +++ b/frontend/src/app/features/boards/board/board-list/board-list.component.ts @@ -5,7 +5,6 @@ import { ElementRef, EventEmitter, inject, - Injector, Input, OnDestroy, OnInit, @@ -19,7 +18,6 @@ import { import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; import { BoardInlineCreateService } from 'core-app/features/boards/board/board-list/board-inline-create.service'; import { AbstractWidgetComponent } from 'core-app/shared/components/grids/widgets/abstract-widget.component'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { Board } from 'core-app/features/boards/board/board'; @@ -89,6 +87,31 @@ export interface DisabledButtonPlaceholder { standalone: false, }) export class BoardListComponent extends AbstractWidgetComponent implements OnInit, OnDestroy { + readonly apiv3Service = inject(ApiV3Service); + readonly state = inject(StateService); + readonly cdRef = inject(ChangeDetectorRef); + readonly transitions = inject(TransitionService); + readonly boardFilters = inject(BoardFiltersService); + readonly toastService = inject(ToastService); + readonly querySpace = inject(IsolatedQuerySpace); + readonly halNotification = inject(HalResourceNotificationService); + readonly halEvents = inject(HalEventsService); + readonly wpStatesInitialization = inject(WorkPackageStatesInitializationService); + readonly wpViewFocusService = inject(WorkPackageViewFocusService); + readonly wpViewSelectionService = inject(WorkPackageViewSelectionService); + readonly boardListCrossSelectionService = inject(BoardListCrossSelectionService); + readonly authorisationService = inject(AuthorisationService); + readonly wpInlineCreate = inject(WorkPackageInlineCreateService); + readonly halEditing = inject(HalResourceEditingService); + readonly loadingIndicator = inject(LoadingIndicatorService); + readonly schemaCache = inject(SchemaCacheService); + readonly boardService = inject(BoardService); + readonly boardActionRegistry = inject(BoardActionsRegistryService); + readonly causedUpdates = inject(CausedUpdatesService); + readonly keepTab = inject(KeepTabService); + readonly currentProject = inject(CurrentProjectService); + readonly pathHelper = inject(PathHelperService); + /** Output fired upon query removal */ @Output() onRemove = new EventEmitter(); @@ -127,13 +150,15 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni public columnsQueryProps:any; - public text = { - addCard: this.I18n.t('js.boards.add_card'), - updateSuccessful: this.I18n.t('js.notice_successful_update'), - areYouSure: this.I18n.t('js.text_are_you_sure'), - unnamed_list: this.I18n.t('js.boards.label_unnamed_list'), - click_to_remove: this.I18n.t('js.boards.click_to_remove_list'), - }; + public get text() { + return { + addCard: this.i18n.t('js.boards.add_card'), + updateSuccessful: this.i18n.t('js.notice_successful_update'), + areYouSure: this.i18n.t('js.text_are_you_sure'), + unnamed_list: this.i18n.t('js.boards.label_unnamed_list'), + click_to_remove: this.i18n.t('js.boards.click_to_remove_list'), + }; + } /** Are we allowed to remove and drag & drop elements ? */ public canDragInto = false; @@ -153,37 +178,6 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni private readonly states = inject(States); - constructor( - readonly apiv3Service:ApiV3Service, - readonly I18n:I18nService, - readonly state:StateService, - readonly cdRef:ChangeDetectorRef, - readonly transitions:TransitionService, - readonly boardFilters:BoardFiltersService, - readonly toastService:ToastService, - readonly querySpace:IsolatedQuerySpace, - readonly halNotification:HalResourceNotificationService, - readonly halEvents:HalEventsService, - readonly wpStatesInitialization:WorkPackageStatesInitializationService, - readonly wpViewFocusService:WorkPackageViewFocusService, - readonly wpViewSelectionService:WorkPackageViewSelectionService, - readonly boardListCrossSelectionService:BoardListCrossSelectionService, - readonly authorisationService:AuthorisationService, - readonly wpInlineCreate:WorkPackageInlineCreateService, - readonly injector:Injector, - readonly halEditing:HalResourceEditingService, - readonly loadingIndicator:LoadingIndicatorService, - readonly schemaCache:SchemaCacheService, - readonly boardService:BoardService, - readonly boardActionRegistry:BoardActionsRegistryService, - readonly causedUpdates:CausedUpdatesService, - readonly keepTab:KeepTabService, - readonly currentProject:CurrentProjectService, - readonly pathHelper:PathHelperService, - ) { - super(I18n, injector); - } - ngOnInit():void { // Unset the isNew flag this.initiallyFocused = this.resource.isNewWidget; @@ -257,7 +251,7 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni } public get errorMessage() { - return this.I18n.t('js.boards.error_loading_the_list', { error_message: this.loadingError }); + return this.i18n.t('js.boards.error_loading_the_list', { error_message: this.loadingError }); } public canMove(workPackage:WorkPackageResource) { diff --git a/frontend/src/app/features/boards/board/board-list/board-lists.service.ts b/frontend/src/app/features/boards/board/board-list/board-lists.service.ts index 86bbbd2a378b..8c6c63adec17 100644 --- a/frontend/src/app/features/boards/board/board-list/board-lists.service.ts +++ b/frontend/src/app/features/boards/board/board-list/board-lists.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; @@ -19,17 +19,14 @@ import { IOPFieldSchema } from 'core-app/features/hal/interfaces'; @Injectable({ providedIn: 'root' }) export class BoardListsService { - private v3 = this.pathHelper.api.v3; - - constructor( - private readonly CurrentProject:CurrentProjectService, - private readonly pathHelper:PathHelperService, - private readonly apiV3Service:ApiV3Service, - private readonly halResourceService:HalResourceService, - private readonly toastService:ToastService, - private readonly I18n:I18nService) { + private readonly CurrentProject = inject(CurrentProjectService); + private readonly pathHelper = inject(PathHelperService); + private readonly apiV3Service = inject(ApiV3Service); + private readonly halResourceService = inject(HalResourceService); + private readonly toastService = inject(ToastService); + private readonly I18n = inject(I18nService); - } + private v3 = this.pathHelper.api.v3; private create(params:object, filters:ApiV3Filter[]):Observable { const filterJson = JSON.stringify(filters); diff --git a/frontend/src/app/features/boards/board/board-partitioned-page/board-entry.component.ts b/frontend/src/app/features/boards/board/board-partitioned-page/board-entry.component.ts index 1fe536a15ebd..e0ef5fdbc678 100644 --- a/frontend/src/app/features/boards/board/board-partitioned-page/board-entry.component.ts +++ b/frontend/src/app/features/boards/board/board-partitioned-page/board-entry.component.ts @@ -26,14 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - ElementRef, - Injector, - Input, - OnDestroy, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Injector, Input, OnDestroy, inject } from '@angular/core'; import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; import { WorkPackageIsolatedQuerySpaceDirective, @@ -64,12 +57,14 @@ import { QueryUpdatedService } from 'core-app/features/boards/board/query-update standalone: false, }) export class BoardEntryComponent implements OnDestroy { + readonly elementRef = inject(ElementRef); + readonly injector = inject(Injector); + @Input() boardId:string; - constructor( - readonly elementRef:ElementRef, - readonly injector:Injector, - ) { + constructor() { + const injector = this.injector; + populateInputsFromDataset(this); document.body.classList.add('router--boards-full-view'); diff --git a/frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts b/frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts index d033ea67e5e9..2fd95d3ef045 100644 --- a/frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts +++ b/frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts @@ -54,6 +54,24 @@ import { resolveRoutingId } from 'core-app/features/work-packages/helpers/work-p standalone: false, }) export class BoardListContainerComponent extends UntilDestroyedMixin implements OnInit { + readonly I18n = inject(I18nService); + readonly state = inject(StateService); + readonly toastService = inject(ToastService); + readonly halNotification = inject(HalResourceNotificationService); + readonly boardComponent = inject(BoardPartitionedPageComponent); + readonly BoardList = inject(BoardListsService); + readonly boardActionRegistry = inject(BoardActionsRegistryService); + readonly opModalService = inject(OpModalService); + readonly injector = inject(Injector); + readonly apiV3Service = inject(ApiV3Service); + readonly Boards = inject(BoardService); + readonly boardListCrossSelectionService = inject(BoardListCrossSelectionService); + readonly Drag = inject(DragAndDropService); + readonly apiv3Service = inject(ApiV3Service); + readonly QueryUpdated = inject(QueryUpdatedService); + readonly pathHelper = inject(PathHelperService); + readonly currentProject = inject(CurrentProjectService); + @Input() boardId:string; text = { delete: this.I18n.t('js.button_delete'), @@ -96,28 +114,6 @@ export class BoardListContainerComponent extends UntilDestroyedMixin implements private readonly wpStates = inject(States); - constructor( - readonly I18n:I18nService, - readonly state:StateService, - readonly toastService:ToastService, - readonly halNotification:HalResourceNotificationService, - readonly boardComponent:BoardPartitionedPageComponent, - readonly BoardList:BoardListsService, - readonly boardActionRegistry:BoardActionsRegistryService, - readonly opModalService:OpModalService, - readonly injector:Injector, - readonly apiV3Service:ApiV3Service, - readonly Boards:BoardService, - readonly boardListCrossSelectionService:BoardListCrossSelectionService, - readonly Drag:DragAndDropService, - readonly apiv3Service:ApiV3Service, - readonly QueryUpdated:QueryUpdatedService, - readonly pathHelper:PathHelperService, - readonly currentProject:CurrentProjectService, -) { - super(); - } - ngOnInit():void { const id:string = this.boardId || this.state.params.board_id?.toString(); this.board$ = this diff --git a/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts b/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts index 464c05c4a7a7..4d127e62e7e2 100644 --- a/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts +++ b/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts @@ -1,11 +1,4 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - Injector, - Input, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, Input, OnInit, inject } from '@angular/core'; import { DynamicComponentDefinition, ToolbarButtonComponentDefinition, @@ -60,6 +53,20 @@ export function boardCardViewHandlerFactory(injector:Injector) { standalone: false, }) export class BoardPartitionedPageComponent extends UntilDestroyedMixin implements OnInit { + readonly I18n = inject(I18nService); + readonly cdRef = inject(ChangeDetectorRef); + readonly state = inject(StateService); + readonly toastService = inject(ToastService); + readonly halNotification = inject(HalResourceNotificationService); + readonly injector = inject(Injector); + readonly apiV3Service = inject(ApiV3Service); + readonly boardFilters = inject(BoardFiltersService); + readonly Boards = inject(BoardService); + readonly titleService = inject(OpTitleService); + readonly submenuService = inject(SubmenuService); + readonly pathHelperService = inject(PathHelperService); + readonly currentProject = inject(CurrentProjectService); + @Input() boardId:string; text = { button_more: this.I18n.t('js.button_more'), @@ -129,24 +136,6 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin implement }, ]; - constructor( - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - readonly state:StateService, - readonly toastService:ToastService, - readonly halNotification:HalResourceNotificationService, - readonly injector:Injector, - readonly apiV3Service:ApiV3Service, - readonly boardFilters:BoardFiltersService, - readonly Boards:BoardService, - readonly titleService:OpTitleService, - readonly submenuService:SubmenuService, - readonly pathHelperService:PathHelperService, - readonly currentProject:CurrentProjectService, - ) { - super(); - } - ngOnInit():void { // Ensure board is being loaded this.Boards.loadAllBoards(); diff --git a/frontend/src/app/features/boards/board/board.service.ts b/frontend/src/app/features/boards/board/board.service.ts index 143b2dd1a6d1..11eda6477d97 100644 --- a/frontend/src/app/features/boards/board/board.service.ts +++ b/frontend/src/app/features/boards/board/board.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @@ -10,6 +10,12 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; @Injectable({ providedIn: 'root' }) export class BoardService { + protected apiV3Service = inject(ApiV3Service); + protected PathHelper = inject(PathHelperService); + protected CurrentProject = inject(CurrentProjectService); + protected halResourceService = inject(HalResourceService); + protected I18n = inject(I18nService); + public currentBoard$:BehaviorSubject = new BehaviorSubject(null); private loadAllPromise:Promise|undefined; @@ -21,15 +27,6 @@ export class BoardService { unnamed_list: this.I18n.t('js.boards.label_unnamed_list'), }; - constructor( - protected apiV3Service:ApiV3Service, - protected PathHelper:PathHelperService, - protected CurrentProject:CurrentProjectService, - protected halResourceService:HalResourceService, - protected I18n:I18nService, - ) { - } - /** * Return all boards in the current scope of the project * diff --git a/frontend/src/app/features/boards/board/configuration-modal/board-configuration.modal.ts b/frontend/src/app/features/boards/board/configuration-modal/board-configuration.modal.ts index a24aee77456a..503bcbdc2470 100644 --- a/frontend/src/app/features/boards/board/configuration-modal/board-configuration.modal.ts +++ b/frontend/src/app/features/boards/board/configuration-modal/board-configuration.modal.ts @@ -1,19 +1,5 @@ -import { - ApplicationRef, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ComponentFactoryResolver, - ElementRef, - Inject, - Injector, - OnDestroy, - OnInit, - ViewChild, -} from '@angular/core'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { ApplicationRef, ChangeDetectionStrategy, Component, ComponentFactoryResolver, ElementRef, Injector, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { ActiveTabInterface, TabComponent, @@ -34,6 +20,13 @@ import { Board } from 'core-app/features/boards/board/board'; changeDetection: ChangeDetectionStrategy.Default, }) export class BoardConfigurationModalComponent extends OpModalComponent implements OnInit, OnDestroy { + readonly I18n = inject(I18nService); + readonly boardService = inject(BoardService); + readonly boardConfigurationService = inject(BoardConfigurationService); + readonly injector = inject(Injector); + readonly appRef = inject(ApplicationRef); + readonly componentFactoryResolver = inject(ComponentFactoryResolver); + public text = { title: this.I18n.t('js.boards.configuration_modal.title'), closePopup: this.I18n.t('js.close_popup_title'), @@ -48,18 +41,6 @@ export class BoardConfigurationModalComponent extends OpModalComponent implement // And a reference to the actual portal host interface public tabPortalHost:TabPortalOutlet; - constructor(@Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly I18n:I18nService, - readonly boardService:BoardService, - readonly boardConfigurationService:BoardConfigurationService, - readonly injector:Injector, - readonly appRef:ApplicationRef, - readonly componentFactoryResolver:ComponentFactoryResolver, - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef) { - super(locals, cdRef, elementRef); - } - ngOnInit() { this.element = this.elementRef.nativeElement as HTMLElement; diff --git a/frontend/src/app/features/boards/board/configuration-modal/board-configuration.service.ts b/frontend/src/app/features/boards/board/configuration-modal/board-configuration.service.ts index 6cbd5f3ef95b..933b78ce7983 100644 --- a/frontend/src/app/features/boards/board/configuration-modal/board-configuration.service.ts +++ b/frontend/src/app/features/boards/board/configuration-modal/board-configuration.service.ts @@ -1,10 +1,12 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TabInterface } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { BoardHighlightingTabComponent } from 'core-app/features/boards/board/configuration-modal/tabs/highlighting-tab.component'; @Injectable() export class BoardConfigurationService { + readonly I18n = inject(I18nService); + protected _tabs:TabInterface[] = [ { id: 'highlighting', @@ -13,9 +15,6 @@ export class BoardConfigurationService { }, ]; - constructor(readonly I18n:I18nService) { - } - public get tabs() { return this._tabs; } diff --git a/frontend/src/app/features/boards/board/configuration-modal/tabs/highlighting-tab.component.ts b/frontend/src/app/features/boards/board/configuration-modal/tabs/highlighting-tab.component.ts index 489277262d9f..e37f4ef97d20 100644 --- a/frontend/src/app/features/boards/board/configuration-modal/tabs/highlighting-tab.component.ts +++ b/frontend/src/app/features/boards/board/configuration-modal/tabs/highlighting-tab.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Inject, Injector, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Injector, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; @@ -15,6 +15,10 @@ import { CardHighlightingMode } from 'core-app/features/work-packages/components changeDetection: ChangeDetectionStrategy.Default, }) export class BoardHighlightingTabComponent implements TabComponent, OnInit { + readonly injector = inject(Injector); + locals = inject(OpModalLocalsToken); + readonly I18n = inject(I18nService); + // Highlighting mode public highlightingMode:CardHighlightingMode = 'none'; @@ -35,11 +39,6 @@ export class BoardHighlightingTabComponent implements TabComponent, OnInit { }, }; - constructor(readonly injector:Injector, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly I18n:I18nService) { - } - public onSave() { this.updateMode(this.highlightingMode); this.board.highlightingMode = this.highlightingMode; diff --git a/frontend/src/app/features/boards/board/inline-add/board-inline-add-autocompleter.component.ts b/frontend/src/app/features/boards/board/inline-add/board-inline-add-autocompleter.component.ts index 540b21765239..4476279bb3f6 100644 --- a/frontend/src/app/features/boards/board/inline-add/board-inline-add-autocompleter.component.ts +++ b/frontend/src/app/features/boards/board/inline-add/board-inline-add-autocompleter.component.ts @@ -26,17 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - EventEmitter, - Input, - Output, - ViewChild, - ViewEncapsulation, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { Observable, of } from 'rxjs'; import { catchError, map, tap } from 'rxjs/operators'; @@ -66,6 +56,18 @@ import { HalResourceService } from 'core-app/features/hal/services/hal-resource. changeDetection: ChangeDetectionStrategy.Default, }) export class BoardInlineAddAutocompleterComponent implements AfterViewInit { + private readonly querySpace = inject(IsolatedQuerySpace); + private readonly pathHelper = inject(PathHelperService); + private readonly apiV3Service = inject(ApiV3Service); + private readonly urlParamsHelper = inject(UrlParamsHelperService); + private readonly notificationService = inject(WorkPackageNotificationService); + private readonly CurrentProject = inject(CurrentProjectService); + private readonly halResourceService = inject(HalResourceService); + private readonly schemaCacheService = inject(SchemaCacheService); + private readonly cdRef = inject(ChangeDetectorRef); + private readonly I18n = inject(I18nService); + private readonly wpCardDragDrop = inject(WorkPackageCardDragAndDropService); + readonly text = { placeholder: this.I18n.t('js.relations_autocomplete.placeholder'), }; @@ -119,19 +121,6 @@ export class BoardInlineAddAutocompleterComponent implements AfterViewInit { @Output() onReferenced = new EventEmitter(); - constructor(private readonly querySpace:IsolatedQuerySpace, - private readonly pathHelper:PathHelperService, - private readonly apiV3Service:ApiV3Service, - private readonly urlParamsHelper:UrlParamsHelperService, - private readonly notificationService:WorkPackageNotificationService, - private readonly CurrentProject:CurrentProjectService, - private readonly halResourceService:HalResourceService, - private readonly schemaCacheService:SchemaCacheService, - private readonly cdRef:ChangeDetectorRef, - private readonly I18n:I18nService, - private readonly wpCardDragDrop:WorkPackageCardDragAndDropService) { - } - ngAfterViewInit():void { if (!this.ngSelectComponent.ngSelectInstance) { return; diff --git a/frontend/src/app/features/boards/board/query-updated/query-updated.service.ts b/frontend/src/app/features/boards/board/query-updated/query-updated.service.ts index 9fedd7401cb6..fa2770625bd5 100644 --- a/frontend/src/app/features/boards/board/query-updated/query-updated.service.ts +++ b/frontend/src/app/features/boards/board/query-updated/query-updated.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { interval } from 'rxjs'; import { filter, startWith, switchMap } from 'rxjs/operators'; import { ActiveWindowService } from 'core-app/core/active-window/active-window.service'; @@ -8,9 +8,9 @@ const POLLING_INTERVAL = 2000; @Injectable() export class QueryUpdatedService { - constructor(readonly activeWindow:ActiveWindowService, - readonly apiV3Service:ApiV3Service) { - } + readonly activeWindow = inject(ActiveWindowService); + readonly apiV3Service = inject(ApiV3Service); + public monitor(ids:string[]) { let time = new Date(); diff --git a/frontend/src/app/features/boards/board/toolbar-menu/boards-menu-button.component.ts b/frontend/src/app/features/boards/board/toolbar-menu/boards-menu-button.component.ts index 758fd6ad1d7f..714c5c79adbb 100644 --- a/frontend/src/app/features/boards/board/toolbar-menu/boards-menu-button.component.ts +++ b/frontend/src/app/features/boards/board/toolbar-menu/boards-menu-button.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { Board } from 'core-app/features/boards/board/board'; import { Observable } from 'rxjs'; @@ -19,12 +19,11 @@ import { Observable } from 'rxjs'; changeDetection: ChangeDetectionStrategy.Default, }) export class BoardsMenuButtonComponent { + readonly I18n = inject(I18nService); + @Input() board$:Observable; text = { button_more: this.I18n.t('js.button_more'), }; - - constructor(readonly I18n:I18nService) { - } } diff --git a/frontend/src/app/features/boards/board/toolbar-menu/boards-toolbar-menu.directive.ts b/frontend/src/app/features/boards/board/toolbar-menu/boards-toolbar-menu.directive.ts index ce45ca6d7ae6..156f48fb0af6 100644 --- a/frontend/src/app/features/boards/board/toolbar-menu/boards-toolbar-menu.directive.ts +++ b/frontend/src/app/features/boards/board/toolbar-menu/boards-toolbar-menu.directive.ts @@ -26,13 +26,10 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - Directive, ElementRef, Injector, Input, -} from '@angular/core'; +import { Directive, Injector, Input, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { Board } from 'core-app/features/boards/board/board'; import { BoardConfigurationModalComponent } from 'core-app/features/boards/board/configuration-modal/board-configuration.modal'; @@ -46,21 +43,16 @@ import { selectableTitleIdentifier, triggerEditingEvent } from 'core-app/shared/ standalone: false, }) export class BoardsToolbarMenuDirective extends OpContextMenuTrigger { - @Input('boardsToolbarMenu-resource') public board:Board; + readonly opModalService = inject(OpModalService); + readonly boardService = inject(BoardService); + readonly Notifications = inject(ToastService); + readonly State = inject(StateService); + readonly injector = inject(Injector); + readonly I18n = inject(I18nService); + readonly http = inject(HttpClient); - constructor( - readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly opModalService:OpModalService, - readonly boardService:BoardService, - readonly Notifications:ToastService, - readonly State:StateService, - readonly injector:Injector, - readonly I18n:I18nService, - readonly http:HttpClient, - ) { - super(elementRef, opContextMenu); - } + // eslint-disable-next-line @angular-eslint/no-input-rename + @Input('boardsToolbarMenu-resource') public board:Board; public get locals() { return { diff --git a/frontend/src/app/features/calendar/calendar-entry.component.ts b/frontend/src/app/features/calendar/calendar-entry.component.ts index 645a7efd35d7..6d7343e01a81 100644 --- a/frontend/src/app/features/calendar/calendar-entry.component.ts +++ b/frontend/src/app/features/calendar/calendar-entry.component.ts @@ -26,13 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - ElementRef, - Input, - OnDestroy, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, OnDestroy, inject } from '@angular/core'; import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; import { WorkPackageIsolatedQuerySpaceDirective, @@ -45,9 +39,11 @@ import { standalone: false, }) export class CalendarEntryComponent implements OnDestroy { + readonly elementRef = inject(ElementRef); + @Input() queryId:string; - constructor(readonly elementRef:ElementRef) { + constructor() { populateInputsFromDataset(this); document.body.classList.add('router--calendar'); } diff --git a/frontend/src/app/features/calendar/op-calendar.service.spec.ts b/frontend/src/app/features/calendar/op-calendar.service.spec.ts index 23ccec1742c1..cd7cf47143e3 100644 --- a/frontend/src/app/features/calendar/op-calendar.service.spec.ts +++ b/frontend/src/app/features/calendar/op-calendar.service.spec.ts @@ -26,16 +26,25 @@ // See COPYRIGHT and LICENSE files for more details. //++ +import { TestBed } from '@angular/core/testing'; import { OpCalendarService } from 'core-app/features/calendar/op-calendar.service'; +import { WeekdayService } from 'core-app/core/days/weekday.service'; +import { DayResourceService } from 'core-app/core/state/days/day.service'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; describe('OP calendar service', () => { let service:OpCalendarService; beforeEach(() => { - // This is not a valid constructor call, but since we only want to test a helper method that does not - // depend on injected services, we can pass null values here. - // @ts-expect-error ignore invalid constructor call since we don't need a completely valid instance - service = new OpCalendarService(null, null, null); + TestBed.configureTestingModule({ + providers: [ + OpCalendarService, + { provide: WeekdayService, useValue: {} }, + { provide: DayResourceService, useValue: {} }, + { provide: ConfigurationService, useValue: {} }, + ], + }); + service = TestBed.inject(OpCalendarService); }); describe('stripYearFromDateFormat', () => { diff --git a/frontend/src/app/features/calendar/op-calendar.service.ts b/frontend/src/app/features/calendar/op-calendar.service.ts index e59d607e81f9..a8f63538014f 100644 --- a/frontend/src/app/features/calendar/op-calendar.service.ts +++ b/frontend/src/app/features/calendar/op-calendar.service.ts @@ -1,7 +1,4 @@ -import { - ElementRef, - Injectable, -} from '@angular/core'; +import { ElementRef, Injectable, inject } from '@angular/core'; import { Subject } from 'rxjs'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { WeekdayService } from 'core-app/core/days/weekday.service'; @@ -13,18 +10,14 @@ import { DayHeaderContentArg } from '@fullcalendar/core'; @Injectable() export class OpCalendarService extends UntilDestroyedMixin { + readonly weekdayService = inject(WeekdayService); + readonly dayService = inject(DayResourceService); + readonly configurationService = inject(ConfigurationService); + resize$ = new Subject(); resizeObs:ResizeObserver; - constructor( - readonly weekdayService:WeekdayService, - readonly dayService:DayResourceService, - readonly configurationService:ConfigurationService, - ) { - super(); - } - resizeObserver(v:ElementRef|undefined):void { if (!v) { return; diff --git a/frontend/src/app/features/calendar/op-work-packages-calendar.service.ts b/frontend/src/app/features/calendar/op-work-packages-calendar.service.ts index 5184f8c5be1f..641751a4e315 100644 --- a/frontend/src/app/features/calendar/op-work-packages-calendar.service.ts +++ b/frontend/src/app/features/calendar/op-work-packages-calendar.service.ts @@ -75,6 +75,28 @@ interface CalendarOptionsWithDayGrid extends CalendarOptions { @Injectable() export class OpWorkPackagesCalendarService extends UntilDestroyedMixin { + private I18n = inject(I18nService); + private configuration = inject(ConfigurationService); + private sanitizer = inject(DomSanitizer); + readonly injector = inject(Injector); + readonly schemaCache = inject(SchemaCacheService); + readonly toastService = inject(ToastService); + readonly wpTableFilters = inject(WorkPackageViewFiltersService); + readonly wpListService = inject(WorkPackagesListService); + readonly wpListChecksumService = inject(WorkPackagesListChecksumService); + readonly urlParamsHelper = inject(UrlParamsHelperService); + readonly querySpace = inject(IsolatedQuerySpace); + readonly apiV3Service = inject(ApiV3Service); + readonly halResourceService = inject(HalResourceService); + readonly timezoneService = inject(TimezoneService); + readonly pathHelper = inject(PathHelperService); + readonly halEditing = inject(HalResourceEditingService); + readonly wpTableSelection = inject(WorkPackageViewSelectionService); + readonly contextMenuService = inject(OPContextMenuService); + readonly calendarService = inject(OpCalendarService); + readonly weekdayService = inject(WeekdayService); + readonly dayService = inject(DayResourceService); + static MAX_DISPLAYED = 500; tooManyResultsText:string|null; @@ -91,32 +113,6 @@ export class OpWorkPackagesCalendarService extends UntilDestroyedMixin { private readonly states = inject(States); - constructor( - private I18n:I18nService, - private configuration:ConfigurationService, - private sanitizer:DomSanitizer, - readonly injector:Injector, - readonly schemaCache:SchemaCacheService, - readonly toastService:ToastService, - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly wpListService:WorkPackagesListService, - readonly wpListChecksumService:WorkPackagesListChecksumService, - readonly urlParamsHelper:UrlParamsHelperService, - readonly querySpace:IsolatedQuerySpace, - readonly apiV3Service:ApiV3Service, - readonly halResourceService:HalResourceService, - readonly timezoneService:TimezoneService, - readonly pathHelper:PathHelperService, - readonly halEditing:HalResourceEditingService, - readonly wpTableSelection:WorkPackageViewSelectionService, - readonly contextMenuService:OPContextMenuService, - readonly calendarService:OpCalendarService, - readonly weekdayService:WeekdayService, - readonly dayService:DayResourceService, - ) { - super(); - } - calendarOptions(additionalOptions:CalendarOptions):CalendarOptions { return { ...this.defaultOptions(), ...additionalOptions }; } diff --git a/frontend/src/app/features/calendar/te-calendar/te-calendar.component.ts b/frontend/src/app/features/calendar/te-calendar/te-calendar.component.ts index c47d86ceb7eb..3d2f844f2b0c 100644 --- a/frontend/src/app/features/calendar/te-calendar/te-calendar.component.ts +++ b/frontend/src/app/features/calendar/te-calendar/te-calendar.component.ts @@ -1,17 +1,4 @@ -import { - AfterViewInit, - ChangeDetectionStrategy, - Component, - ElementRef, - EventEmitter, - Injector, - Input, - OnDestroy, - Output, - SecurityContext, - ViewChild, - ViewEncapsulation, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Injector, Input, OnDestroy, Output, SecurityContext, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { FullCalendarComponent } from '@fullcalendar/angular'; import { States } from 'core-app/core/states/states.service'; import moment, { Moment } from 'moment'; @@ -115,6 +102,25 @@ const ADD_ENTRY_PROHIBITED_CLASS_NAME = '-prohibited'; standalone: false, }) export class TimeEntryCalendarComponent implements AfterViewInit, OnDestroy { + readonly states = inject(States); + readonly apiV3Service = inject(ApiV3Service); + readonly $state = inject(StateService); + private element = inject(ElementRef); + readonly i18n = inject(I18nService); + readonly injector = inject(Injector); + readonly notifications = inject(HalResourceNotificationService); + private sanitizer = inject(DomSanitizer); + private configuration = inject(ConfigurationService); + private timezone = inject(TimezoneService); + private schemaCache = inject(SchemaCacheService); + private colors = inject(ColorsService); + private browserDetector = inject(BrowserDetector); + private calendar = inject(OpCalendarService); + readonly weekdayService = inject(WeekdayService); + readonly dayService = inject(DayResourceService); + readonly turboRequests = inject(TurboRequestsService); + readonly pathHelper = inject(PathHelperService); + @ViewChild(FullCalendarComponent) ucCalendar:FullCalendarComponent; @Input() projectIdentifier:string; @@ -202,27 +208,6 @@ export class TimeEntryCalendarComponent implements AfterViewInit, OnDestroy { }); } - constructor( - readonly states:States, - readonly apiV3Service:ApiV3Service, - readonly $state:StateService, - private element:ElementRef, - readonly i18n:I18nService, - readonly injector:Injector, - readonly notifications:HalResourceNotificationService, - private sanitizer:DomSanitizer, - private configuration:ConfigurationService, - private timezone:TimezoneService, - private schemaCache:SchemaCacheService, - private colors:ColorsService, - private browserDetector:BrowserDetector, - private calendar:OpCalendarService, - readonly weekdayService:WeekdayService, - readonly dayService:DayResourceService, - readonly turboRequests:TurboRequestsService, - readonly pathHelper:PathHelperService, - ) { } - ngAfterViewInit():void { document.addEventListener('dialog:close', this.closeDialogHandler); } diff --git a/frontend/src/app/features/calendar/wp-calendar/wp-calendar.component.ts b/frontend/src/app/features/calendar/wp-calendar/wp-calendar.component.ts index 7c60b93882b0..5fe76aa445b4 100644 --- a/frontend/src/app/features/calendar/wp-calendar/wp-calendar.component.ts +++ b/frontend/src/app/features/calendar/wp-calendar/wp-calendar.component.ts @@ -26,15 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - ElementRef, - Input, - OnInit, - ViewChild, - ViewEncapsulation, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { CalendarOptions, DateSelectArg, @@ -103,6 +95,29 @@ import { TimezoneService } from 'core-app/core/datetime/timezone.service'; standalone: false, }) export class WorkPackagesCalendarComponent extends UntilDestroyedMixin implements OnInit { + readonly actions$ = inject(ActionsService); + readonly states = inject(States); + readonly wpTableFilters = inject(WorkPackageViewFiltersService); + readonly wpListService = inject(WorkPackagesListService); + readonly querySpace = inject(IsolatedQuerySpace); + readonly schemaCache = inject(SchemaCacheService); + private element = inject(ElementRef); + readonly i18n = inject(I18nService); + readonly toastService = inject(ToastService); + private sanitizer = inject(DomSanitizer); + private I18n = inject(I18nService); + private configuration = inject(ConfigurationService); + readonly calendar = inject(OpCalendarService); + readonly workPackagesCalendar = inject(OpWorkPackagesCalendarService); + readonly currentProject = inject(CurrentProjectService); + readonly halEditing = inject(HalResourceEditingService); + readonly halNotification = inject(HalResourceNotificationService); + readonly weekdayService = inject(WeekdayService); + readonly dayService = inject(DayResourceService); + readonly apiV3Service = inject(ApiV3Service); + readonly pathHelper = inject(PathHelperService); + readonly timezoneService = inject(TimezoneService); + @ViewChild(FullCalendarComponent) ucCalendar:FullCalendarComponent; @ViewChild('ucCalendar', { read: ElementRef }) @@ -123,33 +138,6 @@ export class WorkPackagesCalendarComponent extends UntilDestroyedMixin implement today: this.I18n.t('js.team_planner.today'), }; - constructor( - readonly actions$:ActionsService, - readonly states:States, - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly wpListService:WorkPackagesListService, - readonly querySpace:IsolatedQuerySpace, - readonly schemaCache:SchemaCacheService, - private element:ElementRef, - readonly i18n:I18nService, - readonly toastService:ToastService, - private sanitizer:DomSanitizer, - private I18n:I18nService, - private configuration:ConfigurationService, - readonly calendar:OpCalendarService, - readonly workPackagesCalendar:OpWorkPackagesCalendarService, - readonly currentProject:CurrentProjectService, - readonly halEditing:HalResourceEditingService, - readonly halNotification:HalResourceNotificationService, - readonly weekdayService:WeekdayService, - readonly dayService:DayResourceService, - readonly apiV3Service:ApiV3Service, - readonly pathHelper:PathHelperService, - readonly timezoneService:TimezoneService, - ) { - super(); - } - ngOnInit():void { registerEffectCallbacks(this, this.untilDestroyed()); diff --git a/frontend/src/app/features/enterprise/enterprise-banner-frame.component.ts b/frontend/src/app/features/enterprise/enterprise-banner-frame.component.ts index 62aa7175b5cb..971277fe9fdd 100644 --- a/frontend/src/app/features/enterprise/enterprise-banner-frame.component.ts +++ b/frontend/src/app/features/enterprise/enterprise-banner-frame.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { BannersService } from 'core-app/core/enterprise/banners.service'; @@ -37,6 +37,9 @@ import { BannersService } from 'core-app/core/enterprise/banners.service'; standalone: false, }) export class EnterpriseBannerFrameComponent implements OnInit { + protected pathHelper = inject(PathHelperService); + protected banners = inject(BannersService); + @Input() public feature:string; @Input() public dismissable = false; @@ -45,12 +48,6 @@ export class EnterpriseBannerFrameComponent implements OnInit { frameURL:string; frameID:string; - constructor( - protected pathHelper:PathHelperService, - protected banners:BannersService, - ) { - } - ngOnInit() { this.visible = this.banners.showBannerFor(this.feature); this.frameURL = this.pathHelper.bannerFramePath(this.feature, this.dismissable); diff --git a/frontend/src/app/features/hal/services/hal-aware-error-handler.ts b/frontend/src/app/features/hal/services/hal-aware-error-handler.ts index 46075f34ca1e..3cfe401f6248 100644 --- a/frontend/src/app/features/hal/services/hal-aware-error-handler.ts +++ b/frontend/src/app/features/hal/services/hal-aware-error-handler.ts @@ -1,7 +1,4 @@ -import { - ErrorHandler, - Injectable, -} from '@angular/core'; +import { ErrorHandler, Injectable, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { ErrorResource } from 'core-app/features/hal/resources/error-resource'; @@ -14,14 +11,12 @@ interface RejectedPromise { @Injectable() export class HalAwareErrorHandler extends ErrorHandler { + private readonly I18n = inject(I18nService); + private text = { internal_error: this.I18n.t('js.error.internal'), }; - constructor(private readonly I18n:I18nService) { - super(); - } - public handleError(error:unknown):void { let message:string = this.text.internal_error; diff --git a/frontend/src/app/features/hal/services/hal-resource-notification.service.ts b/frontend/src/app/features/hal/services/hal-resource-notification.service.ts index dbfb1d344e5c..709804c80778 100644 --- a/frontend/src/app/features/hal/services/hal-resource-notification.service.ts +++ b/frontend/src/app/features/hal/services/hal-resource-notification.service.ts @@ -28,7 +28,7 @@ import { StateService } from '@uirouter/core'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -41,6 +41,8 @@ import { HalError } from 'core-app/features/hal/services/hal-error'; @Injectable() export class HalResourceNotificationService { + injector = inject(Injector); + @InjectField() protected I18n:I18nService; @InjectField() protected $state:StateService; @@ -53,9 +55,6 @@ export class HalResourceNotificationService { @InjectField() protected schemaCache:SchemaCacheService; - constructor(public injector:Injector) { - } - public showSave(resource:HalResource, isCreate = false) { const message:any = { message: this.I18n.t(`js.notice_successful_${isCreate ? 'create' : 'update'}`), diff --git a/frontend/src/app/features/hal/services/hal-resource.service.ts b/frontend/src/app/features/hal/services/hal-resource.service.ts index 1d583726870a..2d86b4f0eb1a 100644 --- a/frontend/src/app/features/hal/services/hal-resource.service.ts +++ b/frontend/src/app/features/hal/services/hal-resource.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http'; import { catchError, map } from 'rxjs/operators'; import { Observable, throwError } from 'rxjs'; @@ -59,17 +59,14 @@ interface ErrorWithType { @Injectable({ providedIn: 'root' }) export class HalResourceService { + readonly injector = inject(Injector); + readonly http = inject(HttpClient); + /** * List of all known hal resources, extendable. */ private config:Record = {}; - constructor( - readonly injector:Injector, - readonly http:HttpClient, - ) { - } - /** * Perform a HTTP request and return a HalResource promise. */ diff --git a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts index 658aa0515937..cf917c208a8a 100644 --- a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts +++ b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, HostListener } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, HostListener, inject } from '@angular/core'; import { combineLatest, merge, Observable, timer } from 'rxjs'; import { filter, map, shareReplay, switchMap, throttleTime } from 'rxjs/operators'; import { ActiveWindowService } from 'core-app/core/active-window/active-window.service'; @@ -15,6 +15,12 @@ import { populateInputsFromDataset } from 'core-app/shared/components/dataset-in standalone: false, }) export class InAppNotificationBellComponent implements OnInit { + readonly elementRef = inject(ElementRef); + readonly storeService = inject(IanBellService); + readonly apiV3Service = inject(ApiV3Service); + readonly activeWindow = inject(ActiveWindowService); + readonly pathHelper = inject(PathHelperService); + @Input() interval = 50000; polling$:Observable; @@ -25,13 +31,7 @@ export class InAppNotificationBellComponent implements OnInit { public bellDisplayLimit = 99; - constructor( - readonly elementRef:ElementRef, - readonly storeService:IanBellService, - readonly apiV3Service:ApiV3Service, - readonly activeWindow:ActiveWindowService, - readonly pathHelper:PathHelperService, - ) { + constructor() { populateInputsFromDataset(this); } diff --git a/frontend/src/app/features/in-app-notifications/bell/state/ian-bell.service.ts b/frontend/src/app/features/in-app-notifications/bell/state/ian-bell.service.ts index 8c059b2665f1..3263283204a0 100644 --- a/frontend/src/app/features/in-app-notifications/bell/state/ian-bell.service.ts +++ b/frontend/src/app/features/in-app-notifications/bell/state/ian-bell.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { catchError, map, @@ -57,6 +57,9 @@ import { IanBellStore } from 'core-app/features/in-app-notifications/bell/state/ @Injectable({ providedIn: 'root' }) @EffectHandler export class IanBellService { + readonly actions$ = inject(ActionsService); + readonly resourceService = inject(InAppNotificationsResourceService); + readonly id = 'ian-bell'; readonly store = new IanBellStore(); @@ -65,10 +68,7 @@ export class IanBellService { unread$ = this.query.unread$; - constructor( - readonly actions$:ActionsService, - readonly resourceService:InAppNotificationsResourceService, - ) { + constructor() { this.query.unreadCountChanged$.subscribe((count) => { this.actions$.dispatch(notificationCountChanged({ origin: this.id, count })); }); diff --git a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts index 0760d630ab26..8d882fa961df 100644 --- a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts +++ b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { filter, map } from 'rxjs/operators'; import { StateService } from '@uirouter/angular'; @@ -52,6 +52,17 @@ import { standalone: false, }) export class InAppNotificationCenterComponent implements OnInit { + readonly cdRef = inject(ChangeDetectorRef); + readonly elementRef = inject(ElementRef); + readonly I18n = inject(I18nService); + readonly storeService = inject(IanCenterService); + readonly bellService = inject(IanBellService); + readonly urlParams = inject(UrlParamsService); + readonly state = inject(StateService); + readonly apiV3 = inject(ApiV3Service); + readonly pathService = inject(PathHelperService); + readonly colorsService = inject(ColorsService); + maxSize = NOTIFICATIONS_MAX_SIZE; hasMoreThanPageSize$ = this.storeService.hasMoreThanPageSize$; @@ -141,20 +152,6 @@ export class InAppNotificationCenterComponent implements OnInit { protected readonly idFromLink = idFromLink; - constructor( - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef, - readonly I18n:I18nService, - readonly storeService:IanCenterService, - readonly bellService:IanBellService, - readonly urlParams:UrlParamsService, - readonly state:StateService, - readonly apiV3:ApiV3Service, - readonly pathService:PathHelperService, - readonly colorsService:ColorsService, - ) { - } - ngOnInit():void { const facet = this.urlParams.get('facet') || 'unread'; this.storeService.setFacet(facet as 'unread'|'all'); diff --git a/frontend/src/app/features/in-app-notifications/center/state/ian-center.service.ts b/frontend/src/app/features/in-app-notifications/center/state/ian-center.service.ts index d6361f137c06..31709f6a2fbb 100644 --- a/frontend/src/app/features/in-app-notifications/center/state/ian-center.service.ts +++ b/frontend/src/app/features/in-app-notifications/center/state/ian-center.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { debounceTime, defaultIfEmpty, distinctUntilChanged, map, mapTo, switchMap, take, tap } from 'rxjs/operators'; import { forkJoin, from, Observable, Subject } from 'rxjs'; import { ID, Query } from '@datorama/akita'; @@ -71,6 +71,18 @@ export interface INotificationPageQueryParameters { @Injectable({ providedIn: 'root' }) @EffectHandler export class IanCenterService extends UntilDestroyedMixin { + readonly I18n = inject(I18nService); + readonly injector = inject(Injector); + readonly resourceService = inject(InAppNotificationsResourceService); + readonly actions$ = inject(ActionsService); + readonly apiV3Service = inject(ApiV3Service); + readonly toastService = inject(ToastService); + readonly urlParams = inject(UrlParamsService); + readonly state = inject(StateService); + readonly deviceService = inject(DeviceService); + readonly pathHelper = inject(PathHelperService); + readonly ianBellService = inject(IanBellService); + readonly id = 'ian-center'; readonly store = new IanCenterStore(); @@ -182,19 +194,7 @@ export class IanCenterService extends UntilDestroyedMixin { selectedWorkPackage$ = this.urlParams.pathMatching$(/\/details\/(\d+)/); - constructor( - readonly I18n:I18nService, - readonly injector:Injector, - readonly resourceService:InAppNotificationsResourceService, - readonly actions$:ActionsService, - readonly apiV3Service:ApiV3Service, - readonly toastService:ToastService, - readonly urlParams:UrlParamsService, - readonly state:StateService, - readonly deviceService:DeviceService, - readonly pathHelper:PathHelperService, - readonly ianBellService:IanBellService, - ) { + constructor() { super(); this.reload.subscribe(); diff --git a/frontend/src/app/features/in-app-notifications/entry/actors-line/in-app-notification-actors-line.component.ts b/frontend/src/app/features/in-app-notifications/entry/actors-line/in-app-notification-actors-line.component.ts index 9ceab9a5b419..df7f859d9f10 100644 --- a/frontend/src/app/features/in-app-notifications/entry/actors-line/in-app-notification-actors-line.component.ts +++ b/frontend/src/app/features/in-app-notifications/entry/actors-line/in-app-notification-actors-line.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, HostBinding, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, Input, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { DeviceService } from 'core-app/core/browser/device.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { INotification } from 'core-app/core/state/in-app-notifications/in-app-notification.model'; @@ -13,6 +13,9 @@ import { PrincipalLike } from 'core-app/shared/components/principal/principal-ty standalone: false, }) export class InAppNotificationActorsLineComponent implements OnInit { + readonly deviceService = inject(DeviceService); + private I18n = inject(I18nService); + @HostBinding('class.op-ian-actors') className = true; @Input() aggregatedNotifications:INotification[]; @@ -34,11 +37,6 @@ export class InAppNotificationActorsLineComponent implements OnInit { mark_as_read: this.I18n.t('js.notifications.center.mark_as_read'), }; - constructor( - readonly deviceService:DeviceService, - private I18n:I18nService, - ) { } - ngOnInit():void { // Don't show the actor if the first item is actor-less (date alert) if (this.notification._links.actor) { diff --git a/frontend/src/app/features/in-app-notifications/entry/date-alert/in-app-notification-date-alert.component.ts b/frontend/src/app/features/in-app-notifications/entry/date-alert/in-app-notification-date-alert.component.ts index bf7cf86a5cd0..0ef6d1b36e62 100644 --- a/frontend/src/app/features/in-app-notifications/entry/date-alert/in-app-notification-date-alert.component.ts +++ b/frontend/src/app/features/in-app-notifications/entry/date-alert/in-app-notification-date-alert.component.ts @@ -1,11 +1,4 @@ -import { - ChangeDetectionStrategy, - Component, - HostBinding, - Input, - OnInit, - ViewEncapsulation, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, Input, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { IInAppNotificationDetailsAttribute, INotification } from 'core-app/core/state/in-app-notifications/in-app-notification.model'; @@ -20,6 +13,9 @@ import moment, { Moment } from 'moment'; standalone: false, }) export class InAppNotificationDateAlertComponent implements OnInit { + private I18n = inject(I18nService); + private timezoneService = inject(TimezoneService); + @Input() aggregatedNotifications:INotification[]; @HostBinding('class.op-ian-date-alert') className = true; @@ -49,11 +45,6 @@ export class InAppNotificationDateAlertComponent implements OnInit { note: '', // date alerts do not have notes }; - constructor( - private I18n:I18nService, - private timezoneService:TimezoneService, - ) { } - ngOnInit():void { // Find the most important date alert const interestingAlert = this.deriveMostRelevantAlert(this.aggregatedNotifications); diff --git a/frontend/src/app/features/in-app-notifications/entry/in-app-notification-entry.component.ts b/frontend/src/app/features/in-app-notifications/entry/in-app-notification-entry.component.ts index bf3b91a1cfd8..57aae5371e8c 100644 --- a/frontend/src/app/features/in-app-notifications/entry/in-app-notification-entry.component.ts +++ b/frontend/src/app/features/in-app-notifications/entry/in-app-notification-entry.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, HostBinding, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, Input, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @@ -23,6 +23,14 @@ import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destr standalone: false, }) export class InAppNotificationEntryComponent extends UntilDestroyedMixin implements OnInit { + readonly apiV3Service = inject(ApiV3Service); + readonly I18n = inject(I18nService); + readonly storeService = inject(IanCenterService); + readonly timezoneService = inject(TimezoneService); + readonly pathHelper = inject(PathHelperService); + readonly deviceService = inject(DeviceService); + readonly urlParams = inject(UrlParamsService); + @HostBinding('class.op-ian-item') className = true; @Input() notification:INotification; @@ -55,18 +63,6 @@ export class InAppNotificationEntryComponent extends UntilDestroyedMixin impleme workPackageId:string|null; - constructor( - readonly apiV3Service:ApiV3Service, - readonly I18n:I18nService, - readonly storeService:IanCenterService, - readonly timezoneService:TimezoneService, - readonly pathHelper:PathHelperService, - readonly deviceService:DeviceService, - readonly urlParams:UrlParamsService, - ) { - super(); - } - ngOnInit():void { const href = this.notification._links.resource?.href; this.workPackageId = href && HalResource.matchFromLink(href, 'work_packages'); diff --git a/frontend/src/app/features/in-app-notifications/entry/relative-time/in-app-notification-relative-time.component.ts b/frontend/src/app/features/in-app-notifications/entry/relative-time/in-app-notification-relative-time.component.ts index a2db83bca926..1c387e3113bc 100644 --- a/frontend/src/app/features/in-app-notifications/entry/relative-time/in-app-notification-relative-time.component.ts +++ b/frontend/src/app/features/in-app-notifications/entry/relative-time/in-app-notification-relative-time.component.ts @@ -1,10 +1,4 @@ -import { - ChangeDetectionStrategy, - Component, - Input, - OnInit, - ViewEncapsulation, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { INotification } from 'core-app/core/state/in-app-notifications/in-app-notification.model'; @@ -20,6 +14,9 @@ import { distinctUntilChanged, map } from 'rxjs/operators'; standalone: false, }) export class InAppNotificationRelativeTimeComponent implements OnInit { + private I18n = inject(I18nService); + private timezoneService = inject(TimezoneService); + @Input() notification:INotification; @Input() hasActorByLine = true; @@ -37,11 +34,6 @@ export class InAppNotificationRelativeTimeComponent implements OnInit { ), }; - constructor( - private I18n:I18nService, - private timezoneService:TimezoneService, - ) { } - ngOnInit():void { this.buildTime(); } diff --git a/frontend/src/app/features/in-app-notifications/entry/reminder-alert/in-app-notification-reminder-alert.component.ts b/frontend/src/app/features/in-app-notifications/entry/reminder-alert/in-app-notification-reminder-alert.component.ts index 65e0354fa10d..32bcd973b387 100644 --- a/frontend/src/app/features/in-app-notifications/entry/reminder-alert/in-app-notification-reminder-alert.component.ts +++ b/frontend/src/app/features/in-app-notifications/entry/reminder-alert/in-app-notification-reminder-alert.component.ts @@ -1,11 +1,4 @@ -import { - ChangeDetectionStrategy, - Component, - HostBinding, - Input, - OnInit, - ViewEncapsulation, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, Input, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { IInAppNotificationDetailsResource, INotification } from 'core-app/core/state/in-app-notifications/in-app-notification.model'; @@ -18,6 +11,8 @@ import { IInAppNotificationDetailsResource, INotification } from 'core-app/core/ standalone: false, }) export class InAppNotificationReminderAlertComponent implements OnInit { + private I18n = inject(I18nService); + @Input() aggregatedNotifications:INotification[]; @HostBinding('class.op-ian-reminder-alert') className = true; @@ -27,10 +22,6 @@ export class InAppNotificationReminderAlertComponent implements OnInit { hasDateAlert = false; dateAlerts:INotification[] = []; - constructor( - private I18n:I18nService, - ) { } - ngOnInit():void { this.reminderAlert = this.deriveMostRecentReminder(this.aggregatedNotifications); this.reminderNote = this.extractReminderNoteValue(this.reminderAlert._embedded.details); diff --git a/frontend/src/app/features/job-status/job-status-modal.service.ts b/frontend/src/app/features/job-status/job-status-modal.service.ts index b5a22ac57b38..a3db171ad24d 100644 --- a/frontend/src/app/features/job-status/job-status-modal.service.ts +++ b/frontend/src/app/features/job-status/job-status-modal.service.ts @@ -1,13 +1,12 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { TurboRequestsService } from 'core-app/core/turbo/turbo-requests.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @Injectable({ providedIn: 'root' }) export class JobStatusModalService { - constructor( - protected pathHelper:PathHelperService, - protected turboRequests:TurboRequestsService, - ) {} + protected pathHelper = inject(PathHelperService); + protected turboRequests = inject(TurboRequestsService); + public show(jobId:string):void { void this.turboRequests.requestStream(this.jobModalUrl(jobId)); diff --git a/frontend/src/app/features/plugins/openproject-plugins.module.ts b/frontend/src/app/features/plugins/openproject-plugins.module.ts index 04c3ccbafefd..f5ee7e0ffe41 100644 --- a/frontend/src/app/features/plugins/openproject-plugins.module.ts +++ b/frontend/src/app/features/plugins/openproject-plugins.module.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ Ng1FieldControlsWrapper, -import { Injector, NgModule } from '@angular/core'; +import { Injector, NgModule, inject } from '@angular/core'; import { HookService } from 'core-app/features/plugins/hook-service'; import { OpenProjectPluginContext } from 'core-app/features/plugins/plugin-context'; import { debugLog } from 'core-app/shared/helpers/debug_output'; @@ -37,7 +37,9 @@ import { debugLog } from 'core-app/shared/helpers/debug_output'; ], }) export class OpenprojectPluginsModule { - constructor(injector:Injector) { + constructor() { + const injector = inject(Injector); + debugLog('Registering OpenProject plugin context'); const pluginContext = new OpenProjectPluginContext(injector); window.OpenProject.pluginContext.putValue(pluginContext); diff --git a/frontend/src/app/features/team-planner/team-planner/add-work-packages/add-existing-pane.component.ts b/frontend/src/app/features/team-planner/team-planner/add-work-packages/add-existing-pane.component.ts index 6ef92b88a1bb..9d7fee04bb63 100644 --- a/frontend/src/app/features/team-planner/team-planner/add-work-packages/add-existing-pane.component.ts +++ b/frontend/src/app/features/team-planner/team-planner/add-work-packages/add-existing-pane.component.ts @@ -1,12 +1,4 @@ -import { - ChangeDetectionStrategy, - Component, - ElementRef, - HostBinding, - OnDestroy, - OnInit, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, HostBinding, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { imagePath } from 'core-app/shared/helpers/images/path-helper'; import { @@ -45,6 +37,17 @@ import { OpWorkPackagesCalendarService } from 'core-app/features/calendar/op-wor standalone: false, }) export class AddExistingPaneComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { + private readonly querySpace = inject(IsolatedQuerySpace); + private I18n = inject(I18nService); + private readonly apiV3Service = inject(ApiV3Service); + private readonly notificationService = inject(WorkPackageNotificationService); + private readonly currentProject = inject(CurrentProjectService); + private readonly urlParamsHelper = inject(UrlParamsHelperService); + private readonly workPackagesCalendar = inject(OpWorkPackagesCalendarService); + private readonly calendarDrag = inject(CalendarDragDropService); + private readonly actions$ = inject(ActionsService); + private readonly wpFilters = inject(WorkPackageViewFiltersService); + @HostBinding('class.op-add-existing-pane') className = true; @ViewChild('container') container:ElementRef; @@ -109,21 +112,6 @@ export class AddExistingPaneComponent extends UntilDestroyedMixin implements OnI empty_state: imagePath('team-planner/add-existing-pane--empty-state.gif'), }; - constructor( - private readonly querySpace:IsolatedQuerySpace, - private I18n:I18nService, - private readonly apiV3Service:ApiV3Service, - private readonly notificationService:WorkPackageNotificationService, - private readonly currentProject:CurrentProjectService, - private readonly urlParamsHelper:UrlParamsHelperService, - private readonly workPackagesCalendar:OpWorkPackagesCalendarService, - private readonly calendarDrag:CalendarDragDropService, - private readonly actions$:ActionsService, - private readonly wpFilters:WorkPackageViewFiltersService, - ) { - super(); - } - ngOnInit():void { combineLatest([ this diff --git a/frontend/src/app/features/team-planner/team-planner/assignee/add-assignee.component.ts b/frontend/src/app/features/team-planner/team-planner/assignee/add-assignee.component.ts index 7bab1282a299..28f2c7a4343a 100644 --- a/frontend/src/app/features/team-planner/team-planner/assignee/add-assignee.component.ts +++ b/frontend/src/app/features/team-planner/team-planner/assignee/add-assignee.component.ts @@ -26,15 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - ElementRef, - EventEmitter, - Injector, - Input, - Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Injector, Input, Output, inject } from '@angular/core'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -54,24 +46,22 @@ import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/r standalone: false, }) export class AddAssigneeComponent { + protected elementRef = inject(ElementRef); + protected halResourceService = inject(HalResourceService); + protected I18n = inject(I18nService); + protected halNotification = inject(HalResourceNotificationService); + readonly pathHelper = inject(PathHelperService); + readonly apiV3Service = inject(ApiV3Service); + readonly injector = inject(Injector); + readonly currentProjectService = inject(CurrentProjectService); + readonly wpTableFilters = inject(WorkPackageViewFiltersService); + @Output() public selectAssignee = new EventEmitter(); @Input() alreadySelected:string[] = []; public getOptionsFn = (query:string):Observable => this.autocomplete(query); - constructor( - protected elementRef:ElementRef, - protected halResourceService:HalResourceService, - protected I18n:I18nService, - protected halNotification:HalResourceNotificationService, - readonly pathHelper:PathHelperService, - readonly apiV3Service:ApiV3Service, - readonly injector:Injector, - readonly currentProjectService:CurrentProjectService, - readonly wpTableFilters:WorkPackageViewFiltersService, - ) { } - public autocomplete(term:string|null):Observable { const filters = new ApiV3FilterBuilder(); diff --git a/frontend/src/app/features/team-planner/team-planner/calendar-drag-drop.service.ts b/frontend/src/app/features/team-planner/team-planner/calendar-drag-drop.service.ts index 85586a4d20d8..9a337c5efc5d 100644 --- a/frontend/src/app/features/team-planner/team-planner/calendar-drag-drop.service.ts +++ b/frontend/src/app/features/team-planner/team-planner/calendar-drag-drop.service.ts @@ -1,7 +1,4 @@ -import { - ElementRef, - Injectable, -} from '@angular/core'; +import { ElementRef, Injectable, inject } from '@angular/core'; import { ThirdPartyDraggable } from '@fullcalendar/interaction'; import { DragMetaInput } from '@fullcalendar/common'; import dragula, { Drake } from 'dragula'; @@ -15,6 +12,11 @@ import moment from 'moment-timezone'; @Injectable() export class CalendarDragDropService { + readonly authorisation = inject(AuthorisationService); + readonly schemaCache = inject(SchemaCacheService); + readonly workPackagesCalendarService = inject(OpWorkPackagesCalendarService); + readonly I18n = inject(I18nService); + drake:Drake; draggableWorkPackages$ = new BehaviorSubject([]); @@ -28,14 +30,6 @@ export class CalendarDragDropService { }, }; - constructor( - readonly authorisation:AuthorisationService, - readonly schemaCache:SchemaCacheService, - readonly workPackagesCalendarService:OpWorkPackagesCalendarService, - readonly I18n:I18nService, - ) { - } - destroyDrake():void { if (this.drake) { this.drake.destroy(); diff --git a/frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.ts b/frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.ts index 569a70aac640..ccdad53dcb94 100644 --- a/frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.ts +++ b/frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.ts @@ -26,17 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - ElementRef, - HostListener, - Injector, - OnDestroy, - OnInit, - TemplateRef, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, HostListener, Injector, OnDestroy, OnInit, TemplateRef, ViewChild, inject } from '@angular/core'; import { CalendarOptions, DateSelectArg, @@ -143,6 +133,27 @@ export type TeamPlannerViewOptions = Record):Promise { return this.$state.go(route, params); } diff --git a/frontend/src/app/features/work-packages/components/edit-actions-bar/wp-edit-actions-bar.component.ts b/frontend/src/app/features/work-packages/components/edit-actions-bar/wp-edit-actions-bar.component.ts index 17ab41212f40..65d3e74def5a 100644 --- a/frontend/src/app/features/work-packages/components/edit-actions-bar/wp-edit-actions-bar.component.ts +++ b/frontend/src/app/features/work-packages/components/edit-actions-bar/wp-edit-actions-bar.component.ts @@ -26,9 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Output, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; @@ -39,6 +37,10 @@ import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-f standalone: false, }) export class WorkPackageEditActionsBarComponent { + private I18n = inject(I18nService); + private editForm = inject(EditFormComponent); + private cdRef = inject(ChangeDetectorRef); + @Output() public onSave = new EventEmitter(); @Output() public onCancel = new EventEmitter(); @@ -50,11 +52,6 @@ export class WorkPackageEditActionsBarComponent { cancel: this.I18n.t('js.button_cancel'), }; - constructor(private I18n:I18nService, - private editForm:EditFormComponent, - private cdRef:ChangeDetectorRef) { - } - public set saving(active:boolean) { this._saving = active; this.cdRef.detectChanges(); diff --git a/frontend/src/app/features/work-packages/components/filters/abstract-filter-date-time-value/abstract-filter-date-time-value.controller.ts b/frontend/src/app/features/work-packages/components/filters/abstract-filter-date-time-value/abstract-filter-date-time-value.controller.ts index 6b16d3617502..088992948d91 100644 --- a/frontend/src/app/features/work-packages/components/filters/abstract-filter-date-time-value/abstract-filter-date-time-value.controller.ts +++ b/frontend/src/app/features/work-packages/components/filters/abstract-filter-date-time-value/abstract-filter-date-time-value.controller.ts @@ -28,21 +28,17 @@ import { Moment } from 'moment'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { OnInit, Directive } from '@angular/core'; +import { OnInit, Directive, inject } from '@angular/core'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; @Directive() export abstract class AbstractDateTimeValueController extends UntilDestroyedMixin implements OnInit { - public filter:QueryFilterInstanceResource; + protected I18n = inject(I18nService); + protected timezoneService = inject(TimezoneService); - constructor( - protected I18n:I18nService, - protected timezoneService:TimezoneService, - ) { - super(); - } + public filter:QueryFilterInstanceResource; ngOnInit() { this.filter.values = (this.filter.values as string[]).filter((value) => (value === '' || this.timezoneService.isValidISODateTime(value))); diff --git a/frontend/src/app/features/work-packages/components/filters/filter-boolean-value/filter-boolean-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-boolean-value/filter-boolean-value.component.ts index 4d1319a13f75..0954036bbd32 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-boolean-value/filter-boolean-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-boolean-value/filter-boolean-value.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, EventEmitter, Input, Output, ChangeDetectionStrategy } from '@angular/core'; +import { Component, EventEmitter, Input, Output, ChangeDetectionStrategy, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; @@ -38,15 +38,14 @@ import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/que standalone: false, }) export class FilterBooleanValueComponent { + readonly I18n = inject(I18nService); + @Input() public shouldFocus = false; @Input() public filter:QueryFilterInstanceResource; @Output() public filterChanged = new EventEmitter(); - constructor(readonly I18n:I18nService) { - } - public get value():HalResource | string { // Boolean fields should be initialized as true by default if (this.filter.values.length === 0) { diff --git a/frontend/src/app/features/work-packages/components/filters/filter-container/filter-container.directive.ts b/frontend/src/app/features/work-packages/components/filters/filter-container/filter-container.directive.ts index 412cafc03939..66b621729574 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-container/filter-container.directive.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-container/filter-container.directive.ts @@ -26,15 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - Input, - OnDestroy, - OnInit, - Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, Output, inject } from '@angular/core'; import { WorkPackageViewFiltersService, } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; @@ -55,6 +47,11 @@ import { WorkPackagesListService } from 'core-app/features/work-packages/compone standalone: false, }) export class WorkPackageFilterContainerComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { + readonly wpTableFilters = inject(WorkPackageViewFiltersService); + readonly cdRef = inject(ChangeDetectorRef); + readonly wpFiltersService = inject(WorkPackageFiltersService); + readonly wpListService = inject(WorkPackagesListService); + @Input() showFilterButton = false; @Input() filterButtonText:string = I18n.t('js.button_filter'); @@ -67,12 +64,7 @@ export class WorkPackageFilterContainerComponent extends UntilDestroyedMixin imp public loaded = false; - constructor( - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly cdRef:ChangeDetectorRef, - readonly wpFiltersService:WorkPackageFiltersService, - readonly wpListService:WorkPackagesListService, - ) { + constructor() { super(); this.visible$ = this.wpFiltersService.observeUntil(componentDestroyed(this)); } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component.ts index feec49ccfaef..f249c5a0aad7 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component.ts @@ -26,20 +26,11 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - Input, - HostBinding, - OnInit, - Output, -} from '@angular/core'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { ChangeDetectionStrategy, Component, Input, HostBinding, OnInit, Output } from '@angular/core'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; import { Moment } from 'moment'; import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; -import { TimezoneService } from 'core-app/core/datetime/timezone.service'; import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; import { AbstractDateTimeValueController } from '../abstract-filter-date-time-value/abstract-filter-date-time-value.controller'; @@ -65,11 +56,6 @@ export class FilterDateTimeValueComponent extends AbstractDateTimeValueControlle @Output() public filterChanged = new DebouncedEventEmitter(componentDestroyed(this)); - constructor(readonly I18n:I18nService, - readonly timezoneService:TimezoneService) { - super(I18n, timezoneService); - } - public get value():HalResource|string { return this.filter.values[0]; } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts index dcc3fccaf18b..fb175587fefc 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts @@ -27,18 +27,9 @@ //++ import { Moment } from 'moment'; -import { - ChangeDetectionStrategy, - Component, - HostBinding, - Input, - OnInit, - Output, -} from '@angular/core'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { ChangeDetectionStrategy, Component, HostBinding, Input, OnInit, Output } from '@angular/core'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; -import { TimezoneService } from 'core-app/core/datetime/timezone.service'; import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; import { AbstractDateTimeValueController } from '../abstract-filter-date-time-value/abstract-filter-date-time-value.controller'; import { validDate } from 'core-app/shared/components/datepicker/helpers/date-modal.helpers'; @@ -69,13 +60,6 @@ export class FilterDateTimesValueComponent extends AbstractDateTimeValueControll spacer: this.I18n.t('js.filter.value_spacer'), }; - constructor( - readonly I18n:I18nService, - readonly timezoneService:TimezoneService, - ) { - super(I18n, timezoneService); - } - public get begin():string { return (this.filter.values[0] || '') as string; } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-date-value/filter-date-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-date-value/filter-date-value.component.ts index b3fc174bdf4b..ddc6e34ef134 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-date-value/filter-date-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-date-value/filter-date-value.component.ts @@ -26,13 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - HostBinding, - Input, - Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, Input, Output, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @@ -51,6 +45,9 @@ import moment from 'moment-timezone'; changeDetection: ChangeDetectionStrategy.Default, }) export class FilterDateValueComponent extends UntilDestroyedMixin { + readonly timezoneService = inject(TimezoneService); + readonly I18n = inject(I18nService); + @HostBinding('id') get id() { return `div-values-${this.filter.id}`; } @@ -61,11 +58,6 @@ export class FilterDateValueComponent extends UntilDestroyedMixin { @Output() public filterChanged = new DebouncedEventEmitter(componentDestroyed(this)); - constructor(readonly timezoneService:TimezoneService, - readonly I18n:I18nService) { - super(); - } - public get value():string { return this.filter.values[0] as string; } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component.ts index 2812c33b3027..3286d1e34657 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component.ts @@ -26,13 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - HostBinding, - Input, - Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, Input, Output, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; import moment from 'moment'; @@ -51,6 +45,9 @@ import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/que changeDetection: ChangeDetectionStrategy.Default, }) export class FilterDatesValueComponent extends UntilDestroyedMixin { + readonly timezoneService = inject(TimezoneService); + readonly I18n = inject(I18nService); + @HostBinding('id') get id() { return `div-values-${this.filter.id}`; } @@ -67,13 +64,6 @@ export class FilterDatesValueComponent extends UntilDestroyedMixin { spacer: this.I18n.t('js.filter.value_spacer'), }; - constructor( - readonly timezoneService:TimezoneService, - readonly I18n:I18nService, - ) { - super(); - } - public get value():string[] { return (this.filter.values || []) as string[]; } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component.ts index 2002cde4d301..9a7c9b7828dc 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, Output, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @@ -45,17 +45,15 @@ import { QueryFilterResource } from 'core-app/features/hal/resources/query-filte changeDetection: ChangeDetectionStrategy.Default, }) export class FilterIntegerValueComponent extends UntilDestroyedMixin { + readonly I18n = inject(I18nService); + readonly schemaCache = inject(SchemaCacheService); + @Input() public shouldFocus = false; @Input() public filter:QueryFilterInstanceResource; @Output() public filterChanged = new DebouncedEventEmitter(componentDestroyed(this)); - constructor(readonly I18n:I18nService, - readonly schemaCache:SchemaCacheService) { - super(); - } - public get value() { return parseInt(this.filter.values[0] as string); } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-project/filter-project.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-project/filter-project.component.ts index 19043245cfaf..c0ea251b2cb2 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-project/filter-project.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-project/filter-project.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input, OnInit, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, Output, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; @@ -48,6 +48,10 @@ import { IAPIFilter } from 'core-app/shared/components/autocompleter/op-autocomp standalone: false, }) export class FilterProjectComponent extends UntilDestroyedMixin implements OnInit { + readonly I18n = inject(I18nService); + readonly apiV3Service = inject(ApiV3Service); + readonly currentProjectService = inject(CurrentProjectService); + @Input() public shouldFocus = false; @Input() public filter:QueryFilterInstanceResource; @@ -56,14 +60,6 @@ export class FilterProjectComponent extends UntilDestroyedMixin implements OnIni additionalProjectApiFilters:IAPIFilter[] = []; - constructor( - readonly I18n:I18nService, - readonly apiV3Service:ApiV3Service, - readonly currentProjectService:CurrentProjectService, - ) { - super(); - } - ngOnInit():void { const projectID = this.currentProjectService.id; diff --git a/frontend/src/app/features/work-packages/components/filters/filter-searchable-multiselect-value/filter-searchable-multiselect-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-searchable-multiselect-value/filter-searchable-multiselect-value.component.ts index 2aea22095fe5..de1a795993ab 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-searchable-multiselect-value/filter-searchable-multiselect-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-searchable-multiselect-value/filter-searchable-multiselect-value.component.ts @@ -10,16 +10,7 @@ import { withLatestFrom, } from 'rxjs/operators'; import { take } from 'rxjs/internal/operators/take'; -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - EventEmitter, - Input, - OnInit, - Output, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild, inject } from '@angular/core'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; @@ -44,6 +35,14 @@ import { MAGIC_FILTER_AUTOCOMPLETE_PAGE_SIZE } from 'core-app/core/apiv3/helpers standalone: false, }) export class FilterSearchableMultiselectValueComponent extends UntilDestroyedMixin implements OnInit { + readonly halResourceService = inject(HalResourceService); + readonly apiV3Service = inject(ApiV3Service); + readonly cdRef = inject(ChangeDetectorRef); + readonly I18n = inject(I18nService); + protected currentProject = inject(CurrentProjectService); + protected currentUser = inject(CurrentUserService); + readonly halNotification = inject(HalResourceNotificationService); + @Input() public filter:QueryFilterInstanceResource; @Input() public shouldFocus = false; @@ -85,18 +84,6 @@ export class FilterSearchableMultiselectValueComponent extends UntilDestroyedMix @ViewChild('ngSelectInstance', { static: true }) ngSelectInstance:NgSelectComponent; - constructor( - readonly halResourceService:HalResourceService, - readonly apiV3Service:ApiV3Service, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - protected currentProject:CurrentProjectService, - protected currentUser:CurrentUserService, - readonly halNotification:HalResourceNotificationService, - ) { - super(); - } - ngOnInit():void { if (this.filter.id === 'id') { this.resourceType = 'work_packages'; diff --git a/frontend/src/app/features/work-packages/components/filters/filter-string-value/filter-string-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-string-value/filter-string-value.component.ts index 64b35623ffe8..d8b6622b4963 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-string-value/filter-string-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-string-value/filter-string-value.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, Output, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; @@ -44,6 +44,8 @@ import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/que changeDetection: ChangeDetectionStrategy.Default, }) export class FilterStringValueComponent extends UntilDestroyedMixin { + readonly I18n = inject(I18nService); + @Input() public shouldFocus = false; @Input() public filter:QueryFilterInstanceResource; @@ -54,10 +56,6 @@ export class FilterStringValueComponent extends UntilDestroyedMixin { enter_text: this.I18n.t('js.work_packages.description_enter_text'), }; - constructor(readonly I18n:I18nService) { - super(); - } - public get value():HalResource|string { return this.filter.values[0]; } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-toggled-multiselect-value/filter-toggled-multiselect-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-toggled-multiselect-value/filter-toggled-multiselect-value.component.ts index 0f0ab32b317e..7626c72b92df 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-toggled-multiselect-value/filter-toggled-multiselect-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-toggled-multiselect-value/filter-toggled-multiselect-value.component.ts @@ -27,17 +27,7 @@ //++ import { HalResource } from 'core-app/features/hal/resources/hal-resource'; -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - EventEmitter, - Input, - OnInit, - Output, - ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalResourceSortingService } from 'core-app/features/hal/services/hal-resource-sorting.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -55,6 +45,14 @@ import { compareByHref } from 'core-app/shared/helpers/angular/tracking-function standalone: false, }) export class FilterToggledMultiselectValueComponent implements OnInit, AfterViewInit { + readonly halResourceService = inject(HalResourceService); + readonly halSorting = inject(HalResourceSortingService); + readonly PathHelper = inject(PathHelperService); + readonly apiV3Service = inject(ApiV3Service); + readonly currentUser = inject(CurrentUserService); + readonly cdRef = inject(ChangeDetectorRef); + readonly I18n = inject(I18nService); + @Input() public shouldFocus = false; @Input() public filter:QueryFilterInstanceResource; @@ -73,17 +71,6 @@ export class FilterToggledMultiselectValueComponent implements OnInit, AfterView placeholder: this.I18n.t('js.placeholders.selection'), }; - constructor( - readonly halResourceService:HalResourceService, - readonly halSorting:HalResourceSortingService, - readonly PathHelper:PathHelperService, - readonly apiV3Service:ApiV3Service, - readonly currentUser:CurrentUserService, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - ) { - } - ngOnInit():void { const values = (this.filter.currentSchema!.values!.allowedValues as HalResource[]); this.availableOptions = this.halSorting.sort(values); diff --git a/frontend/src/app/features/work-packages/components/filters/query-filter/query-filter.component.ts b/frontend/src/app/features/work-packages/components/filters/query-filter/query-filter.component.ts index 2a3bba8124a9..aa0455a1a9c8 100644 --- a/frontend/src/app/features/work-packages/components/filters/query-filter/query-filter.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/query-filter/query-filter.component.ts @@ -26,16 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - HostBinding, - Input, - OnInit, - Output, - ViewEncapsulation, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, OnInit, Output, ViewEncapsulation, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { compareByHref, @@ -61,6 +52,12 @@ import { WorkPackageViewBaselineService } from 'core-app/features/work-packages/ changeDetection: ChangeDetectionStrategy.Default, }) export class QueryFilterComponent implements OnInit { + readonly wpTableFilters = inject(WorkPackageViewFiltersService); + readonly wpTableBaseline = inject(WorkPackageViewBaselineService); + readonly schemaCache = inject(SchemaCacheService); + readonly I18n = inject(I18nService); + readonly currentProject = inject(CurrentProjectService); + @HostBinding('class.op-query-filter') className = true; @Input() public shouldFocus = false; @@ -91,15 +88,6 @@ export class QueryFilterComponent implements OnInit { incompatible_filter: this.I18n.t('js.work_packages.filters.baseline_incompatible'), }; - constructor( - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly wpTableBaseline:WorkPackageViewBaselineService, - readonly schemaCache:SchemaCacheService, - readonly I18n:I18nService, - readonly currentProject:CurrentProjectService, - ) { - } - public onFilterUpdated(filter:QueryFilterInstanceResource) { this.filter = filter; this.showValuesInput = this.showValues(); diff --git a/frontend/src/app/features/work-packages/components/filters/query-filters/query-filters.component.ts b/frontend/src/app/features/work-packages/components/filters/query-filters/query-filters.component.ts index 2321aadd3d1d..dfe92d1313d6 100644 --- a/frontend/src/app/features/work-packages/components/filters/query-filters/query-filters.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/query-filters/query-filters.component.ts @@ -26,16 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - Input, - OnChanges, - OnInit, - Output, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnInit, Output, ViewChild, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; import { NgSelectComponent } from '@ng-select/ng-select'; @@ -59,6 +50,13 @@ const ADD_FILTER_SELECT_INDEX = -1; standalone: false, }) export class QueryFiltersComponent extends UntilDestroyedMixin implements OnInit, OnChanges { + readonly wpTableFilters = inject(WorkPackageViewFiltersService); + readonly wpTableBaseline = inject(WorkPackageViewBaselineService); + readonly wpFiltersService = inject(WorkPackageFiltersService); + readonly I18n = inject(I18nService); + readonly alternativeSearchService = inject(AlternativeSearchService); + readonly cdRef = inject(ChangeDetectorRef); + @ViewChild(NgSelectComponent) public ngSelectComponent:NgSelectComponent; @Input() public filters:QueryFilterInstanceResource[]; @@ -88,17 +86,6 @@ export class QueryFiltersComponent extends UntilDestroyedMixin implements OnInit baseline_warning: this.I18n.t('js.work_packages.filters.baseline_warning'), }; - constructor( - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly wpTableBaseline:WorkPackageViewBaselineService, - readonly wpFiltersService:WorkPackageFiltersService, - readonly I18n:I18nService, - readonly alternativeSearchService:AlternativeSearchService, - readonly cdRef:ChangeDetectorRef, - ) { - super(); - } - ngOnInit():void { this.wpTableFilters.live$() .pipe( diff --git a/frontend/src/app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component.ts b/frontend/src/app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component.ts index 6052eecc6469..b1ed134ffe32 100644 --- a/frontend/src/app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Output, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; import { Subject } from 'rxjs'; @@ -48,6 +48,10 @@ import { QueryFilterResource } from 'core-app/features/hal/resources/query-filte changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageFilterByTextInputComponent extends UntilDestroyedMixin { + readonly I18n = inject(I18nService); + readonly querySpace = inject(IsolatedQuerySpace); + readonly wpTableFilters = inject(WorkPackageViewFiltersService); + @Output() public deactivateFilter = new EventEmitter(); public text = { @@ -63,9 +67,7 @@ export class WorkPackageFilterByTextInputComponent extends UntilDestroyedMixin { /** Input for search requests */ public searchTermChanged:Subject = new Subject(); - constructor(readonly I18n:I18nService, - readonly querySpace:IsolatedQuerySpace, - readonly wpTableFilters:WorkPackageViewFiltersService) { + constructor() { super(); this.wpTableFilters diff --git a/frontend/src/app/features/work-packages/components/wp-baseline/baseline-legends/baseline-legends.component.ts b/frontend/src/app/features/work-packages/components/wp-baseline/baseline-legends/baseline-legends.component.ts index ce265bc3dfbb..ddcfd00a104a 100644 --- a/frontend/src/app/features/work-packages/components/wp-baseline/baseline-legends/baseline-legends.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-baseline/baseline-legends/baseline-legends.component.ts @@ -26,14 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - HostBinding, - OnInit, - ViewEncapsulation, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageViewBaselineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-baseline.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; @@ -61,6 +54,14 @@ import { filter } from 'rxjs/operators'; standalone: false, }) export class OpBaselineLegendsComponent extends UntilDestroyedMixin implements OnInit { + readonly I18n = inject(I18nService); + readonly wpTableBaseline = inject(WorkPackageViewBaselineService); + readonly querySpace = inject(IsolatedQuerySpace); + readonly schemaCache = inject(SchemaCacheService); + readonly timezoneService = inject(TimezoneService); + readonly configuration = inject(ConfigurationService); + readonly cdRef = inject(ChangeDetectorRef); + @HostBinding('class.op-baseline-legends') className = true; public numAdded = 0; @@ -86,18 +87,6 @@ export class OpBaselineLegendsComponent extends UntilDestroyedMixin implements O in_your_timezone: this.I18n.t('js.baseline.legends.in_your_timezone'), }; - constructor( - readonly I18n:I18nService, - readonly wpTableBaseline:WorkPackageViewBaselineService, - readonly querySpace:IsolatedQuerySpace, - readonly schemaCache:SchemaCacheService, - readonly timezoneService:TimezoneService, - readonly configuration:ConfigurationService, - readonly cdRef:ChangeDetectorRef, - ) { - super(); - } - ngOnInit() { this .wpTableBaseline diff --git a/frontend/src/app/features/work-packages/components/wp-baseline/baseline-modal/baseline-modal.component.ts b/frontend/src/app/features/work-packages/components/wp-baseline/baseline-modal/baseline-modal.component.ts index 6616f8a55e6c..c04ea13a584f 100644 --- a/frontend/src/app/features/work-packages/components/wp-baseline/baseline-modal/baseline-modal.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-baseline/baseline-modal/baseline-modal.component.ts @@ -26,11 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - HostBinding, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ @@ -40,6 +36,8 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; standalone: false, }) export class OpBaselineModalComponent { + readonly I18n = inject(I18nService); + @HostBinding('class.op-baseline-modal') className = true; public opened = false; @@ -52,10 +50,6 @@ export class OpBaselineModalComponent { }; - constructor( - readonly I18n:I18nService, - ) {} - public toggleOpen():void { this.opened = !this.opened; } diff --git a/frontend/src/app/features/work-packages/components/wp-baseline/baseline/baseline.component.ts b/frontend/src/app/features/work-packages/components/wp-baseline/baseline/baseline.component.ts index cf9130613a6c..faae4c000ef3 100644 --- a/frontend/src/app/features/work-packages/components/wp-baseline/baseline/baseline.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-baseline/baseline/baseline.component.ts @@ -26,15 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - HostBinding, - Input, - OnInit, - Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, OnInit, Output, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @@ -71,6 +63,15 @@ const DEFAULT_SELECTED_TIME = '08:00'; standalone: false, }) export class OpBaselineComponent extends UntilDestroyedMixin implements OnInit { + readonly I18n = inject(I18nService); + readonly wpTableBaseline = inject(WorkPackageViewBaselineService); + readonly halResourceService = inject(HalResourceService); + readonly weekdaysService = inject(WeekdayService); + readonly daysService = inject(DayResourceService); + readonly timezoneService = inject(TimezoneService); + readonly configuration = inject(ConfigurationService); + readonly Banner = inject(BannersService); + @HostBinding('class.op-baseline') className = true; @Output() submitted = new EventEmitter(); @@ -162,19 +163,6 @@ export class OpBaselineComponent extends UntilDestroyedMixin implements OnInit { }, ]; - constructor( - readonly I18n:I18nService, - readonly wpTableBaseline:WorkPackageViewBaselineService, - readonly halResourceService:HalResourceService, - readonly weekdaysService:WeekdayService, - readonly daysService:DayResourceService, - readonly timezoneService:TimezoneService, - readonly configuration:ConfigurationService, - readonly Banner:BannersService, - ) { - super(); - } - public ngOnInit():void { this.userTimezone = this.timezoneService.userTimezone(); this.userOffset = moment().tz(this.userTimezone).format('Z'); diff --git a/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component.ts b/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component.ts index ac29f91b335f..c164626783a6 100644 --- a/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component.ts @@ -26,13 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - Input, - Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -49,6 +43,11 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageBreadcrumbParentComponent { + protected readonly I18n = inject(I18nService); + protected readonly wpRelationsHierarchy = inject(WorkPackageRelationsHierarchyService); + protected readonly notificationService = inject(WorkPackageNotificationService); + protected readonly pathHelper = inject(PathHelperService); + @Input() workPackage:WorkPackageResource; @Output() onSwitch = new EventEmitter(); @@ -64,14 +63,6 @@ export class WorkPackageBreadcrumbParentComponent { private editing:boolean; - public constructor( - protected readonly I18n:I18nService, - protected readonly wpRelationsHierarchy:WorkPackageRelationsHierarchyService, - protected readonly notificationService:WorkPackageNotificationService, - protected readonly pathHelper:PathHelperService, - ) { - } - public canModifyParent():boolean { return !!this.workPackage.changeParent; } diff --git a/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component.ts b/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component.ts index 8f0662c6bb2e..89f007dcd95d 100644 --- a/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -42,6 +42,9 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageBreadcrumbComponent { + private I18n = inject(I18nService); + private pathHelper = inject(PathHelperService); + @Input() workPackage:WorkPackageResource; public text = { @@ -49,11 +52,6 @@ export class WorkPackageBreadcrumbComponent { hierarchy: this.I18n.t('js.relations_hierarchy.hierarchy_headline'), }; - constructor( - private I18n:I18nService, - private pathHelper:PathHelperService, - ) {} - public inputActive = false; public get hierarchyCount() { diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-create-button/wp-create-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-create-button/wp-create-button.component.ts index 2bca877a7fd0..85d6e0eaa1fe 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-create-button/wp-create-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-create-button/wp-create-button.component.ts @@ -27,7 +27,7 @@ //++ import { StateService, TransitionService } from '@uirouter/core'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { Observable } from 'rxjs'; @@ -43,6 +43,14 @@ import { CurrentUserService } from 'core-app/core/current-user/current-user.serv standalone: false, }) export class WorkPackageCreateButtonComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { + readonly $state = inject(StateService); + readonly currentUser = inject(CurrentUserService); + readonly currentProject = inject(CurrentProjectService); + readonly authorisationService = inject(AuthorisationService); + readonly transition = inject(TransitionService); + readonly I18n = inject(I18nService); + readonly cdRef = inject(ChangeDetectorRef); + @Input() stateName$:Observable; @Input() routedFromAngular = true; @@ -64,18 +72,6 @@ export class WorkPackageCreateButtonComponent extends UntilDestroyedMixin implem explanation: this.I18n.t('js.label_create_work_package'), }; - constructor( - readonly $state:StateService, - readonly currentUser:CurrentUserService, - readonly currentProject:CurrentProjectService, - readonly authorisationService:AuthorisationService, - readonly transition:TransitionService, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - ) { - super(); - } - ngOnInit() { this.projectIdentifier = this.currentProject.identifier; diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts index 50462a62384e..94b6a73d3549 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts @@ -28,9 +28,7 @@ import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; import { StateService, TransitionService } from '@uirouter/core'; -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, inject } from '@angular/core'; import { AbstractWorkPackageButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-buttons.module'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { States } from 'core-app/core/states/states.service'; @@ -44,6 +42,14 @@ import { resolveRoutingId } from 'core-app/features/work-packages/helpers/work-p standalone: false, }) export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageButtonComponent implements OnDestroy { + readonly $state = inject(StateService); + readonly I18n:I18nService; + readonly transitions = inject(TransitionService); + readonly cdRef = inject(ChangeDetectorRef); + states = inject(States); + wpTableFocus = inject(WorkPackageViewFocusService); + keepTab = inject(KeepTabService); + public projectIdentifier:string; public accessKey = 8; @@ -64,16 +70,12 @@ export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageBu private transitionListener:Function; - constructor( - readonly $state:StateService, - readonly I18n:I18nService, - readonly transitions:TransitionService, - readonly cdRef:ChangeDetectorRef, - public states:States, - public wpTableFocus:WorkPackageViewFocusService, - public keepTab:KeepTabService, - ) { + constructor() { + const I18n = inject(I18nService); + super(I18n); + this.I18n = I18n; + this.activateLabel = I18n.t('js.button_open_details'); this.deactivateLabel = I18n.t('js.button_close_details'); diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component.ts index 825f91f93e64..a8e2d62ab581 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component.ts @@ -28,9 +28,7 @@ import { AbstractWorkPackageButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-buttons.module'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, inject } from '@angular/core'; import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; import { WorkPackageFiltersService } from 'core-app/features/work-packages/components/filters/wp-filters/wp-filters.service'; @@ -42,6 +40,11 @@ import { WorkPackageFiltersService } from 'core-app/features/work-packages/compo standalone: false, }) export class WorkPackageFilterButtonComponent extends AbstractWorkPackageButtonComponent implements OnInit { + readonly I18n:I18nService; + protected cdRef = inject(ChangeDetectorRef); + protected wpFiltersService = inject(WorkPackageFiltersService); + protected wpTableFilters = inject(WorkPackageViewFiltersService); + public count:number; public initialized = false; @@ -52,13 +55,12 @@ export class WorkPackageFilterButtonComponent extends AbstractWorkPackageButtonC public title = this.I18n.t('js.work_packages.filters.title'); - constructor( - readonly I18n:I18nService, - protected cdRef:ChangeDetectorRef, - protected wpFiltersService:WorkPackageFiltersService, - protected wpTableFilters:WorkPackageViewFiltersService, - ) { + constructor() { + const I18n = inject(I18nService); + super(I18n); + + this.I18n = I18n; } ngOnInit():void { diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.ts index b174a20c9b14..25a77f503685 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-mark-notification-button/work-package-mark-notification-button.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { WpSingleViewService } from 'core-app/features/work-packages/routing/wp-view-base/state/wp-single-view.service'; @@ -10,18 +10,15 @@ import { WpSingleViewService } from 'core-app/features/work-packages/routing/wp- standalone: false, }) export class WorkPackageMarkNotificationButtonComponent { + private I18n = inject(I18nService); + private storeService = inject(WpSingleViewService); + @Input() public workPackage:WorkPackageResource; text = { mark_as_read: this.I18n.t('js.notifications.center.mark_as_read'), }; - constructor( - private I18n:I18nService, - private storeService:WpSingleViewService, - ) { - } - markAllBelongingNotificationsAsRead():void { this.storeService.markAllAsRead(); // emit custom event in order to inform the activity tab stimulus controller diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-reminder-button/wp-reminder-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-reminder-button/wp-reminder-button.component.ts index 4dd6608d0c07..135a95475447 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-reminder-button/wp-reminder-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-reminder-button/wp-reminder-button.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ActionsService } from 'core-app/core/state/actions/actions.service'; @@ -51,23 +51,19 @@ import { filter, map, startWith, switchMap } from 'rxjs/operators'; standalone: false, }) export class WorkPackageReminderButtonComponent extends UntilDestroyedMixin implements OnInit { + readonly I18n = inject(I18nService); + readonly opModalService = inject(OpModalService); + readonly cdRef = inject(ChangeDetectorRef); + readonly apiV3Service = inject(ApiV3Service); + readonly actions$ = inject(ActionsService); + readonly storeService = inject(IanBellService); + @Input() public workPackage:WorkPackageResource; hasReminder$:Observable; public buttonTitle = this.I18n.t('js.work_packages.reminders.button_label'); - constructor( - readonly I18n:I18nService, - readonly opModalService:OpModalService, - readonly cdRef:ChangeDetectorRef, - readonly apiV3Service:ApiV3Service, - readonly actions$:ActionsService, - readonly storeService:IanBellService, - ) { - super(); - } - ngOnInit() { const reminderModalUpdated$ = this .actions$ diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-reminder-button/wp-reminder-context-menu.directive.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-reminder-button/wp-reminder-context-menu.directive.ts index 75b068740642..88097ce5b084 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-reminder-button/wp-reminder-context-menu.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-reminder-button/wp-reminder-context-menu.directive.ts @@ -1,5 +1,4 @@ -import { Directive, ElementRef, Input, OnInit } from '@angular/core'; -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; +import { Directive, Input, OnInit, inject } from '@angular/core'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -14,20 +13,14 @@ import { ReminderPreset, REMINDER_PRESET_OPTIONS } from 'core-app/features/work- standalone: false, }) export class WorkPackageReminderContextMenuDirective extends OpContextMenuTrigger implements OnInit { + readonly I18n = inject(I18nService); + readonly opModalService = inject(OpModalService); + // eslint-disable-next-line @angular-eslint/no-input-rename @Input('wpReminderContextMenu-workPackage') workPackage:WorkPackageResource; protected items:OpContextMenuItem[] = []; - constructor( - readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly I18n:I18nService, - readonly opModalService:OpModalService, - ) { - super(elementRef, opContextMenu); - } - ngOnInit() { this.buildItems(); } diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-settings-button/wp-settings-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-settings-button/wp-settings-button.component.ts index 7ce895282284..671e389d27c9 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-settings-button/wp-settings-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-settings-button/wp-settings-button.component.ts @@ -26,11 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - Input, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ @@ -39,6 +35,8 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; standalone: false, }) export class WorkPackageSettingsButtonComponent { + readonly I18n = inject(I18nService); + @Input() hideTableOptions = false; @Input() showCalendarSharingOption = false; @@ -46,7 +44,4 @@ export class WorkPackageSettingsButtonComponent { public text = { more_actions: this.I18n.t('js.button_more_actions'), }; - - constructor(readonly I18n:I18nService) { - } } diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-share-button/wp-share-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-share-button/wp-share-button.component.ts index 5928d5d01e2c..8e5d3e1796de 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-share-button/wp-share-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-share-button/wp-share-button.component.ts @@ -27,7 +27,7 @@ //++ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; @@ -51,6 +51,13 @@ import { CollectionResource } from 'core-app/features/hal/resources/collection-r standalone: false, }) export class WorkPackageShareButtonComponent extends UntilDestroyedMixin implements OnInit { + readonly I18n = inject(I18nService); + readonly opModalService = inject(OpModalService); + readonly cdRef = inject(ChangeDetectorRef); + readonly bannersService = inject(BannersService); + readonly apiV3Service = inject(ApiV3Service); + readonly actions$ = inject(ActionsService); + @Input() public workPackage:WorkPackageResource; showEnterpriseIcon = !this.bannersService.allowsTo('work_package_sharing'); @@ -61,17 +68,6 @@ export class WorkPackageShareButtonComponent extends UntilDestroyedMixin impleme share: this.I18n.t('js.sharing.share'), }; - constructor( - readonly I18n:I18nService, - readonly opModalService:OpModalService, - readonly cdRef:ChangeDetectorRef, - readonly bannersService:BannersService, - readonly apiV3Service:ApiV3Service, - readonly actions$:ActionsService, - ) { - super(); - } - ngOnInit() { this.shareCount$ = this .actions$ diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts index 88c060906d34..b728d6e6f0c3 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts @@ -28,9 +28,7 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @@ -48,6 +46,11 @@ import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageStatusButtonComponent extends UntilDestroyedMixin implements OnInit { + readonly I18n = inject(I18nService); + readonly cdRef = inject(ChangeDetectorRef); + readonly schemaCache = inject(SchemaCacheService); + readonly halEditing = inject(HalResourceEditingService); + @Input() public workPackage:WorkPackageResource; @Input() public small = false; @@ -58,13 +61,6 @@ export class WorkPackageStatusButtonComponent extends UntilDestroyedMixin implem workPackageStatusBlocked: this.I18n.t('js.work_packages.message_work_package_status_blocked'), }; - constructor(readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - readonly schemaCache:SchemaCacheService, - readonly halEditing:HalResourceEditingService) { - super(); - } - ngOnInit() { this.halEditing .temporaryEditResource(this.workPackage) diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component.ts index 12ffec68e322..1fd0c0c4a444 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component.ts @@ -26,9 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; import { TimelineZoomLevel } from 'core-app/features/hal/resources/query-resource'; @@ -48,6 +46,10 @@ export interface TimelineButtonText extends ButtonControllerText { standalone: false, }) export class WorkPackageTimelineButtonComponent extends AbstractWorkPackageButtonComponent implements OnInit { + readonly I18n:I18nService; + readonly cdRef = inject(ChangeDetectorRef); + wpTableTimeline = inject(WorkPackageViewTimelineService); + public buttonId = 'work-packages-timeline-toggle-button'; public iconClass = 'icon-view-timeline'; @@ -68,10 +70,12 @@ export class WorkPackageTimelineButtonComponent extends AbstractWorkPackageButto public isMinLevel = false; - constructor(readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - public wpTableTimeline:WorkPackageViewTimelineService) { + constructor() { + const I18n = inject(I18nService); + super(I18n); + this.I18n = I18n; + this.activateLabel = I18n.t('js.gantt_chart.button_activate'); this.deactivateLabel = I18n.t('js.gantt_chart.button_deactivate'); diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component.ts index cb143a5e6001..2871ebb58317 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { AbstractWorkPackageButtonComponent } from '../wp-buttons.module'; @@ -39,6 +39,9 @@ import screenfull from 'screenfull'; standalone: false, }) export class ZenModeButtonComponent extends AbstractWorkPackageButtonComponent { + readonly I18n:I18nService; + readonly cdRef = inject(ChangeDetectorRef); + public buttonId = 'work-packages-zen-mode-toggle-button'; public buttonClass = 'toolbar-icon'; @@ -51,13 +54,12 @@ export class ZenModeButtonComponent extends AbstractWorkPackageButtonComponent { private deactivateLabel:string; - constructor( - // eslint-disable-next-line @angular-eslint/prefer-inject - readonly I18n:I18nService, - // eslint-disable-next-line @angular-eslint/prefer-inject - readonly cdRef:ChangeDetectorRef - ) { + constructor() { + const I18n = inject(I18nService); + super(I18n); + this.I18n = I18n; + this.activateLabel = I18n.t('js.zen_mode.button_activate'); this.deactivateLabel = I18n.t('js.zen_mode.button_deactivate'); diff --git a/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service.ts b/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service.ts index b3dcbbe96499..632622edd27c 100644 --- a/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Injector, Optional } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { WorkPackageViewOrderService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service'; import { States } from 'core-app/core/states/states.service'; @@ -16,6 +16,16 @@ import { firstValueFrom } from 'rxjs'; @Injectable() export class WorkPackageCardDragAndDropService { + readonly states = inject(States); + readonly injector = inject(Injector); + readonly reorderService = inject(WorkPackageViewOrderService); + readonly wpCreate = inject(WorkPackageCreateService); + readonly notificationService = inject(WorkPackageNotificationService); + readonly apiV3Service = inject(ApiV3Service); + readonly currentProject = inject(CurrentProjectService); + readonly dragService = inject(DragAndDropService, { optional: true }); + readonly wpInlineCreate = inject(WorkPackageInlineCreateService); + private _workPackages:WorkPackageResource[]; /** Whether the card view has an active inline created wp */ @@ -24,18 +34,6 @@ export class WorkPackageCardDragAndDropService { /** A reference to the component in use, to have access to the current input variables */ public cardView:WorkPackageCardViewComponent; - public constructor(readonly states:States, - readonly injector:Injector, - readonly reorderService:WorkPackageViewOrderService, - readonly wpCreate:WorkPackageCreateService, - readonly notificationService:WorkPackageNotificationService, - readonly apiV3Service:ApiV3Service, - readonly currentProject:CurrentProjectService, - @Optional() readonly dragService:DragAndDropService, - readonly wpInlineCreate:WorkPackageInlineCreateService) { - - } - public init(componentRef:WorkPackageCardViewComponent) { this.cardView = componentRef; } diff --git a/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-view.service.ts b/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-view.service.ts index bbbb69483b31..47f80f78eb89 100644 --- a/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-view.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-view.service.ts @@ -1,11 +1,11 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; @Injectable() export class WorkPackageCardViewService { - public constructor(readonly querySpace:IsolatedQuerySpace) { - } + readonly querySpace = inject(IsolatedQuerySpace); + public classIdentifier(wp:WorkPackageResource) { // The same class names are used for the proximity to the table representation. diff --git a/frontend/src/app/features/work-packages/components/wp-card-view/wp-card-view.component.ts b/frontend/src/app/features/work-packages/components/wp-card-view/wp-card-view.component.ts index 9cf71531f98a..8e0031b2d531 100644 --- a/frontend/src/app/features/work-packages/components/wp-card-view/wp-card-view.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-card-view/wp-card-view.component.ts @@ -1,16 +1,4 @@ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - Injector, - Input, - OnInit, - Output, - ViewChild, OnDestroy, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Injector, Input, OnInit, Output, ViewChild, OnDestroy, inject } from '@angular/core'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; @@ -55,6 +43,25 @@ export type CardViewOrientation = 'horizontal'|'vertical'; standalone: false, }) export class WorkPackageCardViewComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit, WorkPackageViewOutputs, OnDestroy { + readonly querySpace = inject(IsolatedQuerySpace); + readonly states = inject(States); + readonly injector = inject(Injector); + readonly $state = inject(StateService); + readonly I18n = inject(I18nService); + readonly wpCreate = inject(WorkPackageCreateService); + readonly wpInlineCreate = inject(WorkPackageInlineCreateService); + readonly notificationService = inject(WorkPackageNotificationService); + readonly halEvents = inject(HalEventsService); + readonly authorisationService = inject(AuthorisationService); + readonly causedUpdates = inject(CausedUpdatesService); + readonly cdRef = inject(ChangeDetectorRef); + readonly pathHelper = inject(PathHelperService); + readonly wpTableSelection = inject(WorkPackageViewSelectionService); + readonly wpViewOrder = inject(WorkPackageViewOrderService); + readonly cardView = inject(WorkPackageCardViewService); + readonly cardDragDrop = inject(WorkPackageCardDragAndDropService); + readonly deviceService = inject(DeviceService); + @Input('dragOutOfHandler') public canDragOutOf:(wp:WorkPackageResource) => boolean; @Input() public dragInto:boolean; @@ -119,27 +126,6 @@ export class WorkPackageCardViewComponent extends UntilDestroyedMixin implements isNewResource = isNewResource; - constructor(readonly querySpace:IsolatedQuerySpace, - readonly states:States, - readonly injector:Injector, - readonly $state:StateService, - readonly I18n:I18nService, - readonly wpCreate:WorkPackageCreateService, - readonly wpInlineCreate:WorkPackageInlineCreateService, - readonly notificationService:WorkPackageNotificationService, - readonly halEvents:HalEventsService, - readonly authorisationService:AuthorisationService, - readonly causedUpdates:CausedUpdatesService, - readonly cdRef:ChangeDetectorRef, - readonly pathHelper:PathHelperService, - readonly wpTableSelection:WorkPackageViewSelectionService, - readonly wpViewOrder:WorkPackageViewOrderService, - readonly cardView:WorkPackageCardViewService, - readonly cardDragDrop:WorkPackageCardDragAndDropService, - readonly deviceService:DeviceService) { - super(); - } - ngOnInit() { this.registerCreationCallback(); diff --git a/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions.component.ts b/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions.component.ts index 8f94d5129b2a..a99eca561b0d 100644 --- a/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions.component.ts @@ -26,9 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { CustomActionResource } from 'core-app/features/hal/resources/custom-action-resource'; @@ -42,20 +40,16 @@ import { BannersService } from 'core-app/core/enterprise/banners.service'; standalone: false, }) export class WpCustomActionsComponent extends UntilDestroyedMixin implements OnInit { + readonly apiV3Service = inject(ApiV3Service); + readonly cdRef = inject(ChangeDetectorRef); + readonly bannersService = inject(BannersService); + @Input() workPackage:WorkPackageResource; actions:CustomActionResource[] = []; available = this.bannersService.allowsTo('custom_actions'); - constructor( - readonly apiV3Service:ApiV3Service, - readonly cdRef:ChangeDetectorRef, - readonly bannersService:BannersService, - ) { - super(); - } - ngOnInit() { this .apiV3Service diff --git a/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions/wp-custom-action.component.ts b/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions/wp-custom-action.component.ts index ea9cead6aeed..604bf4a4e3aa 100644 --- a/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions/wp-custom-action.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions/wp-custom-action.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, Input, OnInit, inject } from '@angular/core'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { CustomActionResource } from 'core-app/features/hal/resources/custom-action-resource'; import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; @@ -51,22 +51,18 @@ import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destr standalone: false, }) export class WpCustomActionComponent extends UntilDestroyedMixin implements OnInit { + private halResourceService = inject(HalResourceService); + private apiV3Service = inject(ApiV3Service); + private wpActivity = inject(WorkPackagesActivityService); + private notificationService = inject(WorkPackageNotificationService); + private halEditing = inject(HalResourceEditingService); + private halEvents = inject(HalEventsService); + private cdRef = inject(ChangeDetectorRef); + @Input() workPackage:WorkPackageResource; @Input() action:CustomActionResource; - constructor( - private halResourceService:HalResourceService, - private apiV3Service:ApiV3Service, - private wpActivity:WorkPackagesActivityService, - private notificationService:WorkPackageNotificationService, - private halEditing:HalResourceEditingService, - private halEvents:HalEventsService, - private cdRef:ChangeDetectorRef, - ) { - super(); - } - ngOnInit() { this .halEvents diff --git a/frontend/src/app/features/work-packages/components/wp-details/wp-details-toolbar.component.ts b/frontend/src/app/features/work-packages/components/wp-details/wp-details-toolbar.component.ts index 3d3ad0323f74..383b4154d160 100644 --- a/frontend/src/app/features/work-packages/components/wp-details/wp-details-toolbar.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-details/wp-details-toolbar.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -44,6 +44,11 @@ import { Observable, of } from 'rxjs'; standalone: false, }) export class WorkPackageSplitViewToolbarComponent implements OnInit { + readonly I18n = inject(I18nService); + readonly halEditing = inject(HalResourceEditingService); + readonly configurationService = inject(ConfigurationService); + readonly currentUserService = inject(CurrentUserService); + @Input() workPackage:WorkPackageResource; @Input() displayNotificationsButton:boolean; @@ -55,14 +60,6 @@ export class WorkPackageSplitViewToolbarComponent implements OnInit { button_more: this.I18n.t('js.button_more'), }; - constructor( - readonly I18n:I18nService, - readonly halEditing:HalResourceEditingService, - readonly configurationService:ConfigurationService, - readonly currentUserService:CurrentUserService, - ) { - } - ngOnInit() { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access this.displayShareButton$ = this.currentUserService.hasCapabilities$('work_package_shares/index', this.workPackage.project.id); diff --git a/frontend/src/app/features/work-packages/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts b/frontend/src/app/features/work-packages/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts index 70919c585f04..6b7a7cba7f10 100644 --- a/frontend/src/app/features/work-packages/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts @@ -26,9 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, inject } from '@angular/core'; import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; @Component({ @@ -41,14 +39,13 @@ import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-f changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageReplacementLabelComponent implements OnInit { + protected wpeditForm = inject(EditFormComponent); + protected elementRef = inject(ElementRef); + @Input() public fieldName:string; private element:HTMLElement; - constructor(protected wpeditForm:EditFormComponent, - protected elementRef:ElementRef) { - } - ngOnInit() { this.element = this.elementRef.nativeElement; } diff --git a/frontend/src/app/features/work-packages/components/wp-form-group/wp-attribute-group.component.ts b/frontend/src/app/features/work-packages/components/wp-form-group/wp-attribute-group.component.ts index 4a8ebf882017..1327b19968d7 100644 --- a/frontend/src/app/features/work-packages/components/wp-form-group/wp-attribute-group.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-form-group/wp-attribute-group.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, HostBinding, Injector, Input, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, Injector, Input, ViewEncapsulation, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; @@ -48,6 +48,10 @@ import { changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageFormAttributeGroupComponent extends UntilDestroyedMixin { + readonly I18n = inject(I18nService); + wpEditForm = inject(EditFormComponent); + protected injector = inject(Injector); + @HostBinding('class.wp-attribute-group') className = true; @HostBinding('class.attributes-group--attributes') parentClassName = true; @@ -55,14 +59,6 @@ export class WorkPackageFormAttributeGroupComponent extends UntilDestroyedMixin @Input() public group:GroupDescriptor; - constructor( - readonly I18n:I18nService, - public wpEditForm:EditFormComponent, - protected injector:Injector, - ) { - super(); - } - /** * Hide read-only fields, but only when in the create mode * @param {FieldDescriptor} field diff --git a/frontend/src/app/features/work-packages/components/wp-grid/wp-grid.component.ts b/frontend/src/app/features/work-packages/components/wp-grid/wp-grid.component.ts index 635b6060a39f..8b872922e0fe 100644 --- a/frontend/src/app/features/work-packages/components/wp-grid/wp-grid.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-grid/wp-grid.component.ts @@ -26,9 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, OnInit, inject } from '@angular/core'; import { WorkPackageViewHighlightingService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service'; import { CardViewOrientation } from 'core-app/features/work-packages/components/wp-card-view/wp-card-view.component'; import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; @@ -74,6 +72,12 @@ import { WorkPackageViewOutputs } from 'core-app/features/work-packages/routing/ standalone: false, }) export class WorkPackagesGridComponent implements WorkPackageViewOutputs, OnInit { + readonly wpTableHighlight = inject(WorkPackageViewHighlightingService); + readonly wpTableSortBy = inject(WorkPackageViewSortByService); + readonly wpList = inject(WorkPackagesListService); + readonly querySpace = inject(IsolatedQuerySpace); + readonly cdRef = inject(ChangeDetectorRef); + @Input() public configuration:WorkPackageTableConfiguration; @Input() public showResizer = false; @@ -96,13 +100,6 @@ export class WorkPackagesGridComponent implements WorkPackageViewOutputs, OnInit public highlightingMode:HighlightingMode = 'none'; - constructor(readonly wpTableHighlight:WorkPackageViewHighlightingService, - readonly wpTableSortBy:WorkPackageViewSortByService, - readonly wpList:WorkPackagesListService, - readonly querySpace:IsolatedQuerySpace, - readonly cdRef:ChangeDetectorRef) { - } - ngOnInit() { this.dragInto = this.configuration.dragAndDropEnabled; this.canDragOutOf = () => this.configuration.dragAndDropEnabled; diff --git a/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.component.ts b/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.component.ts index 97473e2d33a4..daecf685f383 100644 --- a/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.component.ts @@ -26,19 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - HostListener, - Injector, - Input, - OnInit, - Output, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Injector, Input, OnInit, Output, inject } from '@angular/core'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { WorkPackageViewFocusService, @@ -81,6 +69,19 @@ import { delegate, DelegateEvent } from '@knowledgecode/delegate'; changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageInlineCreateComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit { + readonly injector = inject(Injector); + protected readonly elementRef = inject(ElementRef); + protected readonly schemaCache = inject(SchemaCacheService); + protected readonly I18n = inject(I18nService); + protected readonly querySpace = inject(IsolatedQuerySpace); + protected readonly cdRef = inject(ChangeDetectorRef); + protected readonly wpCreate = inject(WorkPackageCreateService); + protected readonly wpInlineCreate = inject(WorkPackageInlineCreateService); + protected readonly wpTableColumns = inject(WorkPackageViewColumnsService); + protected readonly wpTableFocus = inject(WorkPackageViewFocusService); + protected readonly halEditing = inject(HalResourceEditingService); + protected readonly authorisationService = inject(AuthorisationService); + @Input() colspan:number; @Input() table:WorkPackageTable; @@ -113,21 +114,6 @@ export class WorkPackageInlineCreateComponent extends UntilDestroyedMixin implem return this.mode !== 'inactive'; } - constructor(public readonly injector:Injector, - protected readonly elementRef:ElementRef, - protected readonly schemaCache:SchemaCacheService, - protected readonly I18n:I18nService, - protected readonly querySpace:IsolatedQuerySpace, - protected readonly cdRef:ChangeDetectorRef, - protected readonly wpCreate:WorkPackageCreateService, - protected readonly wpInlineCreate:WorkPackageInlineCreateService, - protected readonly wpTableColumns:WorkPackageViewColumnsService, - protected readonly wpTableFocus:WorkPackageViewFocusService, - protected readonly halEditing:HalResourceEditingService, - protected readonly authorisationService:AuthorisationService) { - super(); - } - ngOnInit() { this.element = this.elementRef.nativeElement; } diff --git a/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.service.ts b/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.service.ts index 64996713ebeb..ab4efddfc21b 100644 --- a/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.service.ts @@ -26,11 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - Injectable, - Injector, - OnDestroy, -} from '@angular/core'; +import { Injectable, Injector, OnDestroy, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { Observable, @@ -45,15 +41,14 @@ import { CurrentProjectService } from 'core-app/core/current-project/current-pro @Injectable() export class WorkPackageInlineCreateService implements OnDestroy { + readonly injector = inject(Injector); + @InjectField() I18n!:I18nService; @InjectField() protected readonly currentUser:CurrentUserService; @InjectField() protected readonly currentProject:CurrentProjectService; - constructor(readonly injector:Injector) { - } - /** * A separate reference pane for the inline create component */ diff --git a/frontend/src/app/features/work-packages/components/wp-list/wp-list-checksum.service.ts b/frontend/src/app/features/work-packages/components/wp-list/wp-list-checksum.service.ts index 8b214522f5f2..72d2446e1e9a 100644 --- a/frontend/src/app/features/work-packages/components/wp-list/wp-list-checksum.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-list/wp-list-checksum.service.ts @@ -28,16 +28,16 @@ import { StateService, TransitionPromise } from '@uirouter/core'; import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { WorkPackageViewPagination } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-pagination'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { Subject } from 'rxjs'; @Injectable() export class WorkPackagesListChecksumService { - constructor(protected UrlParamsHelper:UrlParamsHelperService, - protected $state:StateService) { - } + protected UrlParamsHelper = inject(UrlParamsHelperService); + protected $state = inject(StateService); + public id:string|null; diff --git a/frontend/src/app/features/work-packages/components/wp-list/wp-list-invalid-query.service.ts b/frontend/src/app/features/work-packages/components/wp-list/wp-list-invalid-query.service.ts index 64d5d8bd9651..e576e7ca2311 100644 --- a/frontend/src/app/features/work-packages/components/wp-list/wp-list-invalid-query.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-list/wp-list-invalid-query.service.ts @@ -27,7 +27,7 @@ //++ import { QueryResource } from 'core-app/features/hal/resources/query-resource'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { QueryFilterInstanceSchemaResource } from 'core-app/features/hal/resources/query-filter-instance-schema-resource'; import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; @@ -39,8 +39,8 @@ import { QueryColumn } from '../wp-query/query-column'; @Injectable() export class WorkPackagesListInvalidQueryService { - constructor(protected halResourceService:HalResourceService) { - } + protected halResourceService = inject(HalResourceService); + public restoreQuery(query:QueryResource, form:QueryFormResource) { this.restoreFilters(query, form.payload, form.schema); diff --git a/frontend/src/app/features/work-packages/components/wp-list/wp-list.service.ts b/frontend/src/app/features/work-packages/components/wp-list/wp-list.service.ts index 6b47e92c2b65..ffa019a4d83b 100644 --- a/frontend/src/app/features/work-packages/components/wp-list/wp-list.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-list/wp-list.service.ts @@ -31,7 +31,7 @@ import { States } from 'core-app/core/states/states.service'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { StateService } from '@uirouter/core'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import isPersistedResource from 'core-app/features/hal/helpers/is-persisted-resource'; import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; @@ -63,6 +63,23 @@ export interface QueryDefinition { @Injectable() export class WorkPackagesListService { + readonly injector = inject(Injector); + protected toastService = inject(ToastService); + readonly I18n = inject(I18nService); + protected UrlParamsHelper = inject(UrlParamsHelperService); + protected authorisationService = inject(AuthorisationService); + protected $state = inject(StateService); + protected apiV3Service = inject(ApiV3Service); + protected states = inject(States); + protected querySpace = inject(IsolatedQuerySpace); + protected pagination = inject(PaginationService); + protected configuration = inject(ConfigurationService); + protected wpTablePagination = inject(WorkPackageViewPaginationService); + protected wpStatesInitialization = inject(WorkPackageStatesInitializationService); + protected wpListInvalidQueryService = inject(WorkPackagesListInvalidQueryService); + protected wpQueryView = inject(WorkPackagesQueryViewService); + protected submenuService = inject(SubmenuService); + @InjectField() protected readonly currentUser:CurrentUserService; // We remember the query requests coming in so we can ensure only the latest request is being tended to @@ -88,25 +105,6 @@ export class WorkPackagesListService { share(), ); - constructor( - readonly injector:Injector, - protected toastService:ToastService, - readonly I18n:I18nService, - protected UrlParamsHelper:UrlParamsHelperService, - protected authorisationService:AuthorisationService, - protected $state:StateService, - protected apiV3Service:ApiV3Service, - protected states:States, - protected querySpace:IsolatedQuerySpace, - protected pagination:PaginationService, - protected configuration:ConfigurationService, - protected wpTablePagination:WorkPackageViewPaginationService, - protected wpStatesInitialization:WorkPackageStatesInitializationService, - protected wpListInvalidQueryService:WorkPackagesListInvalidQueryService, - protected wpQueryView:WorkPackagesQueryViewService, - protected submenuService:SubmenuService, - ) { } - /** * Stream a query request as a HTTP observable. Each request to this method will * result in a new HTTP request. diff --git a/frontend/src/app/features/work-packages/components/wp-list/wp-query-view.service.ts b/frontend/src/app/features/work-packages/components/wp-list/wp-query-view.service.ts index e9b7d05c6e01..b9aaba7c4767 100644 --- a/frontend/src/app/features/work-packages/components/wp-list/wp-query-view.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-list/wp-query-view.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { StateService } from '@uirouter/core'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { Observable } from 'rxjs'; @@ -8,10 +8,9 @@ import { ConfigurationService } from 'core-app/core/config/configuration.service @Injectable() export class WorkPackagesQueryViewService { - constructor( - protected $state:StateService, - protected apiV3Service:ApiV3Service, - ) { } + protected $state = inject(StateService); + protected apiV3Service = inject(ApiV3Service); + create(query:QueryResource):Observable { if (!query.href) { diff --git a/frontend/src/app/features/work-packages/components/wp-list/wp-states-initialization.service.ts b/frontend/src/app/features/work-packages/components/wp-list/wp-states-initialization.service.ts index ddca30dd19d2..e38645c98846 100644 --- a/frontend/src/app/features/work-packages/components/wp-list/wp-states-initialization.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-list/wp-states-initialization.service.ts @@ -2,7 +2,7 @@ import { States } from 'core-app/core/states/states.service'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { WorkPackageViewHighlightingService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service'; import { take } from 'rxjs/operators'; import { WorkPackageViewOrderService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service'; @@ -29,29 +29,28 @@ import { WorkPackageViewBaselineService } from 'core-app/features/work-packages/ @Injectable() export class WorkPackageStatesInitializationService { - constructor( - protected states:States, - protected querySpace:IsolatedQuerySpace, - protected wpTableColumns:WorkPackageViewColumnsService, - protected wpTableGroupBy:WorkPackageViewGroupByService, - protected wpTableGroupFold:WorkPackageViewCollapsedGroupsService, - protected wpTableSortBy:WorkPackageViewSortByService, - protected wpTableFilters:WorkPackageViewFiltersService, - protected wpTableSum:WorkPackageViewSumService, - protected wpTableTimeline:WorkPackageViewTimelineService, - protected wpTableHierarchies:WorkPackageViewHierarchiesService, - protected wpTableHighlighting:WorkPackageViewHighlightingService, - protected wpTableRelationColumns:WorkPackageViewRelationColumnsService, - protected wpTablePagination:WorkPackageViewPaginationService, - protected wpTableOrder:WorkPackageViewOrderService, - protected wpTableAdditionalElements:WorkPackageViewAdditionalElementsService, - protected apiV3Service:ApiV3Service, - protected wpListChecksumService:WorkPackagesListChecksumService, - protected authorisationService:AuthorisationService, - protected wpDisplayRepresentation:WorkPackageViewDisplayRepresentationService, - protected wpIncludeSubprojects:WorkPackageViewIncludeSubprojectsService, - protected wpTimestamps:WorkPackageViewBaselineService, - ) { } + protected states = inject(States); + protected querySpace = inject(IsolatedQuerySpace); + protected wpTableColumns = inject(WorkPackageViewColumnsService); + protected wpTableGroupBy = inject(WorkPackageViewGroupByService); + protected wpTableGroupFold = inject(WorkPackageViewCollapsedGroupsService); + protected wpTableSortBy = inject(WorkPackageViewSortByService); + protected wpTableFilters = inject(WorkPackageViewFiltersService); + protected wpTableSum = inject(WorkPackageViewSumService); + protected wpTableTimeline = inject(WorkPackageViewTimelineService); + protected wpTableHierarchies = inject(WorkPackageViewHierarchiesService); + protected wpTableHighlighting = inject(WorkPackageViewHighlightingService); + protected wpTableRelationColumns = inject(WorkPackageViewRelationColumnsService); + protected wpTablePagination = inject(WorkPackageViewPaginationService); + protected wpTableOrder = inject(WorkPackageViewOrderService); + protected wpTableAdditionalElements = inject(WorkPackageViewAdditionalElementsService); + protected apiV3Service = inject(ApiV3Service); + protected wpListChecksumService = inject(WorkPackagesListChecksumService); + protected authorisationService = inject(AuthorisationService); + protected wpDisplayRepresentation = inject(WorkPackageViewDisplayRepresentationService); + protected wpIncludeSubprojects = inject(WorkPackageViewIncludeSubprojectsService); + protected wpTimestamps = inject(WorkPackageViewBaselineService); + /** * Initialize the query and table states from the given query and results. diff --git a/frontend/src/app/features/work-packages/components/wp-new/wp-create.component.ts b/frontend/src/app/features/work-packages/components/wp-new/wp-create.component.ts index 340847154b2a..2a7dc88f0b40 100644 --- a/frontend/src/app/features/work-packages/components/wp-new/wp-create.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-new/wp-create.component.ts @@ -26,14 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectorRef, - Directive, - Injector, - Input, - OnInit, - ViewChild, OnDestroy, -} from '@angular/core'; +import { ChangeDetectorRef, Directive, Injector, Input, OnInit, ViewChild, OnDestroy, inject } from '@angular/core'; import { StateService, Transition, @@ -63,6 +56,20 @@ import { HalSource } from 'core-app/features/hal/interfaces'; @Directive() export class WorkPackageCreateComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { + readonly injector = inject(Injector); + protected readonly $state = inject(StateService); + protected readonly I18n = inject(I18nService); + protected readonly titleService = inject(OpTitleService); + protected readonly notificationService = inject(WorkPackageNotificationService); + protected readonly states = inject(States); + protected readonly wpCreate = inject(WorkPackageCreateService); + protected readonly wpViewFocus = inject(WorkPackageViewFocusService); + protected readonly wpTableFilters = inject(WorkPackageViewFiltersService); + protected readonly pathHelper = inject(PathHelperService); + protected readonly apiV3Service = inject(ApiV3Service); + protected readonly currentProjectService = inject(CurrentProjectService); + protected readonly cdRef = inject(ChangeDetectorRef); + public successState:string = splitViewRoute(this.$state); public cancelState:string = this.$state?.current?.data?.baseRoute; @@ -89,24 +96,6 @@ export class WorkPackageCreateComponent extends UntilDestroyedMixin implements O /** Explicitly remember destroy state in this abstract base */ protected destroyed = false; - constructor( - public readonly injector:Injector, - protected readonly $state:StateService, - protected readonly I18n:I18nService, - protected readonly titleService:OpTitleService, - protected readonly notificationService:WorkPackageNotificationService, - protected readonly states:States, - protected readonly wpCreate:WorkPackageCreateService, - protected readonly wpViewFocus:WorkPackageViewFocusService, - protected readonly wpTableFilters:WorkPackageViewFiltersService, - protected readonly pathHelper:PathHelperService, - protected readonly apiV3Service:ApiV3Service, - protected readonly currentProjectService:CurrentProjectService, - protected readonly cdRef:ChangeDetectorRef, - ) { - super(); - } - public ngOnInit() { // In case the create form is still routed via Angular, the stateParams are empty. We then read the params from the Transition if (this.routedFromAngular) { diff --git a/frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts b/frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts index 99bbcf29a6d8..ed9289390384 100644 --- a/frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts @@ -26,10 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - Injectable, - Injector, -} from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { firstValueFrom, Observable, @@ -68,23 +65,23 @@ export const newWorkPackageHref = '/api/v3/work_packages/new'; @Injectable() export class WorkPackageCreateService extends UntilDestroyedMixin { + protected injector = inject(Injector); + protected hooks = inject(HookService); + protected apiV3Service = inject(ApiV3Service); + protected halResourceService = inject(HalResourceService); + protected querySpace = inject(IsolatedQuerySpace); + protected authorisationService = inject(AuthorisationService); + protected halEditing = inject(HalResourceEditingService); + protected schemaCache = inject(SchemaCacheService); + protected halEvents = inject(HalEventsService); + protected attachmentsService = inject(AttachmentsResourceService); + protected form:Promise|undefined; // Allow callbacks to happen on newly created work packages protected newWorkPackageCreatedSubject = new Subject(); - constructor( - protected injector:Injector, - protected hooks:HookService, - protected apiV3Service:ApiV3Service, - protected halResourceService:HalResourceService, - protected querySpace:IsolatedQuerySpace, - protected authorisationService:AuthorisationService, - protected halEditing:HalResourceEditingService, - protected schemaCache:SchemaCacheService, - protected halEvents:HalEventsService, - protected attachmentsService:AttachmentsResourceService, - ) { + constructor() { super(); this.halEditing diff --git a/frontend/src/app/features/work-packages/components/wp-query/query-filters.service.ts b/frontend/src/app/features/work-packages/components/wp-query/query-filters.service.ts index ef0a450e64a5..ea889854dff6 100644 --- a/frontend/src/app/features/work-packages/components/wp-query/query-filters.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-query/query-filters.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; import { QueryFilterInstanceSchemaResource, @@ -11,8 +11,8 @@ import { CollectionResource } from 'core-app/features/hal/resources/collection-r @Injectable() export class QueryFiltersService { - constructor(protected schemaCache:SchemaCacheService) { - } + protected schemaCache = inject(SchemaCacheService); + /** * Get the matching schema of the filter resource diff --git a/frontend/src/app/features/work-packages/components/wp-query/query-param-listener.service.ts b/frontend/src/app/features/work-packages/components/wp-query/query-param-listener.service.ts index adf14df4d0cf..c10d297612fc 100644 --- a/frontend/src/app/features/work-packages/components/wp-query/query-param-listener.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-query/query-param-listener.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { WorkPackagesListChecksumService } from 'core-app/features/work-packages/components/wp-list/wp-list-checksum.service'; import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service'; import { TransitionService } from '@uirouter/core'; @@ -34,6 +34,8 @@ import { Subject } from 'rxjs'; @Injectable() export class QueryParamListenerService { + readonly injector = inject(Injector); + readonly wpListChecksumService:WorkPackagesListChecksumService = this.injector.get(WorkPackagesListChecksumService); readonly wpListService:WorkPackagesListService = this.injector.get(WorkPackagesListService); @@ -44,7 +46,7 @@ export class QueryParamListenerService { public queryChangeListener:Function; - constructor(readonly injector:Injector) { + constructor() { this.listenForQueryParamsChanged(); } diff --git a/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.spec.ts b/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.spec.ts index 0ca8f1bc024c..0a94e5263497 100644 --- a/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.spec.ts +++ b/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.spec.ts @@ -26,16 +26,29 @@ // See COPYRIGHT and LICENSE files for more details. //++ +import { TestBed } from '@angular/core/testing'; import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; +import { PaginationService } from 'core-app/shared/components/table-pagination/pagination-service'; describe('UrlParamsHelper', () => { const paginationStub = { getPerPage: () => 20, } as any; - const UrlParamsHelper = new UrlParamsHelperService(paginationStub); + let UrlParamsHelper:UrlParamsHelperService; let queryString; + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + UrlParamsHelperService, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + { provide: PaginationService, useValue: paginationStub }, + ], + }); + UrlParamsHelper = TestBed.inject(UrlParamsHelperService); + }); + describe('buildQueryString', () => { const params = { ids: [1, 2, 3], diff --git a/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.ts b/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.ts index 79478a48dabc..1ad8c4746634 100644 --- a/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.ts +++ b/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.ts @@ -31,7 +31,7 @@ import { QuerySortByResource } from 'core-app/features/hal/resources/query-sort- import { HalLink } from 'core-app/features/hal/hal-link/hal-link'; import idFromLink from 'core-app/features/hal/helpers/id-from-link'; import isPersistedResource from 'core-app/features/hal/helpers/is-persisted-resource'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; import { ApiV3Filter, ApiV3FilterBuilder, FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; import { PaginationService } from 'core-app/shared/components/table-pagination/pagination-service'; @@ -102,8 +102,8 @@ export interface QueryRequestParams { @Injectable({ providedIn: 'root' }) export class UrlParamsHelperService { - public constructor(public paginationService:PaginationService) { - } + paginationService = inject(PaginationService); + // copied more or less from angular buildUrl public buildQueryString(params:any) { diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service.ts index 633708ee7886..939263483763 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; @@ -41,11 +41,8 @@ import idFromLink from 'core-app/features/hal/helpers/id-from-link'; @Injectable() export class WpChildrenInlineCreateService extends WorkPackageInlineCreateService implements WpRelationInlineCreateServiceInterface { - constructor(readonly injector:Injector, - protected readonly wpRelationsHierarchyService:WorkPackageRelationsHierarchyService, - protected readonly schemaCache:SchemaCacheService) { - super(injector); - } + protected readonly wpRelationsHierarchyService = inject(WorkPackageRelationsHierarchyService); + protected readonly schemaCache = inject(SchemaCacheService); /** * A separate reference pane for the inline create component diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component.ts index d0ffc9275dbe..1bd82798aa06 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component.ts @@ -26,11 +26,10 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; import { OpUnlinkTableAction } from 'core-app/features/work-packages/components/wp-table/table-actions/actions/unlink-table-action'; import { OpTableActionFactory } from 'core-app/features/work-packages/components/wp-table/table-actions/table-action'; @@ -58,6 +57,14 @@ import { WorkPackageRelationsService } from 'core-app/features/work-packages/com changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageChildrenQueryComponent extends WorkPackageRelationQueryBase implements OnInit { + protected wpRelationsHierarchyService = inject(WorkPackageRelationsHierarchyService); + protected PathHelper = inject(PathHelperService); + protected wpInlineCreate = inject(WorkPackageInlineCreateService); + protected halEvents = inject(HalEventsService); + protected apiV3Service = inject(ApiV3Service); + readonly I18n = inject(I18nService); + readonly wpRelations = inject(WorkPackageRelationsService); + @Input() public workPackage:WorkPackageResource; @Input() public query:QueryResource; @@ -82,19 +89,6 @@ export class WorkPackageChildrenQueryComponent extends WorkPackageRelationQueryB ), ]; - constructor( - protected wpRelationsHierarchyService:WorkPackageRelationsHierarchyService, - protected PathHelper:PathHelperService, - protected wpInlineCreate:WorkPackageInlineCreateService, - protected halEvents:HalEventsService, - protected apiV3Service:ApiV3Service, - protected queryUrlParamsHelper:UrlParamsHelperService, - readonly I18n:I18nService, - readonly wpRelations:WorkPackageRelationsService, - ) { - super(queryUrlParamsHelper); - } - ngOnInit() { // Set reference target and reference class this.wpInlineCreate.referenceTarget = this.workPackage; diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component.ts index 3057f20973b9..7f814538a47a 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageInlineCreateService, @@ -61,6 +61,16 @@ import { FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-bui changeDetection: ChangeDetectionStrategy.Default, }) export class WpRelationInlineAddExistingComponent { + protected readonly parent = inject(WorkPackageInlineCreateComponent); + protected readonly wpInlineCreate = inject(WorkPackageInlineCreateService) as WpRelationInlineCreateServiceInterface; + protected apiV3Service = inject(ApiV3Service); + protected wpRelations = inject(WorkPackageRelationsService); + protected notificationService = inject(WorkPackageNotificationService); + protected halEvents = inject(HalEventsService); + protected urlParamsHelper = inject(UrlParamsHelperService); + protected querySpace = inject(IsolatedQuerySpace); + protected readonly I18n = inject(I18nService); + public selectedWpId:string; public isDisabled = false; @@ -71,18 +81,6 @@ export class WpRelationInlineAddExistingComponent { abort: this.I18n.t('js.relation_buttons.abort'), }; - constructor( - protected readonly parent:WorkPackageInlineCreateComponent, - @Inject(WorkPackageInlineCreateService) protected readonly wpInlineCreate:WpRelationInlineCreateServiceInterface, - protected apiV3Service:ApiV3Service, - protected wpRelations:WorkPackageRelationsService, - protected notificationService:WorkPackageNotificationService, - protected halEvents:HalEventsService, - protected urlParamsHelper:UrlParamsHelperService, - protected querySpace:IsolatedQuerySpace, - protected readonly I18n:I18nService, - ) {} - public addExisting() { if (_.isNil(this.selectedWpId)) { return; diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service.ts index df66e0976a4c..8352fad2383f 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; import { WpRelationInlineAddExistingComponent } from 'core-app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component'; @@ -46,10 +46,6 @@ export class WpRelationInlineCreateService extends WorkPackageInlineCreateServic @InjectField() halEvents:HalEventsService; - constructor(public injector:Injector) { - super(injector); - } - /** * A separate reference pane for the inline create component */ diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component.ts index 30a888bb6ce7..35a6d33b7c99 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component.ts @@ -26,17 +26,10 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - Inject, - Input, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; import { OpUnlinkTableAction } from 'core-app/features/work-packages/components/wp-table/table-actions/actions/unlink-table-action'; import { OpTableActionFactory } from 'core-app/features/work-packages/components/wp-table/table-actions/table-action'; import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; @@ -63,6 +56,13 @@ import { GroupDescriptor } from 'core-app/features/work-packages/components/wp-s changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationQueryComponent extends WorkPackageRelationQueryBase implements OnInit { + protected readonly PathHelper = inject(PathHelperService); + protected readonly wpInlineCreate = inject(WorkPackageInlineCreateService) as WpRelationInlineCreateService; + protected readonly wpRelations = inject(WorkPackageRelationsService); + protected readonly halEvents = inject(HalEventsService); + protected readonly notificationService = inject(WorkPackageNotificationService); + protected readonly I18n = inject(I18nService); + @Input() public workPackage:WorkPackageResource; @Input() public query:QueryResource; @@ -89,16 +89,6 @@ export class WorkPackageRelationQueryComponent extends WorkPackageRelationQueryB public idFromLink = idFromLink; - constructor(protected readonly PathHelper:PathHelperService, - @Inject(WorkPackageInlineCreateService) protected readonly wpInlineCreate:WpRelationInlineCreateService, - protected readonly wpRelations:WorkPackageRelationsService, - protected readonly halEvents:HalEventsService, - protected readonly queryUrlParamsHelper:UrlParamsHelperService, - protected readonly notificationService:WorkPackageNotificationService, - protected readonly I18n:I18nService) { - super(queryUrlParamsHelper); - } - ngOnInit() { const relationType = this.getRelationTypeFromQuery(); diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/wp-relation-query.base.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/wp-relation-query.base.ts index 890b01765322..8a97dcb5b129 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/wp-relation-query.base.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/wp-relation-query.base.ts @@ -27,7 +27,7 @@ //++ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { Directive, ViewChild } from '@angular/core'; +import { Directive, ViewChild, inject } from '@angular/core'; import { WorkPackageEmbeddedTableComponent } from 'core-app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; @@ -35,6 +35,8 @@ import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destr @Directive() export abstract class WorkPackageRelationQueryBase extends UntilDestroyedMixin { + protected queryUrlParamsHelper = inject(UrlParamsHelperService); + public workPackage:WorkPackageResource; /** Input is either a query resource, or directly query props */ @@ -49,10 +51,6 @@ export abstract class WorkPackageRelationQueryBase extends UntilDestroyedMixin { /** Reference to the embedded table instance */ @ViewChild('embeddedTable') protected embeddedTable?:WorkPackageEmbeddedTableComponent; - constructor(protected queryUrlParamsHelper:UrlParamsHelperService) { - super(); - } - /** * Request to refresh the results of the embedded table */ diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.component.ts index 2fafea2ad624..0ac5e207e7ec 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.component.ts @@ -1,8 +1,6 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; @@ -23,6 +21,14 @@ import { Highlighting } from 'core-app/features/work-packages/components/wp-fast changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationRowComponent extends UntilDestroyedMixin implements OnInit { + protected apiV3Service = inject(ApiV3Service); + protected notificationService = inject(WorkPackageNotificationService); + protected wpRelations = inject(WorkPackageRelationsService); + protected halEvents = inject(HalEventsService); + protected I18n = inject(I18nService); + protected cdRef = inject(ChangeDetectorRef); + protected PathHelper = inject(PathHelperService); + @Input() public workPackage:WorkPackageResource; @Input() public relatedWorkPackage:WorkPackageResource; @@ -70,16 +76,6 @@ export class WorkPackageRelationRowComponent extends UntilDestroyedMixin impleme }, }; - constructor(protected apiV3Service:ApiV3Service, - protected notificationService:WorkPackageNotificationService, - protected wpRelations:WorkPackageRelationsService, - protected halEvents:HalEventsService, - protected I18n:I18nService, - protected cdRef:ChangeDetectorRef, - protected PathHelper:PathHelperService) { - super(); - } - ngOnInit() { this.relation = this.relatedWorkPackage.relatedBy!; diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-create.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-create.component.ts index 3d2c1564cd0f..8316aff3efa2 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-create.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-create.component.ts @@ -1,10 +1,5 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - Input, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; @@ -21,6 +16,12 @@ import { WorkPackageRelationsService } from '../wp-relations.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationsCreateComponent { + readonly I18n = inject(I18nService); + protected wpRelations = inject(WorkPackageRelationsService); + protected notificationService = inject(WorkPackageNotificationService); + protected halEvents = inject(HalEventsService); + protected cdRef = inject(ChangeDetectorRef); + @Input() readonly workPackage:WorkPackageResource; public showRelationsCreateForm = false; @@ -39,15 +40,6 @@ export class WorkPackageRelationsCreateComponent { addNewRelation: this.I18n.t('js.relation_buttons.add_new_relation'), }; - constructor( - readonly I18n:I18nService, - protected wpRelations:WorkPackageRelationsService, - protected notificationService:WorkPackageNotificationService, - protected halEvents:HalEventsService, - protected cdRef:ChangeDetectorRef, - ) { - } - public onSelected(workPackage?:WorkPackageResource) { if (workPackage) { this.selectedWpId = workPackage.id!; diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.component.ts index 7b1390eb61aa..2cbbebb564b8 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.component.ts @@ -27,9 +27,7 @@ //++ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { - ChangeDetectionStrategy, Component, ElementRef, EventEmitter, HostBinding, Input, Output, ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, HostBinding, Input, Output, ViewChild, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ @@ -42,6 +40,8 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationsGroupComponent { + readonly I18n = inject(I18nService); + @HostBinding('class.attributes-group') className = true; @Input() public relatedWorkPackages:WorkPackageResource[]; @@ -63,11 +63,6 @@ export class WorkPackageRelationsGroupComponent { groupByRelation: this.I18n.t('js.relation_buttons.group_by_relation_type'), }; - constructor( - readonly I18n:I18nService, - ) { - } - public get togglerText() { if (this.groupByWorkPackageType) { return this.text.groupByRelation; diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.directive.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.directive.ts index c7f52ec6bdfc..75a54892d71c 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.directive.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -49,6 +49,12 @@ import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationsHierarchyComponent extends UntilDestroyedMixin implements OnInit { + readonly apiV3Service = inject(ApiV3Service); + readonly PathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); + readonly schemaCache = inject(SchemaCacheService); + readonly cdRef = inject(ChangeDetectorRef); + @Input() public workPackage:WorkPackageResource; @Input() public relationType:string; @@ -65,16 +71,6 @@ export class WorkPackageRelationsHierarchyComponent extends UntilDestroyedMixin public childrenQueryProps:any; - constructor( - readonly apiV3Service:ApiV3Service, - readonly PathHelper:PathHelperService, - readonly I18n:I18nService, - readonly schemaCache:SchemaCacheService, - readonly cdRef:ChangeDetectorRef, - ) { - super(); - } - public text = { parentHeadline: this.I18n.t('js.relations_hierarchy.parent_headline'), childrenHeadline: this.I18n.t('js.relations_hierarchy.children_headline'), diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service.ts index bb71ba67f383..7ba2ea95967b 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service.ts @@ -29,7 +29,7 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { States } from 'core-app/core/states/states.service'; import { StateService } from '@uirouter/core'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; @@ -37,14 +37,13 @@ import { HalEventsService } from 'core-app/features/hal/services/hal-events.serv @Injectable() export class WorkPackageRelationsHierarchyService { - constructor(protected $state:StateService, - protected states:States, - protected halEvents:HalEventsService, - protected notificationService:WorkPackageNotificationService, - protected pathHelper:PathHelperService, - protected apiV3Service:ApiV3Service) { + protected $state = inject(StateService); + protected states = inject(States); + protected halEvents = inject(HalEventsService); + protected notificationService = inject(WorkPackageNotificationService); + protected pathHelper = inject(PathHelperService); + protected apiV3Service = inject(ApiV3Service); - } public changeParent(workPackage:WorkPackageResource, parentId:string|null) { const payload:any = { diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.component.ts index b256f51705a3..24ba6141e126 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.component.ts @@ -26,16 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - Component, - ElementRef, - Input, - OnDestroy, - OnInit, - ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { filter, throttleTime } from 'rxjs/operators'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @@ -53,6 +44,12 @@ import { HalEventsService } from 'core-app/features/hal/services/hal-events.serv standalone: false, }) export class WorkPackageRelationsComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit, OnDestroy { + private wpRelations = inject(WorkPackageRelationsService); + private apiV3Service = inject(ApiV3Service); + private halEvents = inject(HalEventsService); + private PathHelper = inject(PathHelperService); + private turboRequests = inject(TurboRequestsService); + @Input() public workPackage:WorkPackageResource; @ViewChild('frameElement') readonly relationTurboFrame:ElementRef; @@ -61,16 +58,6 @@ export class WorkPackageRelationsComponent extends UntilDestroyedMixin implement private turboFrameListener:EventListener = this.updateFrontendData.bind(this); - constructor( - private wpRelations:WorkPackageRelationsService, - private apiV3Service:ApiV3Service, - private halEvents:HalEventsService, - private PathHelper:PathHelperService, - private turboRequests:TurboRequestsService, -) { - super(); - } - ngOnInit() { this.turboFrameSrc = `${this.PathHelper.staticBase}/work_packages/${this.workPackage.id}/relations_tab`; } diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.service.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.service.ts index 900e49258c4d..c84dadaf40d1 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.service.ts @@ -1,7 +1,7 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { multiInput, MultiInputState, StatesGroup } from '@openproject/reactivestates'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; @@ -26,12 +26,12 @@ export class RelationStateGroup extends StatesGroup { @Injectable() export class WorkPackageRelationsService extends StateCacheService { - constructor( - private PathHelper:PathHelperService, - private apiV3Service:ApiV3Service, - private halResource:HalResourceService, - readonly turboRequests:TurboRequestsService, - ) { + private PathHelper = inject(PathHelperService); + private apiV3Service = inject(ApiV3Service); + private halResource = inject(HalResourceService); + readonly turboRequests = inject(TurboRequestsService); + + constructor() { super(new RelationStateGroup().relations); } diff --git a/frontend/src/app/features/work-packages/components/wp-reminder-modal/wp-reminder.modal.ts b/frontend/src/app/features/work-packages/components/wp-reminder-modal/wp-reminder.modal.ts index 089fdaaf5bb6..c7d5d4a2bb15 100644 --- a/frontend/src/app/features/work-packages/components/wp-reminder-modal/wp-reminder.modal.ts +++ b/frontend/src/app/features/work-packages/components/wp-reminder-modal/wp-reminder.modal.ts @@ -1,15 +1,5 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Inject, - OnInit, - ViewChild, AfterViewInit, OnDestroy, -} from '@angular/core'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { ChangeDetectionStrategy, Component, ElementRef, OnInit, ViewChild, AfterViewInit, OnDestroy, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -28,6 +18,11 @@ import { CollectionResource } from 'core-app/features/hal/resources/collection-r standalone: false, }) export class WorkPackageReminderModalComponent extends OpModalComponent implements OnInit, AfterViewInit, OnDestroy { + readonly I18n = inject(I18nService); + readonly pathHelper = inject(PathHelperService); + readonly actions$ = inject(ActionsService); + readonly apiV3Service = inject(ApiV3Service); + @ViewChild('frameElement') frameElement:ElementRef; // Hide close button so it's not duplicated in primer (WP#51699) @@ -48,16 +43,8 @@ export class WorkPackageReminderModalComponent extends OpModalComponent implemen private boundListener = this.turboSubmitEndListener.bind(this); - constructor( - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - readonly elementRef:ElementRef, - readonly pathHelper:PathHelperService, - readonly actions$:ActionsService, - readonly apiV3Service:ApiV3Service, - ) { - super(locals, cdRef, elementRef); + constructor() { + super(); this.workPackage = this.locals.workPackage as WorkPackageResource; this.preset = this.locals.preset as ReminderPreset | undefined; diff --git a/frontend/src/app/features/work-packages/components/wp-share-modal/wp-share.modal.ts b/frontend/src/app/features/work-packages/components/wp-share-modal/wp-share.modal.ts index fe81b4d0b2b9..5cb321d37309 100644 --- a/frontend/src/app/features/work-packages/components/wp-share-modal/wp-share.modal.ts +++ b/frontend/src/app/features/work-packages/components/wp-share-modal/wp-share.modal.ts @@ -1,15 +1,5 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Inject, - OnInit, - ViewChild, -} from '@angular/core'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { ChangeDetectionStrategy, Component, ElementRef, OnInit, ViewChild, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -23,6 +13,10 @@ import { shareModalUpdated } from 'core-app/features/work-packages/components/wp standalone: false, }) export class WorkPackageShareModalComponent extends OpModalComponent implements OnInit { + readonly I18n = inject(I18nService); + readonly pathHelper = inject(PathHelperService); + readonly actions$ = inject(ActionsService); + @ViewChild('frameElement') frameElement:ElementRef|undefined; // Hide close button so it's not duplicated in primer (WP#51699) @@ -36,15 +30,8 @@ export class WorkPackageShareModalComponent extends OpModalComponent implements button_close: this.I18n.t('js.button_close'), }; - constructor( - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - readonly elementRef:ElementRef, - readonly pathHelper:PathHelperService, - readonly actions$:ActionsService, - ) { - super(locals, cdRef, elementRef); + constructor() { + super(); this.workPackage = this.locals.workPackage as WorkPackageResource; this.frameSrc = this.pathHelper.workPackageSharePath(this.workPackage.id!); diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts index 01cb30fbf20f..57c089846f96 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectorRef, Directive, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Directive, OnInit, inject } from '@angular/core'; import { UIRouterGlobals } from '@uirouter/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -40,25 +40,21 @@ import { UrlHelpers } from 'core-stimulus/controllers/dynamic/work-packages/acti @Directive() export class ActivityPanelBaseController extends UntilDestroyedMixin implements OnInit { + readonly apiV3Service = inject(ApiV3Service); + readonly I18n = inject(I18nService); + readonly cdRef = inject(ChangeDetectorRef); + readonly uiRouterGlobals = inject(UIRouterGlobals); + readonly storeService = inject(WpSingleViewService); + readonly browserDetector = inject(BrowserDetector); + readonly deviceService = inject(DeviceService); + readonly pathHelper = inject(PathHelperService); + public workPackage:WorkPackageResource; public workPackageId:string; public turboFrameSrc:string; - constructor( - readonly apiV3Service:ApiV3Service, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - readonly uiRouterGlobals:UIRouterGlobals, - readonly storeService:WpSingleViewService, - readonly browserDetector:BrowserDetector, - readonly deviceService:DeviceService, - readonly pathHelper:PathHelperService, - ) { - super(); - } - ngOnInit():void { this.turboFrameSrc = this.buildTurboFrameSrc(); } diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service.ts index 91d333353b0e..21748e04203c 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service.ts @@ -28,17 +28,16 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { WorkPackageLinkedResourceCache } from 'core-app/features/work-packages/components/wp-single-view-tabs/wp-linked-resource-cache.service'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; @Injectable() export class WorkPackagesActivityService extends WorkPackageLinkedResourceCache { - constructor(public ConfigurationService:ConfigurationService, - readonly timezoneService:TimezoneService) { - super(); - } + ConfigurationService = inject(ConfigurationService); + readonly timezoneService = inject(TimezoneService); + public get order() { return this.isReversed ? 'desc' : 'asc'; diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/files-tab/op-files-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/files-tab/op-files-tab.component.ts index 9f668e023824..73a482cd7fba 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/files-tab/op-files-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/files-tab/op-files-tab.component.ts @@ -26,12 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - Input, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core'; import { combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -51,6 +46,12 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service standalone: false, }) export class WorkPackageFilesTabComponent implements OnInit { + private readonly i18n = inject(I18nService); + private readonly currentUserService = inject(CurrentUserService); + private readonly projectStoragesResourceService = inject(ProjectStoragesResourceService); + private readonly pathHelper = inject(PathHelperService); + private readonly turboRequests = inject(TurboRequestsService); + @Input() workPackage:WorkPackageResource; text = { @@ -67,14 +68,6 @@ export class WorkPackageFilesTabComponent implements OnInit { showAttachments:boolean; - constructor( - private readonly i18n:I18nService, - private readonly currentUserService:CurrentUserService, - private readonly projectStoragesResourceService:ProjectStoragesResourceService, - private readonly pathHelper:PathHelperService, - private readonly turboRequests:TurboRequestsService, - ) { } - ngOnInit():void { const project = this.workPackage.project as HalResource; if (project.id === null) { diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.spec.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.spec.ts index 4344381b8d44..ba6dbaf42444 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.spec.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.spec.ts @@ -26,6 +26,12 @@ // See COPYRIGHT and LICENSE files for more details. //++ +import { TestBed } from '@angular/core/testing'; +import { + StateService, + TransitionService, + UIRouterGlobals, +} from '@uirouter/core'; import { KeepTabService } from './keep-tab.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @@ -57,7 +63,20 @@ describe('keepTab service', () => { params: { tabIdentifier: 'activity' }, }; - keepTab = new KeepTabService($state, uiRouterGlobals, $transitions, pathHelper, currentProject); + TestBed.configureTestingModule({ + providers: [ + KeepTabService, + /* eslint-disable @typescript-eslint/no-unsafe-assignment */ + { provide: StateService, useValue: $state }, + { provide: UIRouterGlobals, useValue: uiRouterGlobals }, + { provide: TransitionService, useValue: $transitions }, + { provide: PathHelperService, useValue: pathHelper }, + { provide: CurrentProjectService, useValue: currentProject }, + /* eslint-enable @typescript-eslint/no-unsafe-assignment */ + ], + }); + + keepTab = TestBed.inject(KeepTabService); defaults = { showTab: 'work-packages.show.tabs', diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.ts index d86cf6c42ae7..93286856f524 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.ts @@ -33,24 +33,26 @@ import { UIRouterGlobals, } from '@uirouter/core'; import { ReplaySubject } from 'rxjs'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { splitViewRoute } from 'core-app/features/work-packages/routing/split-view-routes.helper'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @Injectable({ providedIn: 'root' }) export class KeepTabService { + protected $state = inject(StateService); + protected uiRouterGlobals = inject(UIRouterGlobals); + protected $transitions = inject(TransitionService); + protected pathHelper = inject(PathHelperService); + protected currentProject = inject(CurrentProjectService); + protected currentTab = 'overview'; protected subject = new ReplaySubject>(1); - constructor( - protected $state:StateService, - protected uiRouterGlobals:UIRouterGlobals, - protected $transitions:TransitionService, - protected pathHelper:PathHelperService, - protected currentProject:CurrentProjectService, - ) { + constructor() { + const $transitions = this.$transitions; + this.updateTabs(); $transitions.onSuccess({}, (transition:Transition) => { this.updateTabs(transition.params('to').tabIdentifier); diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component.ts index 8ebc7a6c611a..848b93711517 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core'; import { StateService } from '@uirouter/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -43,21 +43,17 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageOverviewTabComponent extends UntilDestroyedMixin implements OnInit { + readonly I18n = inject(I18nService); + readonly $state = inject(StateService); + readonly apiV3Service = inject(ApiV3Service); + readonly cdRef = inject(ChangeDetectorRef); + @Input() public workPackage:WorkPackageResource; public workPackageId:string; public tabName = this.I18n.t('js.label_latest_activity'); - public constructor( - readonly I18n:I18nService, - readonly $state:StateService, - readonly apiV3Service:ApiV3Service, - readonly cdRef:ChangeDetectorRef, - ) { - super(); - } - ngOnInit() { this.workPackageId = this.workPackage?.id || this.$state.params.workPackageId as string; diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component.ts index 4d2b69fbc3d7..e3f1b040ea0e 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component.ts @@ -27,7 +27,7 @@ //++ import { UIRouterGlobals } from '@uirouter/core'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @@ -43,19 +43,15 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationsTabComponent extends UntilDestroyedMixin implements OnInit { + readonly I18n = inject(I18nService); + readonly uiRouterGlobals = inject(UIRouterGlobals); + readonly apiV3Service = inject(ApiV3Service); + readonly cdRef = inject(ChangeDetectorRef); + @Input() public workPackageId?:string; @Input() public workPackage:WorkPackageResource; - public constructor( - readonly I18n:I18nService, - readonly uiRouterGlobals:UIRouterGlobals, - readonly apiV3Service:ApiV3Service, - readonly cdRef:ChangeDetectorRef, - ) { - super(); - } - ngOnInit() { const { workPackageId } = this.uiRouterGlobals.params as unknown as { workPackageId:string }; this.workPackageId = (this.workPackage.id!) || workPackageId; diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/watchers-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/watchers-tab.component.ts index fe219032838c..4889aec0550a 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/watchers-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/watchers-tab.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, inject } from '@angular/core'; import { UIRouterGlobals } from '@uirouter/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @@ -50,6 +50,17 @@ import { TurboRequestsService } from 'core-app/core/turbo/turbo-requests.service standalone: false, }) export class WorkPackageWatchersTabComponent extends UntilDestroyedMixin implements OnInit { + readonly I18n = inject(I18nService); + readonly elementRef = inject(ElementRef); + readonly wpWatchersService = inject(WorkPackageWatchersService); + readonly uiRouterGlobals = inject(UIRouterGlobals); + readonly notificationService = inject(WorkPackageNotificationService); + readonly loadingIndicator = inject(LoadingIndicatorService); + readonly cdRef = inject(ChangeDetectorRef); + readonly pathHelper = inject(PathHelperService); + readonly apiV3Service = inject(ApiV3Service); + readonly turboRequests = inject(TurboRequestsService); + @Input() public workPackage:WorkPackageResource; public workPackageId:string; @@ -78,21 +89,6 @@ export class WorkPackageWatchersTabComponent extends UntilDestroyedMixin impleme }, }; - public constructor( - readonly I18n:I18nService, - readonly elementRef:ElementRef, - readonly wpWatchersService:WorkPackageWatchersService, - readonly uiRouterGlobals:UIRouterGlobals, - readonly notificationService:WorkPackageNotificationService, - readonly loadingIndicator:LoadingIndicatorService, - readonly cdRef:ChangeDetectorRef, - readonly pathHelper:PathHelperService, - readonly apiV3Service:ApiV3Service, - readonly turboRequests:TurboRequestsService, - ) { - super(); - } - public ngOnInit() { this.element = this.elementRef.nativeElement; const { workPackageId } = this.uiRouterGlobals.params as unknown as { workPackageId:string }; diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watcher-entry.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watcher-entry.component.ts index 1315b29b2d3e..b5da0555b0dc 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watcher-entry.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watcher-entry.component.ts @@ -26,12 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - Input, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { UserResource } from 'core-app/features/hal/resources/user-resource'; import { WorkPackageWatchersTabComponent } from './watchers-tab.component'; @@ -44,14 +39,13 @@ import { WorkPackageWatchersTabComponent } from './watchers-tab.component'; standalone: false, }) export class WorkPackageWatcherEntryComponent implements OnInit { + readonly I18n = inject(I18nService); + readonly panelCtrl = inject(WorkPackageWatchersTabComponent); + @Input() public watcher:UserResource; public text:{ remove:string }; - constructor(readonly I18n:I18nService, - readonly panelCtrl:WorkPackageWatchersTabComponent) { - } - ngOnInit():void { this.text = { remove: this.I18n.t('js.label_remove_watcher', { name: this.watcher.name }), diff --git a/frontend/src/app/features/work-packages/components/wp-single-view/wp-single-view.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view/wp-single-view.component.ts index 9ebbde7098ed..ca4533f1b342 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view/wp-single-view.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view/wp-single-view.component.ts @@ -26,15 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Injector, - Input, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Injector, Input, OnInit, inject } from '@angular/core'; import { StateService } from '@uirouter/core'; import { BehaviorSubject, combineLatest } from 'rxjs'; import { distinctUntilChanged, first, map } from 'rxjs/operators'; @@ -99,6 +91,23 @@ export const overflowingContainerAttribute = 'overflowingIdentifier'; standalone: false, }) export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implements OnInit { + protected readonly injector = inject(Injector); + private readonly states = inject(States); + private readonly I18n = inject(I18nService); + private readonly hook = inject(HookService); + private readonly $state = inject(StateService); + private readonly elementRef = inject(ElementRef); + private readonly cdRef = inject(ChangeDetectorRef); + private readonly PathHelper = inject(PathHelperService); + private readonly schemaCache = inject(SchemaCacheService); + private readonly currentProject = inject(CurrentProjectService); + private readonly halEditing = inject(HalResourceEditingService); + private readonly halResourceService = inject(HalResourceService); + private readonly currentUserService = inject(CurrentUserService); + private readonly displayFieldService = inject(DisplayFieldService); + private readonly projectsResourceService = inject(ProjectsResourceService); + private readonly projectStoragesService = inject(ProjectStoragesResourceService); + @Input() public workPackage:WorkPackageResource; /** Should we show the project field */ @@ -145,27 +154,6 @@ export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implemen projectStorages = new BehaviorSubject([]); - constructor( - protected readonly injector:Injector, - private readonly states:States, - private readonly I18n:I18nService, - private readonly hook:HookService, - private readonly $state:StateService, - private readonly elementRef:ElementRef, - private readonly cdRef:ChangeDetectorRef, - private readonly PathHelper:PathHelperService, - private readonly schemaCache:SchemaCacheService, - private readonly currentProject:CurrentProjectService, - private readonly halEditing:HalResourceEditingService, - private readonly halResourceService:HalResourceService, - private readonly currentUserService:CurrentUserService, - private readonly displayFieldService:DisplayFieldService, - private readonly projectsResourceService:ProjectsResourceService, - private readonly projectStoragesService:ProjectStoragesResourceService, - ) { - super(); - } - public ngOnInit():void { this.element = this.elementRef.nativeElement as HTMLElement; diff --git a/frontend/src/app/features/work-packages/components/wp-subject/wp-subject.component.ts b/frontend/src/app/features/work-packages/components/wp-subject/wp-subject.component.ts index 121c81957a2b..9881e8271d66 100644 --- a/frontend/src/app/features/work-packages/components/wp-subject/wp-subject.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-subject/wp-subject.component.ts @@ -26,11 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - Input, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, inject } from '@angular/core'; import { UIRouterGlobals } from '@uirouter/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { randomString } from 'core-app/shared/helpers/random-string'; @@ -47,14 +43,10 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageSubjectComponent extends UntilDestroyedMixin { + protected uiRouterGlobals = inject(UIRouterGlobals); + protected apiV3Service = inject(ApiV3Service); + @Input() workPackage:WorkPackageResource; public readonly uniqueElementIdentifier = `work-packages--subject-type-row-${randomString(16)}`; - - constructor( - protected uiRouterGlobals:UIRouterGlobals, - protected apiV3Service:ApiV3Service, - ) { - super(); - } } diff --git a/frontend/src/app/features/work-packages/components/wp-table/config-menu/config-menu.component.ts b/frontend/src/app/features/work-packages/components/wp-table/config-menu/config-menu.component.ts index 75f67ec7be82..d4c289f3388a 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/config-menu/config-menu.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/config-menu/config-menu.component.ts @@ -1,5 +1,5 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { ChangeDetectionStrategy, Component, Injector } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Injector, inject } from '@angular/core'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; import { WpTableConfigurationModalComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal'; @@ -14,17 +14,15 @@ import { WpTableConfigurationModalComponent } from 'core-app/features/work-packa changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackagesTableConfigMenuComponent { + readonly I18n = inject(I18nService); + readonly injector = inject(Injector); + readonly opModalService = inject(OpModalService); + readonly opContextMenu = inject(OPContextMenuService); + public text = { configureTable: this.I18n.t('js.toolbar.settings.configure_view'), }; - constructor( - readonly I18n:I18nService, - readonly injector:Injector, - readonly opModalService:OpModalService, - readonly opContextMenu:OPContextMenuService, - ) { } - public openTableConfigurationModal() { this.opContextMenu.close(); this.opModalService.show(WpTableConfigurationModalComponent, this.injector); diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component.ts index afadf97d491d..613929cc2292 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Injector, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; import { @@ -20,6 +20,10 @@ import { changeDetection: ChangeDetectionStrategy.Default, }) export class WpTableConfigurationColumnsTabComponent implements TabComponent, OnInit { + readonly injector = inject(Injector); + readonly I18n = inject(I18nService); + readonly wpTableColumns = inject(WorkPackageViewColumnsService); + public availableColumnsOptions = this.wpTableColumns.all.map((c) => this.column2Like(c)); public availableColumns = this.wpTableColumns.all; @@ -40,13 +44,6 @@ export class WpTableConfigurationColumnsTabComponent implements TabComponent, On inputDragLabel: this.I18n.t('js.label_manage_columns'), }; - constructor( - readonly injector:Injector, - readonly I18n:I18nService, - readonly wpTableColumns:WorkPackageViewColumnsService, -) { - } - public onSave() { this.wpTableColumns.setColumnsById(this.selectedColumns.map((c) => c.id)); } diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component.ts index 3526c27874d4..a173cea748c5 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component.ts @@ -3,7 +3,7 @@ import { TabComponent } from 'core-app/features/work-packages/components/wp-tabl import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; import { WorkPackageViewSumService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit, inject } from '@angular/core'; import { QueryGroupByResource } from 'core-app/features/hal/resources/query-group-by-resource'; @Component({ @@ -16,6 +16,13 @@ import { QueryGroupByResource } from 'core-app/features/hal/resources/query-grou changeDetection: ChangeDetectionStrategy.Default, }) export class WpTableConfigurationDisplaySettingsTabComponent implements TabComponent, OnInit { + readonly injector = inject(Injector); + readonly I18n = inject(I18nService); + readonly wpTableGroupBy = inject(WorkPackageViewGroupByService); + readonly wpTableHierarchies = inject(WorkPackageViewHierarchiesService); + readonly wpTableSums = inject(WorkPackageViewSumService); + readonly cdRef = inject(ChangeDetectorRef); + // Display mode public displayMode:'hierarchy'|'grouped'|'default' = 'default'; @@ -44,15 +51,6 @@ export class WpTableConfigurationDisplaySettingsTabComponent implements TabCompo }, }; - constructor( - readonly injector:Injector, - readonly I18n:I18nService, - readonly wpTableGroupBy:WorkPackageViewGroupByService, - readonly wpTableHierarchies:WorkPackageViewHierarchiesService, - readonly wpTableSums:WorkPackageViewSumService, - readonly cdRef:ChangeDetectorRef, - ) { } - public onSave() { // Update hierarchy state this.wpTableHierarchies.setEnabled(this.displayMode === 'hierarchy'); diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/filters-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/filters-tab.component.ts index 0af78ae3c7b1..b672fee06f13 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/filters-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/filters-tab.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Injector, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TabComponent, @@ -19,6 +19,11 @@ import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/que standalone: false, }) export class WpTableConfigurationFiltersTabComponent implements TabComponent, OnInit { + readonly injector = inject(Injector); + readonly I18n = inject(I18nService); + readonly wpTableFilters = inject(WorkPackageViewFiltersService); + readonly wpFiltersService = inject(WorkPackageFiltersService); + public filters:QueryFilterInstanceResource[] = []; public eeShowBanners = false; @@ -32,14 +37,6 @@ export class WpTableConfigurationFiltersTabComponent implements TabComponent, On upsellRelationColumnsLink: this.I18n.t('js.modals.upsell_relation_columns_link'), }; - constructor( - readonly injector:Injector, - readonly I18n:I18nService, - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly wpFiltersService:WorkPackageFiltersService, - ) { - } - ngOnInit() { this.wpTableFilters .onReady() diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/highlighting-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/highlighting-tab.component.ts index c719ba6b6cdb..d3e543521077 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/highlighting-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/highlighting-tab.component.ts @@ -1,9 +1,4 @@ -import { - ChangeDetectionStrategy, - Component, - Injector, - ViewChild, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Injector, ViewChild, OnInit, inject } from '@angular/core'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { WorkPackageViewHighlightingService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -22,6 +17,13 @@ import { repositionDropdownBugfix } from 'core-app/shared/components/autocomplet standalone: false, }) export class WpTableConfigurationHighlightingTabComponent implements TabComponent, OnInit { + readonly injector = inject(Injector); + readonly I18n = inject(I18nService); + readonly states = inject(States); + readonly querySpace = inject(IsolatedQuerySpace); + readonly Banners = inject(BannersService); + readonly wpTableHighlight = inject(WorkPackageViewHighlightingService); + // Display mode public highlightingMode:HighlightingMode = 'inline'; @@ -54,14 +56,6 @@ export class WpTableConfigurationHighlightingTabComponent implements TabComponen more_info_link: enterpriseDocsUrl.tableHighlighting, }; - constructor(readonly injector:Injector, - readonly I18n:I18nService, - readonly states:States, - readonly querySpace:IsolatedQuerySpace, - readonly Banners:BannersService, - readonly wpTableHighlight:WorkPackageViewHighlightingService) { - } - ngOnInit() { this.availableInlineHighlightedAttributes = this.availableHighlightedAttributes; this.availableRowHighlightedAttributes = [ diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component.ts index aa35a7696cf1..8a5f49122cfd 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; @@ -30,6 +30,11 @@ export type SortingMode = 'automatic'|'manual'; changeDetection: ChangeDetectionStrategy.Default, }) export class WpTableConfigurationSortByTabComponent implements TabComponent, OnInit { + readonly injector = inject(Injector); + readonly I18n = inject(I18nService); + readonly wpTableSortBy = inject(WorkPackageViewSortByService); + readonly cdRef = inject(ChangeDetectorRef); + public text = { title: this.I18n.t('js.label_sort_by'), placeholder: this.I18n.t('js.placeholders.default'), @@ -61,13 +66,6 @@ export class WpTableConfigurationSortByTabComponent implements TabComponent, OnI public manualSortColumn:SortColumn; - constructor(readonly injector:Injector, - readonly I18n:I18nService, - readonly wpTableSortBy:WorkPackageViewSortByService, - readonly cdRef:ChangeDetectorRef) { - - } - public onSave() { let sortElements; if (this.sortingMode === 'automatic') { diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component.ts index 0d8e8618b264..5092244b5dab 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component.ts @@ -1,9 +1,4 @@ -import { - ChangeDetectionStrategy, - Component, - Injector, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Injector, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; @@ -22,6 +17,12 @@ import { StateService } from '@uirouter/angular'; changeDetection: ChangeDetectionStrategy.Default, }) export class WpTableConfigurationTimelinesTabComponent implements TabComponent, OnInit { + readonly injector = inject(Injector); + readonly I18n = inject(I18nService); + readonly wpTableTimeline = inject(WorkPackageViewTimelineService); + readonly wpTableColumns = inject(WorkPackageViewColumnsService); + readonly $state = inject(StateService); + public timelineVisible = false; public availableAttributes:{ id:string, name:string }[]; @@ -61,15 +62,6 @@ export class WpTableConfigurationTimelinesTabComponent implements TabComponent, }, }; - constructor( - readonly injector:Injector, - readonly I18n:I18nService, - readonly wpTableTimeline:WorkPackageViewTimelineService, - readonly wpTableColumns:WorkPackageViewColumnsService, - readonly $state:StateService, - ) { - } - public onSave() { this.wpTableTimeline.update({ ...this.wpTableTimeline.current, diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector.ts index e4effd2709ab..9fd532292973 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector.ts @@ -1,10 +1,4 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - Injector, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit, inject } from '@angular/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; @@ -24,6 +18,13 @@ import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class WpTableConfigurationRelationSelectorComponent implements OnInit { + readonly injector = inject(Injector); + readonly I18n = inject(I18nService); + readonly wpTableFilters = inject(WorkPackageViewFiltersService); + readonly ConfigurationService = inject(ConfigurationService); + readonly schemaCache = inject(SchemaCacheService); + readonly cdRef = inject(ChangeDetectorRef); + private relationFilterIds:string[] = [ 'parent', 'precedes', @@ -62,14 +63,6 @@ export class WpTableConfigurationRelationSelectorComponent implements OnInit { includes: this.I18n.t('js.relation_labels.partof'), }; - constructor(readonly injector:Injector, - readonly I18n:I18nService, - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly ConfigurationService:ConfigurationService, - readonly schemaCache:SchemaCacheService, - readonly cdRef:ChangeDetectorRef) { - } - ngOnInit() { void this.initializeRelationFilters(); } diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts index d6f4a718a943..63c7624979f8 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts @@ -1,19 +1,4 @@ -import { - ApplicationRef, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ComponentFactoryResolver, - ElementRef, - EventEmitter, - Inject, - InjectionToken, - Injector, - OnDestroy, - OnInit, - Optional, - ViewChild, -} from '@angular/core'; +import { ApplicationRef, ChangeDetectionStrategy, Component, ComponentFactoryResolver, ElementRef, EventEmitter, InjectionToken, Injector, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; import { WpTableConfigurationService } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service'; @@ -27,9 +12,7 @@ import { WorkPackageStatesInitializationService } from 'core-app/features/work-p import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { ComponentType } from '@angular/cdk/portal'; import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; @@ -48,6 +31,20 @@ export const WpTableConfigurationModalPrependToken = new InjectionToken | null>(WpTableConfigurationModalPrependToken, { optional: true }); + readonly I18n = inject(I18nService); + readonly injector = inject(Injector); + readonly appRef = inject(ApplicationRef); + readonly componentFactoryResolver = inject(ComponentFactoryResolver); + readonly loadingIndicator = inject(LoadingIndicatorService); + readonly querySpace = inject(IsolatedQuerySpace); + readonly wpStatesInitialization = inject(WorkPackageStatesInitializationService); + readonly apiV3Service = inject(ApiV3Service); + readonly notificationService = inject(WorkPackageNotificationService); + readonly wpTableColumns = inject(WorkPackageViewColumnsService); + readonly ConfigurationService = inject(ConfigurationService); + readonly $state = inject(StateService); + public text = { title: this.I18n.t('js.work_packages.table_configuration.modal_title'), closePopup: this.I18n.t('js.close_popup_title'), @@ -74,28 +71,7 @@ export class WpTableConfigurationModalComponent extends OpModalComponent impleme // Try to load an optional provided configuration service, and fall back to the default one private wpTableConfigurationService:WpTableConfigurationService = - this.injector.get(WpTableConfigurationService, new WpTableConfigurationService(this.I18n, this.$state)); - - constructor( - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - @Optional() @Inject(WpTableConfigurationModalPrependToken) public prependModalComponent:ComponentType|null, - readonly I18n:I18nService, - readonly injector:Injector, - readonly appRef:ApplicationRef, - readonly componentFactoryResolver:ComponentFactoryResolver, - readonly loadingIndicator:LoadingIndicatorService, - readonly querySpace:IsolatedQuerySpace, - readonly wpStatesInitialization:WorkPackageStatesInitializationService, - readonly apiV3Service:ApiV3Service, - readonly notificationService:WorkPackageNotificationService, - readonly wpTableColumns:WorkPackageViewColumnsService, - readonly cdRef:ChangeDetectorRef, - readonly ConfigurationService:ConfigurationService, - readonly elementRef:ElementRef, - readonly $state:StateService, - ) { - super(locals, cdRef, elementRef); - } + this.injector.get(WpTableConfigurationService); ngOnInit() { this.element = this.elementRef.nativeElement as HTMLElement; diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service.ts index 141691f9d665..b400562b93ba 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WpTableConfigurationDisplaySettingsTabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component'; import { TabInterface } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; @@ -10,8 +10,11 @@ import { WpTableConfigurationHighlightingTabComponent } from 'core-app/features/ import { OpBaselineComponent } from 'core-app/features/work-packages/components/wp-baseline/baseline/baseline.component'; import { StateService } from '@uirouter/angular'; -@Injectable() +@Injectable({ providedIn: 'root' }) export class WpTableConfigurationService { + readonly I18n = inject(I18nService); + readonly $state = inject(StateService); + protected _tabs:TabInterface[] = [ { id: 'columns', @@ -45,12 +48,6 @@ export class WpTableConfigurationService { }, ]; - constructor( - readonly I18n:I18nService, - readonly $state:StateService, - ) { - } - public get tabs() { if (this.$state.current.name?.includes('work-packages') || this.$state.current.name?.includes('bim')) { return this._tabs; diff --git a/frontend/src/app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service.ts b/frontend/src/app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service.ts index a5904b734791..6a186f1b5818 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service.ts @@ -27,7 +27,7 @@ //++ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { HalLink } from 'core-app/features/hal/hal-link/hal-link'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; import { HookService } from 'core-app/features/plugins/hook-service'; @@ -47,6 +47,13 @@ export interface WorkPackageAction { @Injectable() export class WorkPackageContextMenuHelperService { + private HookService = inject(HookService); + private UrlParamsHelper = inject(UrlParamsHelperService); + private wpViewRepresentation = inject(WorkPackageViewDisplayRepresentationService); + private wpViewTimeline = inject(WorkPackageViewTimelineService); + private wpViewIndent = inject(WorkPackageViewHierarchyIdentationService); + private PathHelper = inject(PathHelperService); + private BULK_ACTIONS = [ { text: I18n.t('js.work_packages.bulk_actions.edit'), @@ -74,14 +81,6 @@ export class WorkPackageContextMenuHelperService { }, ]; - constructor(private HookService:HookService, - private UrlParamsHelper:UrlParamsHelperService, - private wpViewRepresentation:WorkPackageViewDisplayRepresentationService, - private wpViewTimeline:WorkPackageViewTimelineService, - private wpViewIndent:WorkPackageViewHierarchyIdentationService, - private PathHelper:PathHelperService) { - } - public getPermittedActionLinks(workPackage:WorkPackageResource, permittedActionConstants:any, allowSplitScreenActions:boolean):WorkPackageAction[] { const singularPermittedActions:any[] = []; diff --git a/frontend/src/app/features/work-packages/components/wp-table/embedded/embedded-tables-macro.component.ts b/frontend/src/app/features/work-packages/components/wp-table/embedded/embedded-tables-macro.component.ts index 04dffe12beb1..70ce4a6924d1 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/embedded/embedded-tables-macro.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/embedded/embedded-tables-macro.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ Ng1FieldControlsWrapper, -import { ChangeDetectionStrategy, Component, ElementRef, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, inject } from '@angular/core'; import { WorkPackageTableConfigurationObject, } from 'core-app/features/work-packages/components/wp-table/wp-table-configuration'; @@ -44,6 +44,8 @@ import { populateInputsFromDataset } from 'core-app/shared/components/dataset-in changeDetection: ChangeDetectionStrategy.Default, }) export class EmbeddedTablesMacroComponent { + readonly elementRef = inject(ElementRef); + @Input() public queryProps:object; public configuration:WorkPackageTableConfigurationObject = { @@ -52,9 +54,7 @@ export class EmbeddedTablesMacroComponent { contextMenuEnabled: false, }; - constructor( - readonly elementRef:ElementRef, - ) { + constructor() { populateInputsFromDataset(this); } } diff --git a/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table-entry.component.ts b/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table-entry.component.ts index 629b5d22e37d..67c633547abf 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table-entry.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table-entry.component.ts @@ -1,6 +1,4 @@ -import { - ChangeDetectionStrategy, Component, ElementRef, Input, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, inject } from '@angular/core'; import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; import { WorkPackageIsolatedQuerySpaceDirective, @@ -23,13 +21,15 @@ export const wpTableEntrySelector = 'wp-embedded-table-entry'; changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageEmbeddedTableEntryComponent { + readonly elementRef = inject(ElementRef); + @Input() public queryProps:unknown; @Input() public configuration:unknown; @Input() public initialLoadingIndicator = true; - constructor(readonly elementRef:ElementRef) { + constructor() { populateInputsFromDataset(this); } } diff --git a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service.ts b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service.ts index d9ab83de3841..bb731009f330 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service.ts @@ -1,6 +1,4 @@ -import { - ApplicationRef, ComponentFactoryResolver, Injectable, Injector, -} from '@angular/core'; +import { ApplicationRef, Injectable, Injector, inject } from '@angular/core'; import { ComponentPortal, DomPortalOutlet } from '@angular/cdk/portal'; import { TransitionService } from '@uirouter/core'; import { FocusHelperService } from 'core-app/shared/directives/focus/focus-helper'; @@ -14,19 +12,17 @@ export type Class = new(...args:any[]) => any; @Injectable() export class ExternalQueryConfigurationService { + readonly FocusHelper = inject(FocusHelperService); + private appRef = inject(ApplicationRef); + private $transitions = inject(TransitionService); + private injector = inject(Injector); + // Hold a reference to the DOM node we're using as a host private _portalHostElement:HTMLElement; // And a reference to the actual portal host interface on top of the element private _bodyPortalHost:DomPortalOutlet; - constructor( - readonly FocusHelper:FocusHelperService, - private appRef:ApplicationRef, - private $transitions:TransitionService, - private injector:Injector) { - } - /** * Create a portal host element to contain the table configuration components. */ diff --git a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/restricted-wp-table-configuration.service.ts b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/restricted-wp-table-configuration.service.ts index d1b91c01a0a4..777f6cee357e 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/restricted-wp-table-configuration.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/restricted-wp-table-configuration.service.ts @@ -1,20 +1,12 @@ -import { Inject, Injectable } from '@angular/core'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { Injectable, inject } from '@angular/core'; import { TabInterface } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { WpTableConfigurationService } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service'; import { QueryConfigurationLocals } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component'; import { OpQueryConfigurationLocalsToken } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.constants'; -import { StateService } from '@uirouter/angular'; @Injectable() export class RestrictedWpTableConfigurationService extends WpTableConfigurationService { - constructor( - @Inject(OpQueryConfigurationLocalsToken) readonly locals:QueryConfigurationLocals, - readonly I18n:I18nService, - readonly $state:StateService, - ) { - super(I18n, $state); - } + readonly locals = inject(OpQueryConfigurationLocalsToken); public get tabs():TabInterface[] { const disabledTabs = this.locals.disabledTabs || {}; diff --git a/frontend/src/app/features/work-packages/components/wp-table/sort-header/sort-header.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/sort-header/sort-header.directive.ts index b18e3ba573bb..4dd50182efe2 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/sort-header/sort-header.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/sort-header/sort-header.directive.ts @@ -26,13 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Input, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { QueryColumn, queryColumnTypes, @@ -61,6 +55,15 @@ import { WorkPackageViewBaselineService } from 'core-app/features/work-packages/ }) // eslint-disable-next-line @angular-eslint/component-class-suffix export class SortHeaderDirective extends UntilDestroyedMixin implements AfterViewInit { + private wpTableHierarchies = inject(WorkPackageViewHierarchiesService); + private wpTableSortBy = inject(WorkPackageViewSortByService); + private wpTableGroupBy = inject(WorkPackageViewGroupByService); + private wpTableBaseline = inject(WorkPackageViewBaselineService); + private wpTableRelationColumns = inject(WorkPackageViewRelationColumnsService); + private elementRef = inject>(ElementRef); + private cdRef = inject(ChangeDetectorRef); + private I18n = inject(I18nService); + @Input() headerColumn:QueryColumn; @Input() locale:string; @@ -91,19 +94,6 @@ export class SortHeaderDirective extends UntilDestroyedMixin implements AfterVie private currentSortDirection:QuerySortByDirection|null; - constructor( - private wpTableHierarchies:WorkPackageViewHierarchiesService, - private wpTableSortBy:WorkPackageViewSortByService, - private wpTableGroupBy:WorkPackageViewGroupByService, - private wpTableBaseline:WorkPackageViewBaselineService, - private wpTableRelationColumns:WorkPackageViewRelationColumnsService, - private elementRef:ElementRef, - private cdRef:ChangeDetectorRef, - private I18n:I18nService, - ) { - super(); - } - ngAfterViewInit() { setTimeout(() => this.initialize()); } diff --git a/frontend/src/app/features/work-packages/components/wp-table/table-actions/table-actions.service.ts b/frontend/src/app/features/work-packages/components/wp-table/table-actions/table-actions.service.ts index 2c927c6bad69..9a2f1fa75a31 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/table-actions/table-actions.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/table-actions/table-actions.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { OpTableActionFactory, } from 'core-app/features/work-packages/components/wp-table/table-actions/table-action'; @@ -8,8 +8,8 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-packag @Injectable() export class OpTableActionsService { - constructor(private readonly injector:Injector) { - } + private readonly injector = inject(Injector); + /** * Actions currently registered diff --git a/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.ts b/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.ts index aaae51d4bca3..296f2c2ade6b 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.ts @@ -26,16 +26,9 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - OnDestroy, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { combineLatest } from 'rxjs'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageViewPaginationService, } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service'; @@ -48,7 +41,6 @@ import { import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; import { TablePaginationComponent } from 'core-app/shared/components/table-pagination/table-pagination.component'; -import { PaginationService } from 'core-app/shared/components/table-pagination/pagination-service'; @Component({ templateUrl: '../../../../../shared/components/table-pagination/table-pagination.component.html', @@ -57,16 +49,9 @@ import { PaginationService } from 'core-app/shared/components/table-pagination/p standalone: false, }) export class WorkPackageTablePaginationComponent extends TablePaginationComponent implements OnInit, OnDestroy { - constructor( - protected paginationService:PaginationService, - protected cdRef:ChangeDetectorRef, - protected wpTablePagination:WorkPackageViewPaginationService, - readonly querySpace:IsolatedQuerySpace, - readonly wpTableSortBy:WorkPackageViewSortByService, - readonly I18n:I18nService, - ) { - super(paginationService, cdRef, I18n); - } + protected wpTablePagination = inject(WorkPackageViewPaginationService); + readonly querySpace = inject(IsolatedQuerySpace); + readonly wpTableSortBy = inject(WorkPackageViewSortByService); ngOnInit() { super.ngOnInit(); @@ -110,7 +95,7 @@ export class WorkPackageTablePaginationComponent extends TablePaginationComponen public paginationInfoText(work_packages:WorkPackageCollectionResource) { if (this.isManualSortingMode && (work_packages.count < work_packages.total)) { - return I18n.t('js.work_packages.limited_results', + return this.I18n.t('js.work_packages.limited_results', { count: work_packages.count }); } return undefined; diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive.ts index 9e4e34a7982e..7025c3852e97 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive.ts @@ -26,13 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - Component, - ElementRef, - Injector, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Injector, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { IToast, @@ -101,6 +95,22 @@ import { IDay } from 'core-app/core/state/days/day.model'; changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageTimelineTableController extends UntilDestroyedMixin implements AfterViewInit { + readonly injector = inject(Injector); + private elementRef = inject(ElementRef); + private states = inject(States); + wpTableComponent = inject(WorkPackagesTableComponent); + private toastService = inject(ToastService); + private wpTableTimeline = inject(WorkPackageViewTimelineService); + private notificationService = inject(WorkPackageNotificationService); + private wpRelations = inject(WorkPackageRelationsService); + private wpTableHierarchies = inject(WorkPackageViewHierarchiesService); + private halEvents = inject(HalEventsService); + private querySpace = inject(IsolatedQuerySpace); + readonly I18n = inject(I18nService); + private workPackageViewCollapsedGroupsService = inject(WorkPackageViewCollapsedGroupsService); + private weekdaysService = inject(WeekdayService); + private daysService = inject(DayResourceService); + private element:HTMLElement; public workPackageTable:WorkPackageTable; @@ -149,26 +159,6 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl return workPackagesWithGroupHeaderCell; } - constructor( - public readonly injector:Injector, - private elementRef:ElementRef, - private states:States, - public wpTableComponent:WorkPackagesTableComponent, - private toastService:ToastService, - private wpTableTimeline:WorkPackageViewTimelineService, - private notificationService:WorkPackageNotificationService, - private wpRelations:WorkPackageRelationsService, - private wpTableHierarchies:WorkPackageViewHierarchiesService, - private halEvents:HalEventsService, - private querySpace:IsolatedQuerySpace, - readonly I18n:I18nService, - private workPackageViewCollapsedGroupsService:WorkPackageViewCollapsedGroupsService, - private weekdaysService:WeekdayService, - private daysService:DayResourceService, - ) { - super(); - } - ngAfterViewInit() { this.element = this.elementRef.nativeElement; diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts index ca8fd1e59484..ea8b916d9189 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts @@ -26,9 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, Component, ElementRef, Injector, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Injector, OnInit, inject } from '@angular/core'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { State } from '@openproject/reactivestates'; import { combineLatest } from 'rxjs'; @@ -87,21 +85,19 @@ function newSegment(vp:TimelineViewParameters, changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageTableTimelineRelations extends UntilDestroyedMixin implements OnInit { + readonly injector = inject(Injector); + elementRef = inject(ElementRef); + states = inject(States); + workPackageTimelineTableController = inject(WorkPackageTimelineTableController); + wpTableTimeline = inject(WorkPackageViewTimelineService); + wpRelations = inject(WorkPackageRelationsService); + @InjectField() querySpace:IsolatedQuerySpace; private container:HTMLElement; private workPackagesWithRelations:Record = {}; - constructor(public readonly injector:Injector, - public elementRef:ElementRef, - public states:States, - public workPackageTimelineTableController:WorkPackageTimelineTableController, - public wpTableTimeline:WorkPackageViewTimelineService, - public wpRelations:WorkPackageRelationsService) { - super(); - } - ngOnInit() { const element = this.elementRef.nativeElement; this.container = element.querySelector('.wp-table-timeline--relations'); diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts index 1d384759303a..90f77856db57 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts @@ -25,12 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - ElementRef, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, OnInit, inject } from '@angular/core'; import { States } from 'core-app/core/states/states.service'; import { WorkPackageTimelineTableController } from '../container/wp-timeline-container.directive'; import { calculatePositionValueForDayCountingPx, TimelineViewParameters } from '../wp-timeline'; @@ -50,15 +45,18 @@ import { TodayLineElement } from './wp-timeline.today-line'; changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageTableTimelineStaticElements implements OnInit { + states = inject(States); + workPackageTimelineTableController = inject(WorkPackageTimelineTableController); + public element:HTMLElement; private container:HTMLElement; private elements:TimelineStaticElement[]; - constructor(elementRef:ElementRef, - public states:States, - public workPackageTimelineTableController:WorkPackageTimelineTableController) { + constructor() { + const elementRef = inject(ElementRef); + this.element = elementRef.nativeElement; this.elements = [ diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts index 44aa2768c37b..abd945c1726b 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts @@ -25,12 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - Component, - ElementRef, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, inject } from '@angular/core'; import moment, { Moment } from 'moment'; import { TimelineZoomLevel } from 'core-app/features/hal/resources/query-resource'; import { WorkPackageTimelineTableController } from '../container/wp-timeline-container.directive'; @@ -53,16 +48,14 @@ import { WeekdayService } from 'core-app/core/days/weekday.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageTableTimelineGrid implements AfterViewInit { + private elementRef = inject(ElementRef); + wpTimeline = inject(WorkPackageTimelineTableController); + private weekdaysService = inject(WeekdayService); + private activeZoomLevel:TimelineZoomLevel; private gridContainer:HTMLElement; - constructor( - private elementRef:ElementRef, - public wpTimeline:WorkPackageTimelineTableController, - private weekdaysService:WeekdayService, - ) {} - ngAfterViewInit():void { const element = this.elementRef.nativeElement; this.gridContainer = element.querySelector('.wp-table-timeline--grid'); diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/header/wp-timeline-header.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/header/wp-timeline-header.directive.ts index c92bb105c57c..605e7016b294 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/header/wp-timeline-header.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/header/wp-timeline-header.directive.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, ElementRef, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, OnInit, inject } from '@angular/core'; import { WorkPackageTimelineTableController } from 'core-app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive'; import moment, { Moment } from 'moment'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -50,16 +50,19 @@ import { changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageTimelineHeaderController implements OnInit { + readonly I18n = inject(I18nService); + readonly wpTimelineService = inject(WorkPackageViewTimelineService); + readonly workPackageTimelineTableController = inject(WorkPackageTimelineTableController); + public element:HTMLElement; private activeZoomLevel:TimelineZoomLevel; private innerHeader:HTMLElement; - constructor(elementRef:ElementRef, - readonly I18n:I18nService, - readonly wpTimelineService:WorkPackageViewTimelineService, - readonly workPackageTimelineTableController:WorkPackageTimelineTableController) { + constructor() { + const elementRef = inject(ElementRef); + this.element = elementRef.nativeElement; } diff --git a/frontend/src/app/features/work-packages/components/wp-table/wp-table-sums-row/wp-table-sums-row.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/wp-table-sums-row/wp-table-sums-row.directive.ts index 0097787240ac..eedf335a2837 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/wp-table-sums-row/wp-table-sums-row.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/wp-table-sums-row/wp-table-sums-row.directive.ts @@ -26,9 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, Directive, ElementRef, Injector, Input, -} from '@angular/core'; +import { AfterViewInit, Directive, ElementRef, Injector, Input, inject } from '@angular/core'; import { takeUntil } from 'rxjs/operators'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { States } from 'core-app/core/states/states.service'; @@ -51,6 +49,15 @@ import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/w standalone: false, }) export class WorkPackageTableSumsRowController implements AfterViewInit { + readonly injector = inject(Injector); + readonly elementRef = inject(ElementRef); + readonly querySpace = inject(IsolatedQuerySpace); + readonly states = inject(States); + readonly schemaCache = inject(SchemaCacheService); + readonly wpTableColumns = inject(WorkPackageViewColumnsService); + readonly wpTableSums = inject(WorkPackageViewSumService); + readonly I18n = inject(I18nService); + @Input('wpTableSumsRow-table') workPackageTable:WorkPackageTable; public isHidden = true; @@ -61,14 +68,9 @@ export class WorkPackageTableSumsRowController implements AfterViewInit { private groupSumsBuilder:GroupSumsBuilder; - constructor(readonly injector:Injector, - readonly elementRef:ElementRef, - readonly querySpace:IsolatedQuerySpace, - readonly states:States, - readonly schemaCache:SchemaCacheService, - readonly wpTableColumns:WorkPackageViewColumnsService, - readonly wpTableSums:WorkPackageViewSumService, - readonly I18n:I18nService) { + constructor() { + const I18n = this.I18n; + this.text = { sum: I18n.t('js.label_total_sum'), }; diff --git a/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.ts b/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.ts index 311f9a114437..06a14c344786 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.ts @@ -26,18 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - Injector, - Input, - OnInit, - Output, - ViewEncapsulation, OnDestroy, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Injector, Input, OnInit, Output, ViewEncapsulation, OnDestroy, inject } from '@angular/core'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { @@ -82,6 +71,19 @@ export interface WorkPackageFocusContext { standalone: false, }) export class WorkPackagesTableComponent extends UntilDestroyedMixin implements OnInit, TableEventComponent, OnDestroy { + readonly elementRef = inject(ElementRef); + readonly injector = inject(Injector); + readonly states = inject(States); + readonly querySpace = inject(IsolatedQuerySpace); + readonly I18n = inject(I18nService); + readonly cdRef = inject(ChangeDetectorRef); + readonly wpTableGroupBy = inject(WorkPackageViewGroupByService); + readonly wpTableTimeline = inject(WorkPackageViewTimelineService); + readonly wpTableColumns = inject(WorkPackageViewColumnsService); + readonly wpTableSortBy = inject(WorkPackageViewSortByService); + readonly wpTableSums = inject(WorkPackageViewSumService); + readonly wpTableBaseline = inject(WorkPackageViewBaselineService); + @Input() projectIdentifier:string; @Input('configuration') configurationObject:WorkPackageTableConfigurationObject; @@ -136,23 +138,6 @@ export class WorkPackagesTableComponent extends UntilDestroyedMixin implements O public sumVisible = false; - constructor( - readonly elementRef:ElementRef, - readonly injector:Injector, - readonly states:States, - readonly querySpace:IsolatedQuerySpace, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - readonly wpTableGroupBy:WorkPackageViewGroupByService, - readonly wpTableTimeline:WorkPackageViewTimelineService, - readonly wpTableColumns:WorkPackageViewColumnsService, - readonly wpTableSortBy:WorkPackageViewSortByService, - readonly wpTableSums:WorkPackageViewSumService, - readonly wpTableBaseline:WorkPackageViewBaselineService, - ) { - super(); - } - ngOnInit():void { this.configuration = new WorkPackageTableConfiguration(this.configurationObject); this.element = this.elementRef.nativeElement; diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component.ts b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component.ts index ef1d857e15d9..ad5990065dfe 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component.ts @@ -27,12 +27,7 @@ //++ import { UIRouterGlobals } from '@uirouter/core'; -import { - ChangeDetectionStrategy, - Component, - Input, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { Observable } from 'rxjs'; @@ -51,6 +46,11 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-packag changeDetection: ChangeDetectionStrategy.Default, }) export class WpTabWrapperComponent implements OnInit { + readonly I18n = inject(I18nService); + readonly uiRouterGlobals = inject(UIRouterGlobals); + readonly apiV3Service = inject(ApiV3Service); + readonly wpTabsService = inject(WorkPackageTabsService); + @Input() public workPackageId:string; @Input() public tabIdentifier:string; @@ -61,13 +61,6 @@ export class WpTabWrapperComponent implements OnInit { tab:WpTabDefinition | undefined; }>; - constructor( - readonly I18n:I18nService, - readonly uiRouterGlobals:UIRouterGlobals, - readonly apiV3Service:ApiV3Service, - readonly wpTabsService:WorkPackageTabsService - ) {} - ngOnInit() { if (this.workPackageId === undefined) { this.workPackageId = this.uiRouterGlobals.params.workPackageId; diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.ts b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.ts index 545dc98fa503..ab6f6effc39a 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Injector, Input, OnInit, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Injector, Input, OnInit, Output, inject } from '@angular/core'; import { KeepTabService, } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; @@ -21,6 +21,15 @@ import { WpTabDefinition } from 'core-app/features/work-packages/components/wp-t standalone: false, }) export class WpTabsComponent implements OnInit { + readonly wpTabsService = inject(WorkPackageTabsService); + readonly I18n = inject(I18nService); + readonly injector = inject(Injector); + readonly $state = inject(StateService); + readonly uiRouterGlobals = inject(UIRouterGlobals); + readonly keepTab = inject(KeepTabService); + readonly pathHelper = inject(PathHelperService); + readonly currentProject = inject(CurrentProjectService); + @Input() workPackage:WorkPackageResource; @Input() view:'full'|'split'; @@ -42,18 +51,6 @@ export class WpTabsComponent implements OnInit { }, }; - constructor( - readonly wpTabsService:WorkPackageTabsService, - readonly I18n:I18nService, - readonly injector:Injector, - readonly $state:StateService, - readonly uiRouterGlobals:UIRouterGlobals, - readonly keepTab:KeepTabService, - readonly pathHelper:PathHelperService, - readonly currentProject:CurrentProjectService, - ) { - } - ngOnInit():void { this.canViewWatchers = !!(this.workPackage && this.workPackage.watchers); this.tabs = this.getDisplayableTabs(); diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts index cce4c6d6c805..2890616f578e 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { from } from 'rxjs'; import { StateService } from '@uirouter/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @@ -64,13 +64,13 @@ import { providedIn: 'root', }) export class WorkPackageTabsService { + private $state = inject(StateService); + private I18n = inject(I18nService); + private injector = inject(Injector); + private registeredTabs:WpTabDefinition[]; - constructor( - private $state:StateService, - private I18n:I18nService, - private injector:Injector, - ) { + constructor() { this.registeredTabs = this.buildDefaultTabs(); } diff --git a/frontend/src/app/features/work-packages/components/wp-timer-button/wp-timer-button.component.ts b/frontend/src/app/features/work-packages/components/wp-timer-button/wp-timer-button.component.ts index ee7ff7d2739b..31a36a55692f 100644 --- a/frontend/src/app/features/work-packages/components/wp-timer-button/wp-timer-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-timer-button/wp-timer-button.component.ts @@ -27,7 +27,7 @@ //++ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, Input } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; @@ -54,6 +54,16 @@ import { TurboRequestsService } from 'core-app/core/turbo/turbo-requests.service standalone: false, }) export class WorkPackageTimerButtonComponent extends UntilDestroyedMixin { + readonly injector = inject(Injector); + readonly I18n = inject(I18nService); + readonly apiV3Service = inject(ApiV3Service); + readonly timeEntryService = inject(TimeEntryTimerService); + readonly halEditing = inject(HalResourceEditingService); + readonly schemaCache = inject(SchemaCacheService); + readonly timezoneService = inject(TimezoneService); + readonly toastService = inject(ToastService); + readonly cdRef = inject(ChangeDetectorRef); + @Input() public workPackage:WorkPackageResource; @InjectField() PathHelper:PathHelperService; @InjectField() TurboRequests:TurboRequestsService; @@ -75,21 +85,6 @@ export class WorkPackageTimerButtonComponent extends UntilDestroyedMixin { }; - constructor( - readonly injector:Injector, - readonly I18n:I18nService, - readonly apiV3Service:ApiV3Service, - readonly timeEntryService:TimeEntryTimerService, - readonly halEditing:HalResourceEditingService, - readonly schemaCache:SchemaCacheService, - readonly timezoneService:TimezoneService, - readonly toastService:ToastService, - readonly cdRef:ChangeDetectorRef, - ) { - super(); - } - - activeForWorkPackage(entry:TimeEntryResource | null):boolean { return !!entry && entry.entity.href === this.workPackage.href; } diff --git a/frontend/src/app/features/work-packages/components/wp-watcher-button/wp-watcher-button.component.ts b/frontend/src/app/features/work-packages/components/wp-watcher-button/wp-watcher-button.component.ts index f94c58c47041..7eeaf9afe388 100644 --- a/frontend/src/app/features/work-packages/components/wp-watcher-button/wp-watcher-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-watcher-button/wp-watcher-button.component.ts @@ -27,9 +27,7 @@ //++ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageWatchersService } from 'core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watchers.service'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @@ -42,6 +40,11 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; standalone: false, }) export class WorkPackageWatcherButtonComponent extends UntilDestroyedMixin implements OnInit { + readonly I18n = inject(I18nService); + readonly wpWatchersService = inject(WorkPackageWatchersService); + readonly apiV3Service = inject(ApiV3Service); + readonly cdRef = inject(ChangeDetectorRef); + @Input() public workPackage:WorkPackageResource; @Input() public disabled = false; @@ -54,15 +57,6 @@ export class WorkPackageWatcherButtonComponent extends UntilDestroyedMixin imple public watched:boolean; - constructor( - readonly I18n:I18nService, - readonly wpWatchersService:WorkPackageWatchersService, - readonly apiV3Service:ApiV3Service, - readonly cdRef:ChangeDetectorRef, - ) { - super(); - } - ngOnInit() { this .apiV3Service diff --git a/frontend/src/app/features/work-packages/directives/query-space/wp-isolated-query-space.directive.ts b/frontend/src/app/features/work-packages/directives/query-space/wp-isolated-query-space.directive.ts index 11760dc0498d..b848b3db9ff8 100644 --- a/frontend/src/app/features/work-packages/directives/query-space/wp-isolated-query-space.directive.ts +++ b/frontend/src/app/features/work-packages/directives/query-space/wp-isolated-query-space.directive.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Directive, ElementRef } from '@angular/core'; +import { Directive, ElementRef, inject } from '@angular/core'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { OpTableActionsService, @@ -196,10 +196,11 @@ import { ], }) export class WorkPackageIsolatedQuerySpaceDirective { - constructor( - public querySpace:IsolatedQuerySpace, - elementRef:ElementRef, - ) { + querySpace = inject(IsolatedQuerySpace); + + constructor() { + const elementRef = inject(ElementRef); + debugLog('Opening isolated query space in %O', elementRef.nativeElement); } } diff --git a/frontend/src/app/features/work-packages/openproject-work-packages.module.ts b/frontend/src/app/features/work-packages/openproject-work-packages.module.ts index 19616116d94e..ab4663dbc5a6 100644 --- a/frontend/src/app/features/work-packages/openproject-work-packages.module.ts +++ b/frontend/src/app/features/work-packages/openproject-work-packages.module.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { CUSTOM_ELEMENTS_SCHEMA, Injector, NgModule } from '@angular/core'; +import { CUSTOM_ELEMENTS_SCHEMA, Injector, NgModule, inject } from '@angular/core'; import { OpSharedModule } from 'core-app/shared/shared.module'; import { OpenprojectFieldsModule } from 'core-app/shared/components/fields/openproject-fields.module'; import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; @@ -683,9 +683,13 @@ import { schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class OpenprojectWorkPackagesModule { + private injector = inject(Injector); + static bootstrapAttributeGroupsCalled = false; - constructor(private injector:Injector) { + constructor() { + const injector = this.injector; + OpenprojectWorkPackagesModule.bootstrapAttributeGroups(injector); } diff --git a/frontend/src/app/features/work-packages/routing/wp-full-copy/wp-full-copy-entry.component.ts b/frontend/src/app/features/work-packages/routing/wp-full-copy/wp-full-copy-entry.component.ts index 4e6f76a9af14..08f804e11869 100644 --- a/frontend/src/app/features/work-packages/routing/wp-full-copy/wp-full-copy-entry.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-full-copy/wp-full-copy-entry.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, ElementRef, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, inject } from '@angular/core'; import { WorkPackageIsolatedQuerySpaceDirective, } from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive'; @@ -48,13 +48,15 @@ import { populateInputsFromDataset } from 'core-app/shared/components/dataset-in changeDetection: ChangeDetectionStrategy.OnPush, }) export class WorkPackageFullCopyEntryComponent { + readonly elementRef = inject(ElementRef); + @Input() type:string; @Input() copiedFromWorkPackageId:string; @Input() parentId?:string; @Input() projectIdentifier?:string; @Input() routedFromAngular:boolean; - constructor(readonly elementRef:ElementRef) { + constructor() { populateInputsFromDataset(this); document.body.classList.add('router--work-packages-full-create'); diff --git a/frontend/src/app/features/work-packages/routing/wp-full-create/wp-full-create-entry.component.ts b/frontend/src/app/features/work-packages/routing/wp-full-create/wp-full-create-entry.component.ts index d5859bf0aab7..12a87e553874 100644 --- a/frontend/src/app/features/work-packages/routing/wp-full-create/wp-full-create-entry.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-full-create/wp-full-create-entry.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, ElementRef, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, inject } from '@angular/core'; import { WorkPackageIsolatedQuerySpaceDirective, } from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive'; @@ -48,12 +48,14 @@ import { populateInputsFromDataset } from 'core-app/shared/components/dataset-in changeDetection: ChangeDetectionStrategy.OnPush, }) export class WorkPackageFullCreateEntryComponent { + readonly elementRef = inject(ElementRef); + @Input() type:string; @Input() parentId?:string; @Input() projectIdentifier?:string; @Input() routedFromAngular:boolean; - constructor(readonly elementRef:ElementRef) { + constructor() { populateInputsFromDataset(this); document.body.classList.add('router--work-packages-full-create'); diff --git a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view-entry.component.ts b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view-entry.component.ts index 7c0b841a1fb8..ab6082b5c65c 100644 --- a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view-entry.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view-entry.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, ElementRef, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, inject } from '@angular/core'; import { WorkPackageIsolatedQuerySpaceDirective, } from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive'; @@ -49,11 +49,13 @@ import { populateInputsFromDataset } from 'core-app/shared/components/dataset-in changeDetection: ChangeDetectionStrategy.OnPush, }) export class WorkPackageFullViewEntryComponent { + readonly elementRef = inject(ElementRef); + @Input() workPackageId:string; @Input() activeTab:string; @Input() routedFromAngular:boolean; - constructor(readonly elementRef:ElementRef) { + constructor() { populateInputsFromDataset(this); document.body.classList.add('router--work-packages-full-view', 'router--work-packages-base'); diff --git a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts index 09be2b266120..2208437e33ca 100644 --- a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts @@ -26,15 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - HostListener, - Injector, - Input, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, OnInit, inject } from '@angular/core'; import { StateService } from '@uirouter/core'; import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; import { TabDefinition } from 'core-app/shared/components/tabs/tab.interface'; @@ -61,6 +53,12 @@ import { Observable, of } from 'rxjs'; standalone: false, }) export class WorkPackagesFullViewComponent extends WorkPackageSingleViewBase implements OnInit { + wpTableSelection = inject(WorkPackageViewSelectionService); + recentItemsService = inject(RecentItemsService); + readonly $state = inject(StateService); + readonly currentUserService = inject(CurrentUserService); + readonly cdRef = inject(ChangeDetectorRef); + // Watcher properties public isWatched:boolean; @@ -80,17 +78,6 @@ export class WorkPackagesFullViewComponent extends WorkPackageSingleViewBase imp }, }; - constructor( - public injector:Injector, - public wpTableSelection:WorkPackageViewSelectionService, - public recentItemsService:RecentItemsService, - readonly $state:StateService, - readonly currentUserService:CurrentUserService, - readonly cdRef:ChangeDetectorRef, - ) { - super(injector); - } - public onTabSelected(tab:TabDefinition):void { if (!this.routedFromAngular) { this.activeTab = tab.id; diff --git a/frontend/src/app/features/work-packages/routing/wp-split-create/wp-split-create-entry.component.ts b/frontend/src/app/features/work-packages/routing/wp-split-create/wp-split-create-entry.component.ts index 29da7e5f034c..9b755ece86cc 100644 --- a/frontend/src/app/features/work-packages/routing/wp-split-create/wp-split-create-entry.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-split-create/wp-split-create-entry.component.ts @@ -26,14 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - Component, - ElementRef, - Input, - OnDestroy, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, OnDestroy, inject } from '@angular/core'; import { WorkPackageIsolatedQuerySpaceDirective, } from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive'; @@ -57,10 +50,12 @@ import { populateInputsFromDataset } from 'core-app/shared/components/dataset-in changeDetection: ChangeDetectionStrategy.OnPush, }) export class WorkPackageSplitCreateEntryComponent implements AfterViewInit, OnDestroy { + readonly elementRef = inject(ElementRef); + @Input() projectIdentifier?:string; @Input() type?:string; - constructor(readonly elementRef:ElementRef) { + constructor() { populateInputsFromDataset(this); document.body.classList.add('router--work-packages-partitioned-split-view-new'); } diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts index 74bef2108614..050c49eb180c 100644 --- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, HostListener, Injector, Input, OnInit, Type } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostListener, Input, OnInit, Type, inject } from '@angular/core'; import { StateService } from '@uirouter/core'; import { WorkPackageViewFocusService, @@ -67,6 +67,17 @@ import { resolveRoutingId } from 'core-app/features/work-packages/helpers/work-p standalone: false, }) export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase implements OnInit { + states = inject(States); + firstRoute = inject(FirstRouteService); + keepTab = inject(KeepTabService); + wpTableSelection = inject(WorkPackageViewSelectionService); + wpTableFocus = inject(WorkPackageViewFocusService); + recentItemsService = inject(RecentItemsService); + readonly $state = inject(StateService); + readonly urlParams = inject(UrlParamsService); + readonly backRouting = inject(BackRoutingService); + readonly wpTabs = inject(WorkPackageTabsService); + hasState = !!this.$state.current; /** Reference to the base route e.g., work-packages.partitioned.list or bim.partitioned.split */ private baseRoute:string = this.$state.current?.data?.baseRoute as string; @@ -75,22 +86,6 @@ export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase imp @Input() resizerClass = 'work-packages-partitioned-page--content-right'; - constructor( - public injector:Injector, - public states:States, - public firstRoute:FirstRouteService, - public keepTab:KeepTabService, - public wpTableSelection:WorkPackageViewSelectionService, - public wpTableFocus:WorkPackageViewFocusService, - public recentItemsService:RecentItemsService, - readonly $state:StateService, - readonly urlParams:UrlParamsService, - readonly backRouting:BackRoutingService, - readonly wpTabs:WorkPackageTabsService, - ) { - super(injector); - } - // enable other parts of the application to trigger an immediate update // e.g. a stimulus controller // currently used by the new activities tab which does its own polling diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/state/wp-single-view.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/state/wp-single-view.service.ts index 593b302cdb33..a24d68eef1c0 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/state/wp-single-view.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/state/wp-single-view.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { WpSingleViewStore } from './wp-single-view.store'; import { filter, @@ -27,6 +27,10 @@ import { Query } from '@datorama/akita'; @EffectHandler @Injectable() export class WpSingleViewService { + readonly actions$ = inject(ActionsService); + readonly currentUser$ = inject(CurrentUserService); + private resourceService = inject(InAppNotificationsResourceService); + id = 'WorkPackage Activity Store'; protected store = new WpSingleViewStore(); @@ -64,13 +68,6 @@ export class WpSingleViewService { return { filters: this.query.getValue().notifications.filters }; } - constructor( - readonly actions$:ActionsService, - readonly currentUser$:CurrentUserService, - private resourceService:InAppNotificationsResourceService, - ) { - } - setFilters(workPackageId:string):void { const filters:ApiV3ListFilter[] = [ ['readIAN', '=', false], diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service.ts index da3fd3abe15b..477c775fbe71 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service.ts @@ -28,7 +28,7 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { RelationsStateValue, WorkPackageRelationsService, @@ -51,17 +51,15 @@ import { firstValueFrom } from 'rxjs'; @Injectable() export class WorkPackageViewAdditionalElementsService { - constructor( - readonly querySpace:IsolatedQuerySpace, - readonly wpTableHierarchies:WorkPackageViewHierarchiesService, - readonly wpTableColumns:WorkPackageViewColumnsService, - readonly notificationService:WorkPackageNotificationService, - readonly halResourceService:HalResourceService, - readonly apiV3Service:ApiV3Service, - readonly schemaCache:SchemaCacheService, - readonly wpRelations:WorkPackageRelationsService, - ) { - } + readonly querySpace = inject(IsolatedQuerySpace); + readonly wpTableHierarchies = inject(WorkPackageViewHierarchiesService); + readonly wpTableColumns = inject(WorkPackageViewColumnsService); + readonly notificationService = inject(WorkPackageNotificationService); + readonly halResourceService = inject(HalResourceService); + readonly apiV3Service = inject(ApiV3Service); + readonly schemaCache = inject(SchemaCacheService); + readonly wpRelations = inject(WorkPackageRelationsService); + public initialize(query:QueryResource, results:WorkPackageCollectionResource):void { const rows = results.elements; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service.ts index c63e9f50bc67..fe6bccffd7d5 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service.ts @@ -33,22 +33,20 @@ import { import { map, mapTo, take } from 'rxjs/operators'; import { merge, Observable } from 'rxjs'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { QuerySchemaResource } from 'core-app/features/hal/resources/query-schema-resource'; import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; @Injectable() export abstract class WorkPackageViewBaseService { + protected readonly querySpace = inject(IsolatedQuerySpace); + /** Internal state to push non-persisted updates */ protected updatesState = input(); /** Internal pristine state filled during +initialize+ only */ protected pristineState = input(); - constructor( - protected readonly querySpace:IsolatedQuerySpace, - ) { } - /** * Get the state value from the current query. * diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-baseline.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-baseline.service.ts index a0f42f7c84ee..64ba8d348cca 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-baseline.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-baseline.service.ts @@ -26,9 +26,8 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { States } from 'core-app/core/states/states.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { WorkPackageQueryStateService } from './wp-view-base.service'; @@ -66,17 +65,12 @@ export const BASELINE_INCOMPATIBLE_COLUMNS = [ @Injectable() export class WorkPackageViewBaselineService extends WorkPackageQueryStateService { - constructor( - protected readonly states:States, - protected readonly querySpace:IsolatedQuerySpace, - protected readonly pathHelper:PathHelperService, - protected readonly configurationService:ConfigurationService, - protected readonly timezoneService:TimezoneService, - protected readonly weekdaysService:WeekdayService, - protected readonly daysService:DayResourceService, - ) { - super(querySpace); - } + protected readonly states = inject(States); + protected readonly pathHelper = inject(PathHelperService); + protected readonly configurationService = inject(ConfigurationService); + protected readonly timezoneService = inject(TimezoneService); + protected readonly weekdaysService = inject(WeekdayService); + protected readonly daysService = inject(DayResourceService); public nonWorkingDays:IDay[] = []; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service.ts index 98db7f395c1b..e9c0457bb541 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service.ts @@ -27,9 +27,8 @@ //++ import { QueryResource } from 'core-app/features/hal/resources/query-resource'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { take } from 'rxjs/operators'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; @@ -40,7 +39,12 @@ import { WorkPackageViewBaseService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewCollapsedGroupsService extends WorkPackageViewBaseService { - readonly wpTypesToShowInCollapsedGroupHeaders:((wp:WorkPackageResource) => boolean)[]; + readonly workPackageViewGroupByService = inject(WorkPackageViewGroupByService); + private schemaCacheService = inject(SchemaCacheService); + + isMilestone = (workPackage:WorkPackageResource):boolean => this.schemaCacheService.of(workPackage)?.isMilestone as boolean; + + readonly wpTypesToShowInCollapsedGroupHeaders:((wp:WorkPackageResource) => boolean)[] = [this.isMilestone]; readonly groupTypesWithHeaderCellsWhenCollapsed = ['project']; @@ -64,15 +68,6 @@ export class WorkPackageViewCollapsedGroupsService extends WorkPackageViewBaseSe return this.workPackageViewGroupByService.current; } - constructor( - protected readonly querySpace:IsolatedQuerySpace, - readonly workPackageViewGroupByService:WorkPackageViewGroupByService, - private schemaCacheService:SchemaCacheService, - ) { - super(querySpace); - this.wpTypesToShowInCollapsedGroupHeaders = [this.isMilestone]; - } - // Every time the groupedBy changes, this services is initialized private getDefaultState():IGroupsCollapseEvent { return { @@ -84,8 +79,6 @@ export class WorkPackageViewCollapsedGroupsService extends WorkPackageViewBaseSe }; } - isMilestone = (workPackage:WorkPackageResource):boolean => this.schemaCacheService.of(workPackage)?.isMilestone; - toggleGroupCollapseState(groupIdentifier:string):void { const newCollapsedState = !this.config.state[groupIdentifier]; const state = { diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service.ts index f205112542ad..aa165a42cbaf 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service.ts @@ -27,9 +27,8 @@ //++ import { QueryResource } from 'core-app/features/hal/resources/query-resource'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { States } from 'core-app/core/states/states.service'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { QueryColumn, queryColumnTypes } from 'core-app/features/work-packages/components/wp-query/query-column'; import { combine } from '@openproject/reactivestates'; import { mapTo, take } from 'rxjs/operators'; @@ -39,9 +38,7 @@ import { sharedUserColumn } from 'core-app/features/work-packages/components/wp- @Injectable() export class WorkPackageViewColumnsService extends WorkPackageQueryStateService { - public constructor(readonly states:States, readonly querySpace:IsolatedQuerySpace) { - super(querySpace); - } + readonly states = inject(States); public valueFromQuery(query:QueryResource):QueryColumn[] { return [...query.columns]; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service.ts index c9a1462b16d5..122037c6e7ee 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service.ts @@ -28,8 +28,7 @@ import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { States } from 'core-app/core/states/states.service'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { WorkPackageQueryStateService } from './wp-view-base.service'; export const wpDisplayListRepresentation = 'list'; @@ -38,12 +37,7 @@ export type WorkPackageDisplayRepresentationValue = 'list'|'card'; @Injectable() export class WorkPackageViewDisplayRepresentationService extends WorkPackageQueryStateService { - public constructor( - readonly states:States, - readonly querySpace:IsolatedQuerySpace, - ) { - super(querySpace); - } + readonly states = inject(States); public hasChanged(query:QueryResource) { return this.current !== query.displayRepresentation; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service.ts index c30403fa7519..17e3ec620e5d 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service.ts @@ -26,8 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { Injectable, inject } from '@angular/core'; import { combine, input, InputState } from '@openproject/reactivestates'; import { States } from 'core-app/core/states/states.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @@ -43,6 +42,8 @@ import { map } from 'rxjs/operators'; @Injectable() export class WorkPackageViewFiltersService extends WorkPackageQueryStateService { + protected readonly states = inject(States); + public hidden:string[] = [ 'datesInterval', 'precedes', @@ -68,13 +69,6 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< /** Flag state to determine whether the filters are incomplete */ private incomplete = input(false); - constructor( - protected readonly states:States, - readonly querySpace:IsolatedQuerySpace, - ) { - super(querySpace); - } - /** * Load all schemas for the current filters and fill respective states * @param query diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service.ts index ca688718c30d..e3ca9d317ce0 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service.ts @@ -26,8 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { distinctUntilChanged, map } from 'rxjs/operators'; import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; @@ -42,10 +41,7 @@ export interface WPFocusState { @Injectable() export class WorkPackageViewFocusService extends WorkPackageViewBaseService { - constructor(public querySpace:IsolatedQuerySpace, - public wpTableSelection:WorkPackageViewSelectionService) { - super(querySpace); - } + wpTableSelection = inject(WorkPackageViewSelectionService); public isFocused(workPackageId:string) { return this.focusedWorkPackage === workPackageId; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service.ts index 42bd70f975e3..f49b9e037dc5 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service.ts @@ -28,8 +28,7 @@ import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { States } from 'core-app/core/states/states.service'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { QueryGroupByResource } from 'core-app/features/hal/resources/query-group-by-resource'; @@ -37,10 +36,7 @@ import { WorkPackageQueryStateService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewGroupByService extends WorkPackageQueryStateService { - public constructor(readonly states:States, - readonly querySpace:IsolatedQuerySpace) { - super(querySpace); - } + readonly states = inject(States); valueFromQuery(query:QueryResource) { return query.groupBy || null; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.ts index 4a77fb032785..9ce2a7744188 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; @@ -10,13 +10,13 @@ import { firstValueFrom } from 'rxjs'; @Injectable() export class WorkPackageViewHierarchyIdentationService { - constructor(private wpViewHierarchies:WorkPackageViewHierarchiesService, - private wpDisplayRepresentation:WorkPackageViewDisplayRepresentationService, - private states:States, - private wpRelationHierarchy:WorkPackageRelationsHierarchyService, - private apiV3Service:ApiV3Service, - private querySpace:IsolatedQuerySpace) { - } + private wpViewHierarchies = inject(WorkPackageViewHierarchiesService); + private wpDisplayRepresentation = inject(WorkPackageViewDisplayRepresentationService); + private states = inject(States); + private wpRelationHierarchy = inject(WorkPackageRelationsHierarchyService); + private apiV3Service = inject(ApiV3Service); + private querySpace = inject(IsolatedQuerySpace); + /** * Return whether the current hierarchy mode is active diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service.ts index 301c3a38667a..d47077dc9b8a 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service.ts @@ -1,6 +1,5 @@ import { QueryResource } from 'core-app/features/hal/resources/query-resource'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { States } from 'core-app/core/states/states.service'; import { BannersService } from 'core-app/core/enterprise/banners.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @@ -13,13 +12,8 @@ import { WorkPackageQueryStateService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewHighlightingService extends WorkPackageQueryStateService { - public constructor( - readonly states:States, - readonly Banners:BannersService, - readonly querySpace:IsolatedQuerySpace, - ) { - super(querySpace); - } + readonly states = inject(States); + readonly Banners = inject(BannersService); initialize(query:QueryResource, results:WorkPackageCollectionResource, schema?:QuerySchemaResource) { super.initialize(query, results, schema); diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-include-subprojects.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-include-subprojects.service.ts index 5767c5aea94a..453a2c72c690 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-include-subprojects.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-include-subprojects.service.ts @@ -28,18 +28,12 @@ import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { States } from 'core-app/core/states/states.service'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { WorkPackageQueryStateService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewIncludeSubprojectsService extends WorkPackageQueryStateService { - public constructor( - readonly states:States, - readonly querySpace:IsolatedQuerySpace, - ) { - super(querySpace); - } + readonly states = inject(States); public hasChanged(query:QueryResource):boolean { return this.current !== query.includeSubprojects; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service.ts index 96777b637d12..8a8b2ece7571 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service.ts @@ -26,11 +26,10 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { take } from 'rxjs/operators'; import { InputState } from '@openproject/reactivestates'; import { States } from 'core-app/core/states/states.service'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; @@ -47,16 +46,11 @@ import { firstValueFrom } from 'rxjs'; @Injectable() export class WorkPackageViewOrderService extends WorkPackageQueryStateService { - constructor( - protected readonly querySpace:IsolatedQuerySpace, - protected readonly apiV3Service:ApiV3Service, - protected readonly states:States, - protected readonly causedUpdates:CausedUpdatesService, - protected readonly wpTableSortBy:WorkPackageViewSortByService, - protected readonly pathHelper:PathHelperService, -) { - super(querySpace); - } + protected readonly apiV3Service = inject(ApiV3Service); + protected readonly states = inject(States); + protected readonly causedUpdates = inject(CausedUpdatesService); + protected readonly wpTableSortBy = inject(WorkPackageViewSortByService); + protected readonly pathHelper = inject(PathHelperService); public initialize(query:QueryResource, results:WorkPackageCollectionResource, schema?:QuerySchemaResource):Promise { // Take over our current value if the query is not saved diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service.ts index aef7acd9534a..d493ef910c2e 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service.ts @@ -26,10 +26,9 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { WorkPackageViewPagination } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-pagination'; import { WorkPackageViewBaseService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service'; import { PaginationObject, PaginationService } from 'core-app/shared/components/table-pagination/pagination-service'; @@ -43,10 +42,7 @@ export interface PaginationUpdateObject { @Injectable() export class WorkPackageViewPaginationService extends WorkPackageViewBaseService { - public constructor(querySpace:IsolatedQuerySpace, - readonly paginationService:PaginationService) { - super(querySpace); - } + readonly paginationService = inject(PaginationService); public get paginationObject():PaginationObject { if (this.current) { diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service.ts index fe2ae04ef816..474b97f65055 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service.ts @@ -29,9 +29,8 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { WorkPackageViewRelationColumns } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-relation-columns'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { RelationsStateValue, WorkPackageRelationsService } from 'core-app/features/work-packages/components/wp-relations/wp-relations.service'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { QueryColumn, queryColumnTypes, @@ -48,15 +47,10 @@ export type RelationColumnType = 'toType'|'ofType'|'children'; @Injectable() export class WorkPackageViewRelationColumnsService extends WorkPackageViewBaseService { - constructor( -public querySpace:IsolatedQuerySpace, - public wpTableColumns:WorkPackageViewColumnsService, - public halResourceService:HalResourceService, - public apiV3Service:ApiV3Service, - public wpRelations:WorkPackageRelationsService, -) { - super(querySpace); - } + wpTableColumns = inject(WorkPackageViewColumnsService); + halResourceService = inject(HalResourceService); + apiV3Service = inject(ApiV3Service); + wpRelations = inject(WorkPackageRelationsService); public valueFromQuery(_query:QueryResource):WorkPackageViewRelationColumns { // Take over current expanded values diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service.ts index ca5bef4548e7..8c5da761bd9e 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service.ts @@ -1,5 +1,4 @@ -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { States } from 'core-app/core/states/states.service'; import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; @@ -18,10 +17,12 @@ export interface WorkPackageViewSelectionState { @Injectable() export class WorkPackageViewSelectionService extends WorkPackageViewBaseService implements OnDestroy { - public constructor(readonly querySpace:IsolatedQuerySpace, - readonly states:States, - readonly opContextMenu:OPContextMenuService) { - super(querySpace); + readonly states = inject(States); + readonly opContextMenu = inject(OPContextMenuService); + + public constructor() { + super(); + this.reset(); } diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service.ts index c74075631f6a..74c46b4e9069 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service.ts @@ -28,10 +28,9 @@ import { combine } from '@openproject/reactivestates'; import { mapTo } from 'rxjs/operators'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { States } from 'core-app/core/states/states.service'; import { QuerySortByResource } from 'core-app/features/hal/resources/query-sort-by-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -40,11 +39,8 @@ import { WorkPackageQueryStateService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewSortByService extends WorkPackageQueryStateService { - constructor(protected readonly states:States, - protected readonly querySpace:IsolatedQuerySpace, - protected readonly pathHelper:PathHelperService) { - super(querySpace); - } + protected readonly states = inject(States); + protected readonly pathHelper = inject(PathHelperService); public valueFromQuery(query:QueryResource) { return [...query.sortBy]; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service.ts index 94df241c0a9b..8316c88907ff 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service.ts @@ -27,15 +27,11 @@ //++ import { QueryResource } from 'core-app/features/hal/resources/query-resource'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { Injectable } from '@angular/core'; import { WorkPackageQueryStateService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewSumService extends WorkPackageQueryStateService { - public constructor(querySpace:IsolatedQuerySpace) { - super(querySpace); - } public valueFromQuery(query:QueryResource) { return !!query.sums; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service.ts index eedb10432646..a9ee99762a57 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service.ts @@ -28,7 +28,6 @@ import { Injectable } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { input } from '@openproject/reactivestates'; import { WorkPackageTimelineState } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-timeline'; import { zoomLevelOrder } from 'core-app/features/work-packages/components/wp-table/timeline/wp-timeline'; @@ -40,10 +39,6 @@ export class WorkPackageViewTimelineService extends WorkPackageQueryStateService /** Remember the computed zoom level to correct zooming after leaving autozoom */ public appliedZoomLevel$ = input('auto'); - public constructor(protected readonly querySpace:IsolatedQuerySpace) { - super(querySpace); - } - public valueFromQuery(query:QueryResource) { return { ...this.defaultState, diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/work-package-single-view.base.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/work-package-single-view.base.ts index cd499bb647fb..87659920fbba 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/work-package-single-view.base.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/work-package-single-view.base.ts @@ -26,12 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectorRef, - Directive, - Injector, - Input, -} from '@angular/core'; +import { ChangeDetectorRef, Directive, Injector, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { @@ -71,6 +66,8 @@ import { StateService } from '@uirouter/angular'; @Directive() export abstract class WorkPackageSingleViewBase extends UntilDestroyedMixin { + injector = inject(Injector); + @Input() routedFromAngular = true; @Input() workPackageId:string; @@ -128,9 +125,7 @@ export abstract class WorkPackageSingleViewBase extends UntilDestroyedMixin { public displayNotificationsButton$:Observable; - constructor( - public injector:Injector, - ) { + constructor() { super(); if (this.routedFromAngular && this.workPackageId === undefined) { diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/work-packages-view.base.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/work-packages-view.base.ts index aeb8116befe7..9b45c0eb85ed 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/work-packages-view.base.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/work-packages-view.base.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectorRef, Directive, Injector, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Directive, Injector, OnDestroy, OnInit, inject } from '@angular/core'; import { StateService, TransitionService } from '@uirouter/core'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; @@ -94,6 +94,8 @@ import { tableRefreshRequest } from 'core-app/features/work-packages/routing/wp- @Directive() export abstract class WorkPackagesViewBase extends UntilDestroyedMixin implements OnInit, OnDestroy { + injector = inject(Injector); + @InjectField() $state:StateService; @InjectField() states:States; @@ -158,10 +160,6 @@ export abstract class WorkPackagesViewBase extends UntilDestroyedMixin implement /** Remember explicitly when this component was destroyed */ destroyed = false; - constructor(public injector:Injector) { - super(); - } - ngOnInit() { // Listen to changes on the query state objects this.setupQueryObservers(); diff --git a/frontend/src/app/features/work-packages/services/notifications/work-package-notification.service.ts b/frontend/src/app/features/work-packages/services/notifications/work-package-notification.service.ts index 7044fb5d3756..2483047d9d67 100644 --- a/frontend/src/app/features/work-packages/services/notifications/work-package-notification.service.ts +++ b/frontend/src/app/features/work-packages/services/notifications/work-package-notification.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { IToast } from 'core-app/shared/components/toaster/toast.service'; import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @@ -36,13 +36,8 @@ import { TurboRequestsService } from 'core-app/core/turbo/turbo-requests.service @Injectable() export class WorkPackageNotificationService extends HalResourceNotificationService { - constructor( - readonly injector:Injector, - readonly apiV3Service:ApiV3Service, - readonly turboRequests:TurboRequestsService, - ) { - super(injector); - } + readonly apiV3Service = inject(ApiV3Service); + readonly turboRequests = inject(TurboRequestsService); public showSave(resource:HalResource, isCreate = false) { const message:IToast = { diff --git a/frontend/src/app/features/work-packages/services/work-package.service.ts b/frontend/src/app/features/work-packages/services/work-package.service.ts index adf662113798..3d9d3f5741f8 100644 --- a/frontend/src/app/features/work-packages/services/work-package.service.ts +++ b/frontend/src/app/features/work-packages/services/work-package.service.ts @@ -39,21 +39,20 @@ import { resolveNumericId } from 'core-app/features/work-packages/helpers/work-p @Injectable() export class WorkPackageService { + private readonly http = inject(HttpClient); + private readonly $state = inject(StateService); + private readonly PathHelper = inject(PathHelperService); + private readonly UrlParamsHelper = inject(UrlParamsHelperService); + private readonly toastService = inject(ToastService); + private readonly I18n = inject(I18nService); + private readonly halEvents = inject(HalEventsService); + private text = { successful_delete: this.I18n.t('js.work_packages.message_successful_bulk_delete'), }; private readonly states = inject(States); - constructor(private readonly http:HttpClient, - private readonly $state:StateService, - private readonly PathHelper:PathHelperService, - private readonly UrlParamsHelper:UrlParamsHelperService, - private readonly toastService:ToastService, - private readonly I18n:I18nService, - private readonly halEvents:HalEventsService) { - } - public performBulkDelete(ids:string[], defaultHandling:boolean) { const params = { 'ids[]': ids, diff --git a/frontend/src/app/shared/components/attachments/attachment-list/attachment-list-item.component.ts b/frontend/src/app/shared/components/attachments/attachment-list/attachment-list-item.component.ts index 2c4f7d9334af..72b3719864dc 100644 --- a/frontend/src/app/shared/components/attachments/attachment-list/attachment-list-item.component.ts +++ b/frontend/src/app/shared/components/attachments/attachment-list/attachment-list-item.component.ts @@ -26,17 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - Component, - ElementRef, - EventEmitter, - Input, - OnInit, - Output, - ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild, inject } from '@angular/core'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { distinctUntilChanged } from 'rxjs/operators'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -60,6 +50,13 @@ import { IFileIcon } from 'core-app/shared/components/storages/icons.mapping'; standalone: false, }) export class OpAttachmentListItemComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit { + private readonly I18n = inject(I18nService); + private readonly pathHelper = inject(PathHelperService); + private readonly timezoneService = inject(TimezoneService); + private readonly confirmDialogService = inject(ConfirmDialogService); + private readonly principalsResourceService = inject(PrincipalsResourceService); + private readonly principalRendererService = inject(PrincipalRendererService); + @Input() public attachment:IAttachment; @Input() public index:number; @@ -94,17 +91,6 @@ export class OpAttachmentListItemComponent extends UntilDestroyedMixin implement private viewInitialized$ = new BehaviorSubject(false); - constructor( - private readonly I18n:I18nService, - private readonly pathHelper:PathHelperService, - private readonly timezoneService:TimezoneService, - private readonly confirmDialogService:ConfirmDialogService, - private readonly principalsResourceService:PrincipalsResourceService, - private readonly principalRendererService:PrincipalRendererService, - ) { - super(); - } - ngOnInit():void { this.fileIcon = getIconForMimeType(this.attachment.contentType); diff --git a/frontend/src/app/shared/components/attachments/attachment-list/attachment-list.component.ts b/frontend/src/app/shared/components/attachments/attachment-list/attachment-list.component.ts index 5763e73f5b6a..9765fbb77821 100644 --- a/frontend/src/app/shared/components/attachments/attachment-list/attachment-list.component.ts +++ b/frontend/src/app/shared/components/attachments/attachment-list/attachment-list.component.ts @@ -26,13 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - Input, - Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { IAttachment } from 'core-app/core/state/attachments/attachment.model'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { AttachmentsResourceService } from 'core-app/core/state/attachments/attachments.service'; @@ -44,6 +38,8 @@ import { AttachmentsResourceService } from 'core-app/core/state/attachments/atta standalone: false, }) export class OpAttachmentListComponent extends UntilDestroyedMixin { + private readonly attachmentsResourceService = inject(AttachmentsResourceService); + @Input() public attachments:IAttachment[] = []; @Input() public collectionKey:string; @@ -54,12 +50,6 @@ export class OpAttachmentListComponent extends UntilDestroyedMixin { @Output() public attachmentRemoved = new EventEmitter(); - constructor( - private readonly attachmentsResourceService:AttachmentsResourceService, - ) { - super(); - } - public removeAttachment(attachment:IAttachment):void { this.attachmentsResourceService.removeAttachment(this.collectionKey, attachment).subscribe(() => { this.attachmentRemoved.emit(); diff --git a/frontend/src/app/shared/components/attachments/attachments.component.ts b/frontend/src/app/shared/components/attachments/attachments.component.ts index 9aab8cadc473..037a4401a15a 100644 --- a/frontend/src/app/shared/components/attachments/attachments.component.ts +++ b/frontend/src/app/shared/components/attachments/attachments.component.ts @@ -26,20 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - HostBinding, - Input, - OnDestroy, - OnInit, - Output, - ViewChild, - ViewEncapsulation, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, Input, OnDestroy, OnInit, Output, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { fromEvent, Observable } from 'rxjs'; import { filter, map, tap } from 'rxjs/operators'; @@ -69,6 +56,16 @@ function containsFiles(dataTransfer:DataTransfer):boolean { standalone: false, }) export class OpAttachmentsComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { + elementRef = inject(ElementRef); + protected readonly I18n = inject(I18nService); + protected readonly states = inject(States); + protected readonly toastService = inject(ToastService); + private readonly uploadService = inject(OpUploadService); + protected readonly halResourceService = inject(HalResourceService); + protected readonly attachmentsResourceService = inject(AttachmentsResourceService); + protected readonly timezoneService = inject(TimezoneService); + protected readonly cdRef = inject(ChangeDetectorRef); + @HostBinding('attr.data-test-selector') public testSelector = 'op-attachments'; @HostBinding('class.op-file-section') public className = true; @@ -135,17 +132,7 @@ export class OpAttachmentsComponent extends UntilDestroyedMixin implements OnIni this.cdRef.detectChanges(); }; - constructor( - public elementRef:ElementRef, - protected readonly I18n:I18nService, - protected readonly states:States, - protected readonly toastService:ToastService, - private readonly uploadService:OpUploadService, - protected readonly halResourceService:HalResourceService, - protected readonly attachmentsResourceService:AttachmentsResourceService, - protected readonly timezoneService:TimezoneService, - protected readonly cdRef:ChangeDetectorRef, - ) { + constructor() { super(); populateInputsFromDataset(this); diff --git a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text-modal.service.ts b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text-modal.service.ts index 33771009941d..8f251654527f 100644 --- a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text-modal.service.ts +++ b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text-modal.service.ts @@ -26,18 +26,15 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { TurboRequestsService } from 'core-app/core/turbo/turbo-requests.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @Injectable({ providedIn: 'root' }) export class AttributeHelpTextModalService { - constructor( - protected pathHelper:PathHelperService, - protected turboRequests:TurboRequestsService, - ) { + protected pathHelper = inject(PathHelperService); + protected turboRequests = inject(TurboRequestsService); - } public show(helpTextId:string):Promise { return this.turboRequests.requestStream(this.helpTextModalUrl(helpTextId)); diff --git a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.component.ts b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.component.ts index 625bd116a17c..1170e3feac25 100644 --- a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.component.ts +++ b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.component.ts @@ -26,15 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Injector, - Input, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Injector, Input, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; import { AttributeHelpTextsService } from './attribute-help-text.service'; @@ -50,6 +42,13 @@ export const attributeHelpTextSelector = 'attribute-help-text'; standalone: false, }) export class AttributeHelpTextComponent implements OnInit { + readonly elementRef = inject(ElementRef); + protected attributeHelpTexts = inject(AttributeHelpTextsService); + protected attributeHelpTextModalService = inject(AttributeHelpTextModalService); + protected cdRef = inject(ChangeDetectorRef); + protected injector = inject(Injector); + protected I18n = inject(I18nService); + // Attribute to show help text for @Input() public attribute:string; @@ -69,14 +68,7 @@ export class AttributeHelpTextComponent implements OnInit { open_dialog: this.I18n.t('js.help_texts.show_modal'), }; - constructor( - readonly elementRef:ElementRef, - protected attributeHelpTexts:AttributeHelpTextsService, - protected attributeHelpTextModalService:AttributeHelpTextModalService, - protected cdRef:ChangeDetectorRef, - protected injector:Injector, - protected I18n:I18nService, - ) { + constructor() { populateInputsFromDataset(this); } diff --git a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.service.ts b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.service.ts index ea15bf05fc7f..31fb87c8e904 100644 --- a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.service.ts +++ b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.service.ts @@ -27,17 +27,16 @@ //++ import { input } from '@openproject/reactivestates'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { HelpTextResource } from 'core-app/features/hal/resources/help-text-resource'; import { firstValueFrom, map } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AttributeHelpTextsService { - private helpTexts = input(); + private apiV3Service = inject(ApiV3Service); - constructor(private apiV3Service:ApiV3Service) { - } + private helpTexts = input(); /** * Search for a given attribute help text diff --git a/frontend/src/app/shared/components/attribute-help-texts/static-attribute-help-text.component.ts b/frontend/src/app/shared/components/attribute-help-texts/static-attribute-help-text.component.ts index 2b538e87a00d..01f36fade433 100644 --- a/frontend/src/app/shared/components/attribute-help-texts/static-attribute-help-text.component.ts +++ b/frontend/src/app/shared/components/attribute-help-texts/static-attribute-help-text.component.ts @@ -26,15 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - HostBinding, - Injector, - Input, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostBinding, Injector, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; @@ -48,6 +40,12 @@ import { StaticAttributeHelpTextModalComponent } from './static-attribute-help-t standalone: false, }) export class StaticAttributeHelpTextComponent { + readonly elementRef = inject(ElementRef); + protected opModalService = inject(OpModalService); + protected cdRef = inject(ChangeDetectorRef); + protected injector = inject(Injector); + protected I18n = inject(I18nService); + // Attribute pass the modal title and content @Input() public title:string; @@ -59,13 +57,7 @@ export class StaticAttributeHelpTextComponent { open_dialog: this.I18n.t('js.help_texts.show_modal'), }; - constructor( - readonly elementRef:ElementRef, - protected opModalService:OpModalService, - protected cdRef:ChangeDetectorRef, - protected injector:Injector, - protected I18n:I18nService, - ) { + constructor() { populateInputsFromDataset(this); } diff --git a/frontend/src/app/shared/components/attribute-help-texts/static-attribute-help-text.modal.ts b/frontend/src/app/shared/components/attribute-help-texts/static-attribute-help-text.modal.ts index cf0b0862e490..6098d3e758ac 100644 --- a/frontend/src/app/shared/components/attribute-help-texts/static-attribute-help-text.modal.ts +++ b/frontend/src/app/shared/components/attribute-help-texts/static-attribute-help-text.modal.ts @@ -26,13 +26,9 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; @Component({ templateUrl: './static-attribute-help-text.modal.html', @@ -40,6 +36,8 @@ import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; standalone: false, }) export class StaticAttributeHelpTextModalComponent extends OpModalComponent implements OnInit { + readonly I18n = inject(I18nService); + readonly text = { close: this.I18n.t('js.button_close'), }; @@ -47,17 +45,4 @@ export class StaticAttributeHelpTextModalComponent extends OpModalComponent impl public title:string = this.locals.title as string; public content:string = this.locals.content as string; - - constructor( - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef, - ) { - super(locals, cdRef, elementRef); - } - - ngOnInit() { - super.ngOnInit(); - } } diff --git a/frontend/src/app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component.ts index 3fed492e2c7a..01ee86deb3af 100644 --- a/frontend/src/app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component.ts @@ -26,17 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - EventEmitter, - Injector, - Input, - Output, - ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Injector, Input, Output, ViewChild, inject } from '@angular/core'; import { NgSelectComponent } from '@ng-select/ng-select'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @@ -64,6 +54,8 @@ export interface CreateAutocompleterValueOption { standalone: false, }) export class CreateAutocompleterComponent extends UntilDestroyedMixin implements AfterViewInit { + readonly injector = inject(Injector); + @Input() public availableValues:CreateAutocompleterValueOption[]; @Input() public appendTo:string; @@ -118,7 +110,7 @@ export class CreateAutocompleterComponent extends UntilDestroyedMixin implements private _openDirectly = false; - constructor(readonly injector:Injector) { + constructor() { super(); this.text.add_new_action = this.I18n.t('js.label_create'); diff --git a/frontend/src/app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component.ts b/frontend/src/app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component.ts index 13f984ccd742..49299f0eb115 100644 --- a/frontend/src/app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component.ts +++ b/frontend/src/app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component.ts @@ -1,15 +1,4 @@ -import { - AfterViewInit, - ChangeDetectionStrategy, - Component, - ElementRef, - EventEmitter, - Input, - OnInit, - OnDestroy, - Output, - ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnInit, OnDestroy, Output, ViewChild, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { NgSelectComponent } from '@ng-select/ng-select'; import { DragulaService, Group } from 'ng2-dragula'; @@ -40,6 +29,11 @@ export interface DraggableOption { standalone: false, }) export class DraggableAutocompleteComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit, OnDestroy { + readonly I18n = inject(I18nService); + readonly elementRef = inject(ElementRef); + readonly dragula = inject(DragulaService); + readonly alternativeSearchService = inject(AlternativeSearchService); + /** Options to show in the autocompleter */ @Input() options:DraggableOption[]; @@ -98,15 +92,6 @@ export class DraggableAutocompleteComponent extends UntilDestroyedMixin implemen public appendTo = 'body'; - constructor( - readonly I18n:I18nService, - readonly elementRef:ElementRef, - readonly dragula:DragulaService, - readonly alternativeSearchService:AlternativeSearchService, - ) { - super(); - } - ngOnInit():void { populateInputsFromDataset(this); diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.ts index e54f12128c93..a3671506caab 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.ts @@ -1,27 +1,6 @@ /* We just forward the ng-select outputs without renaming */ /* eslint-disable @angular-eslint/no-output-native */ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ContentChild, - ElementRef, - EventEmitter, - forwardRef, - HostBinding, - Injector, - Input, - OnChanges, - OnInit, - Output, - SimpleChanges, - TemplateRef, - Type, - ViewChild, - ViewContainerRef, - ViewEncapsulation, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, forwardRef, HostBinding, Injector, Input, OnChanges, OnInit, Output, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, ViewEncapsulation, inject } from '@angular/core'; import { DropdownPosition, NgSelectComponent } from '@ng-select/ng-select'; import { BehaviorSubject, merge, NEVER, Observable, of, Subject } from 'rxjs'; import { debounceTime, distinctUntilChanged, filter, switchMap, tap } from 'rxjs/operators'; @@ -96,6 +75,16 @@ type GroupValueFn = (key:string | any, children:any[]) => string | any; export class OpAutocompleterComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit, OnChanges, ControlValueAccessor { + readonly injector = inject(Injector); + readonly elementRef = inject>(ElementRef); + readonly http = inject(HttpClient); + readonly apiV3Service = inject(ApiV3Service); + readonly cdRef = inject(ChangeDetectorRef); + readonly vcRef = inject(ViewContainerRef); + readonly I18n = inject(I18nService); + readonly halResourceService = inject(HalResourceService); + readonly pathHelperService = inject(PathHelperService); + @HostBinding('class.op-autocompleter') className = true; @Input() public filters?:IAPIFilter[] = []; @@ -297,21 +286,7 @@ export class OpAutocompleterComponent; - readonly opAutocompleterService = new OpAutocompleterService(this.apiV3Service, this.halResourceService); - - constructor( - readonly injector:Injector, - readonly elementRef:ElementRef, - readonly http:HttpClient, - readonly apiV3Service:ApiV3Service, - readonly cdRef:ChangeDetectorRef, - readonly vcRef:ViewContainerRef, - readonly I18n:I18nService, - readonly halResourceService:HalResourceService, - readonly pathHelperService:PathHelperService, - ) { - super(); - } + readonly opAutocompleterService = inject(OpAutocompleterService); ngOnInit() { populateInputsFromDataset(this); @@ -333,8 +308,7 @@ export class OpAutocompleterComponent !!(this.defaultData || this.url || this.getOptionsFn)), + filter(() => [this.defaultData, this.url, this.getOptionsFn].some(Boolean)), distinctUntilChanged(), tap(() => this.loading$.next(true)), debounceTime(this.debounceTimeForCurrentEnvironment), @@ -518,11 +492,9 @@ export class OpAutocompleterComponent { - }; + onChange = (_:T|T[]|null):void => undefined; - onTouched = (_:T|T[]|null):void => { - }; + onTouched = (_:T|T[]|null):void => undefined; registerOnChange(fn:(_:T|T[]|null) => void):void { this.onChange = fn; diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts index 5b2f00691c47..58ad73324e27 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts @@ -2,7 +2,7 @@ import type { Mock } from 'vitest'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { States } from 'core-app/core/states/states.service'; import { provideHttpClientTesting } from '@angular/common/http/testing'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ChangeDetectionStrategy, Component, NO_ERRORS_SCHEMA } from '@angular/core'; import { of, map } from 'rxjs'; import { NgSelectModule } from '@ng-select/ng-select'; @@ -11,6 +11,15 @@ import { TOpAutocompleterResource } from './typings'; import { By } from '@angular/platform-browser'; import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +@Component({ + selector: 'op-test-autocompleter', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false, +}) +class TestAutocompleterComponent extends OpAutocompleterComponent { +} + describe('autocompleter', () => { let fixture:ComponentFixture; let getOptionsFnSpy:Mock; @@ -86,7 +95,7 @@ describe('autocompleter', () => { fixture = TestBed.createComponent(OpAutocompleterComponent); getOptionsFnSpy = vi.fn().mockImplementation((searchTerm:string) => { return of(workPackagesStub).pipe(map((wps) => wps.filter((wp) => searchTerm !== '' && wp.subject.includes(searchTerm)))); - }); + }) as unknown as Mock; fixture.componentInstance.resource = 'work_packages' as TOpAutocompleterResource; fixture.componentInstance.filters = []; @@ -330,3 +339,18 @@ describe('autocompleter', () => { }); }); }); + +describe('derived autocompleter', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TestAutocompleterComponent], + providers: [States, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()], + }).compileComponents(); + }); + + it('provides shared autocompleter dependencies to subclasses', () => { + const fixture = TestBed.createComponent(TestAutocompleterComponent); + + expect(fixture.componentInstance.opAutocompleterService).toBeDefined(); + }); +}); diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/services/op-autocompleter.service.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/services/op-autocompleter.service.ts index ab94f9c5c13e..e530ea441c94 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/services/op-autocompleter.service.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/services/op-autocompleter.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; import { map } from 'rxjs/operators'; import { ApiV3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; @@ -20,15 +20,11 @@ import { addFiltersToPath } from 'core-app/core/apiv3/helpers/add-filters-to-pat import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; -@Injectable() - +@Injectable({ providedIn: 'root' }) export class OpAutocompleterService extends UntilDestroyedMixin { - constructor( - private apiV3Service:ApiV3Service, - private halResourceService:HalResourceService, - ) { - super(); - } + private apiV3Service = inject(ApiV3Service); + private halResourceService = inject(HalResourceService); + // A method for fetching data with different resource type and different filter public loadAvailable(matching:string, resource:TOpAutocompleterResource, filters?:IAPIFilter[], searchKey?:string):Observable { @@ -98,7 +94,7 @@ export class OpAutocompleterService extends UntilDestroyedMixin { switch (resource) { // in this case we can add more functions for fetching usual resources default: { - return this.loadAvailable(matching || '', resource, filters, searchKey); + return this.loadAvailable(matching ?? '', resource, filters, searchKey); } } } @@ -111,7 +107,7 @@ export class OpAutocompleterService extends UntilDestroyedMixin { return of([]); } - const finalFilters:ApiV3FilterBuilder = this.createFilters(filters ?? [], matching || '', searchKey); + const finalFilters:ApiV3FilterBuilder = this.createFilters(filters ?? [], matching ?? '', searchKey); const params = this.createParams(resource); const stringifiedBuiltOutUrl = addFiltersToPath(url, finalFilters, params).toString(); diff --git a/frontend/src/app/shared/components/autocompleter/project-phase-autocompleter/project-phase-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/project-phase-autocompleter/project-phase-autocompleter.component.ts index 7dcc39308342..40be5a99b6a9 100644 --- a/frontend/src/app/shared/components/autocompleter/project-phase-autocompleter/project-phase-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/project-phase-autocompleter/project-phase-autocompleter.component.ts @@ -28,7 +28,7 @@ * ++ */ -import { ChangeDetectionStrategy, Component, Injector } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { CreateAutocompleterComponent } from '../create-autocompleter/create-autocompleter.component'; import { opPhaseIconData, toDOMString } from '@openproject/octicons-angular'; import { ProjectPhaseResource } from 'core-app/features/hal/resources/project-phase-resource'; @@ -49,8 +49,8 @@ export class ProjectPhaseAutocompleterComponent extends CreateAutocompleterCompo public phaseIcon:SafeHtml; - constructor(readonly injector:Injector) { - super(injector); + constructor() { + super(); this.phaseIcon = this.sanitizer.bypassSecurityTrustHtml(toDOMString(opPhaseIconData, 'small', { 'aria-hidden': 'true', class: 'octicon' })); } diff --git a/frontend/src/app/shared/components/autocompleter/time-entries-work-package-autocompleter/time-entries-work-package-autocompleter-template.component.ts b/frontend/src/app/shared/components/autocompleter/time-entries-work-package-autocompleter/time-entries-work-package-autocompleter-template.component.ts index aa97fa5a4e4b..2d7a558a3db9 100644 --- a/frontend/src/app/shared/components/autocompleter/time-entries-work-package-autocompleter/time-entries-work-package-autocompleter-template.component.ts +++ b/frontend/src/app/shared/components/autocompleter/time-entries-work-package-autocompleter/time-entries-work-package-autocompleter-template.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Injector, Input, TemplateRef, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Injector, Input, TemplateRef, ViewChild, inject } from '@angular/core'; import { IAutocompleterTemplateComponent, OpAutocompleterComponent, @@ -41,6 +41,8 @@ import { standalone: false, }) export class TimeEntriesWorkPackageAutocompleterTemplateComponent implements IAutocompleterTemplateComponent { + readonly injector = inject(Injector); + @Input() public mode:string|undefined; @Input() public isOpenedInModal = false; @Input() public hoverCards = true; @@ -48,9 +50,4 @@ export class TimeEntriesWorkPackageAutocompleterTemplateComponent implements IAu @ViewChild('headerTemplate') headerTemplate:TemplateRef; autocompleter:TimeEntriesWorkPackageAutocompleterComponent = this.injector.get(OpAutocompleterComponent) as TimeEntriesWorkPackageAutocompleterComponent; - - constructor( - readonly injector:Injector, - ) { - } } diff --git a/frontend/src/app/shared/components/autocompleter/user-autocompleter/user-autocompleter-template.component.ts b/frontend/src/app/shared/components/autocompleter/user-autocompleter/user-autocompleter-template.component.ts index 4dfaa1687651..7857c5e99d5b 100644 --- a/frontend/src/app/shared/components/autocompleter/user-autocompleter/user-autocompleter-template.component.ts +++ b/frontend/src/app/shared/components/autocompleter/user-autocompleter/user-autocompleter-template.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input, TemplateRef, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, TemplateRef, ViewChild, inject } from '@angular/core'; import { IAutocompleterTemplateComponent, } from 'core-app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component'; @@ -41,12 +41,12 @@ import { hrefFromPrincipal, typeFromHref } from 'core-app/shared/components/prin standalone: false, }) export class UserAutocompleterTemplateComponent implements IAutocompleterTemplateComponent { + private readonly pathHelperService = inject(PathHelperService); + @Input() public inviteUserToProject:string|undefined; @Input() public isOpenedInModal = false; @Input() public hoverCards = true; - constructor(private readonly pathHelperService:PathHelperService) {} - @ViewChild('optionTemplate') optionTemplate:TemplateRef; @ViewChild('footerTemplate') footerTemplate?:TemplateRef; diff --git a/frontend/src/app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component.ts index 531947a644d9..244df7f93e5b 100644 --- a/frontend/src/app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component.ts @@ -26,15 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - EventEmitter, - Injector, - Output, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Output, inject } from '@angular/core'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { CreateAutocompleterComponent } from 'core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; @@ -52,6 +44,13 @@ import { firstValueFrom } from 'rxjs'; standalone: false, }) export class VersionAutocompleterComponent extends CreateAutocompleterComponent implements AfterViewInit { + readonly I18n = inject(I18nService); + readonly currentProject = inject(CurrentProjectService); + readonly cdRef = inject(ChangeDetectorRef); + readonly pathHelper = inject(PathHelperService); + readonly apiV3Service = inject(ApiV3Service); + readonly halNotification = inject(HalResourceNotificationService); + @Output() public onCreate = new EventEmitter(); groupByFn = (item:HalResource):string|null => { @@ -60,18 +59,6 @@ export class VersionAutocompleterComponent extends CreateAutocompleterComponent return project?.name || this.I18n.t('js.project.not_available'); }; - constructor( - readonly injector:Injector, - readonly I18n:I18nService, - readonly currentProject:CurrentProjectService, - readonly cdRef:ChangeDetectorRef, - readonly pathHelper:PathHelperService, - readonly apiV3Service:ApiV3Service, - readonly halNotification:HalResourceNotificationService, - ) { - super(injector); - } - ngAfterViewInit() { super.ngAfterViewInit(); diff --git a/frontend/src/app/shared/components/breadcrumbs/op-breadcrumbs.component.ts b/frontend/src/app/shared/components/breadcrumbs/op-breadcrumbs.component.ts index a3a2eaedac55..0132888898c7 100644 --- a/frontend/src/app/shared/components/breadcrumbs/op-breadcrumbs.component.ts +++ b/frontend/src/app/shared/components/breadcrumbs/op-breadcrumbs.component.ts @@ -26,11 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - Input, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; export type BreadcrumbItem = @@ -45,13 +41,11 @@ export type BreadcrumbItem = standalone: false, }) export class OpBreadcrumbsComponent { + readonly I18n = inject(I18nService); + @Input() items:BreadcrumbItem[] = []; @Input() lastItemSection?:string | null = null; - constructor( - readonly I18n:I18nService, - ) { } - text = { breadcrumb: this.I18n.t('js.breadcrumb'), }; diff --git a/frontend/src/app/shared/components/colors/colors-autocompleter.component.ts b/frontend/src/app/shared/components/colors/colors-autocompleter.component.ts index 01996e13690b..8850aee5a30d 100644 --- a/frontend/src/app/shared/components/colors/colors-autocompleter.component.ts +++ b/frontend/src/app/shared/components/colors/colors-autocompleter.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, ElementRef, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, OnInit, inject } from '@angular/core'; import { Highlighting, } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; @@ -62,6 +62,9 @@ interface ColorItem { standalone: false, }) export class ColorsAutocompleterComponent implements OnInit { + protected elementRef = inject>(ElementRef); + protected readonly I18n = inject(I18nService); + public options:ColorItem[]; public selectedOption?:ColorItem|string; @@ -74,12 +77,6 @@ export class ColorsAutocompleterComponent implements OnInit { private selectedColorId:string; - constructor( - protected elementRef:ElementRef, - protected readonly I18n:I18nService, - ) { - } - ngOnInit() { this.setColorOptions(); diff --git a/frontend/src/app/shared/components/copy-to-clipboard/copy-to-clipboard.service.ts b/frontend/src/app/shared/components/copy-to-clipboard/copy-to-clipboard.service.ts index 751f45f9c659..175aba389dbc 100644 --- a/frontend/src/app/shared/components/copy-to-clipboard/copy-to-clipboard.service.ts +++ b/frontend/src/app/shared/components/copy-to-clipboard/copy-to-clipboard.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -34,10 +34,9 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; providedIn: 'root', }) export class CopyToClipboardService { - constructor( - readonly toastService:ToastService, - readonly I18n:I18nService, - ) { } + readonly toastService = inject(ToastService); + readonly I18n = inject(I18nService); + copy(content:string, successMessage?:string) { if (!navigator.clipboard) { diff --git a/frontend/src/app/shared/components/date/op-date-time.component.ts b/frontend/src/app/shared/components/date/op-date-time.component.ts index 4020cd79f35d..281c8366de71 100644 --- a/frontend/src/app/shared/components/date/op-date-time.component.ts +++ b/frontend/src/app/shared/components/date/op-date-time.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; @Component({ @@ -45,15 +45,14 @@ import { TimezoneService } from 'core-app/core/datetime/timezone.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class OpDateTimeComponent implements OnInit { + readonly timezoneService = inject(TimezoneService); + @Input() dateTimeValue:any; public date:any; public time:any; - constructor(readonly timezoneService:TimezoneService) { - } - ngOnInit() { const c = this.timezoneService.formattedDatetimeComponents(this.dateTimeValue); this.date = c[0]; diff --git a/frontend/src/app/shared/components/datepicker/basic-datepicker.module.ts b/frontend/src/app/shared/components/datepicker/basic-datepicker.module.ts index 498dcfd7b274..229f0ac5cb0b 100644 --- a/frontend/src/app/shared/components/datepicker/basic-datepicker.module.ts +++ b/frontend/src/app/shared/components/datepicker/basic-datepicker.module.ts @@ -1,4 +1,4 @@ -import { Injector, NgModule } from '@angular/core'; +import { Injector, NgModule, inject } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { CommonModule } from '@angular/common'; @@ -28,6 +28,5 @@ import { OpBasicSingleDatePickerComponent } from './basic-single-date-picker/bas ], }) export class OpBasicDatePickerModule { - constructor(readonly injector:Injector) { - } + readonly injector = inject(Injector); } diff --git a/frontend/src/app/shared/components/datepicker/basic-range-date-picker/basic-range-date-picker.component.ts b/frontend/src/app/shared/components/datepicker/basic-range-date-picker/basic-range-date-picker.component.ts index dc707ceafcfc..7e62b029ccd2 100644 --- a/frontend/src/app/shared/components/datepicker/basic-range-date-picker/basic-range-date-picker.component.ts +++ b/frontend/src/app/shared/components/datepicker/basic-range-date-picker/basic-range-date-picker.component.ts @@ -26,22 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - forwardRef, - HostBinding, - Injector, - Input, - OnInit, - Output, - ViewChild, - ViewEncapsulation, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, forwardRef, HostBinding, Injector, Input, OnInit, Output, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ControlValueAccessor, @@ -81,6 +66,13 @@ export const opBasicRangeDatePickerSelector = 'op-basic-range-date-picker'; standalone: false, }) export class OpBasicRangeDatePickerComponent implements OnInit, ControlValueAccessor, AfterViewInit { + readonly I18n = inject(I18nService); + readonly timezoneService = inject(TimezoneService); + readonly injector = inject(Injector); + readonly cdRef = inject(ChangeDetectorRef); + readonly elementRef = inject(ElementRef); + readonly deviceService = inject(DeviceService); + @HostBinding('class.op-basic-range-datepicker') className = true; @HostBinding('class.op-basic-range-datepicker_mobile') mobile = false; @@ -133,14 +125,7 @@ export class OpBasicRangeDatePickerComponent implements OnInit, ControlValueAcce spacer: this.I18n.t('js.filter.value_spacer'), }; - constructor( - readonly I18n:I18nService, - readonly timezoneService:TimezoneService, - readonly injector:Injector, - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef, - readonly deviceService:DeviceService, - ) { + constructor() { populateInputsFromDataset(this); } diff --git a/frontend/src/app/shared/components/datepicker/basic-single-date-picker/basic-single-date-picker.component.ts b/frontend/src/app/shared/components/datepicker/basic-single-date-picker/basic-single-date-picker.component.ts index 8265e6969b25..b4f3a1da95d5 100644 --- a/frontend/src/app/shared/components/datepicker/basic-single-date-picker/basic-single-date-picker.component.ts +++ b/frontend/src/app/shared/components/datepicker/basic-single-date-picker/basic-single-date-picker.component.ts @@ -26,22 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - forwardRef, - Injector, - Input, - OnDestroy, - OnInit, - Output, - ViewChild, - ViewEncapsulation, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, forwardRef, Injector, Input, OnDestroy, OnInit, Output, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { onDayCreate, validDate } from 'core-app/shared/components/datepicker/helpers/date-modal.helpers'; @@ -67,6 +52,13 @@ import { DeviceService } from 'core-app/core/browser/device.service'; standalone: false, }) export class OpBasicSingleDatePickerComponent implements ControlValueAccessor, OnInit, AfterViewInit, OnDestroy { + readonly I18n = inject(I18nService); + readonly timezoneService = inject(TimezoneService); + readonly injector = inject(Injector); + readonly cdRef = inject(ChangeDetectorRef); + readonly elementRef = inject(ElementRef); + readonly deviceService = inject(DeviceService); + @Output() valueChange = new EventEmitter(); @Output() picked = new EventEmitter(); @@ -112,14 +104,7 @@ export class OpBasicSingleDatePickerComponent implements ControlValueAccessor, O public datePickerInstance:DatePicker; - constructor( - readonly I18n:I18nService, - readonly timezoneService:TimezoneService, - readonly injector:Injector, - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef, - readonly deviceService:DeviceService, - ) { + constructor() { populateInputsFromDataset(this); } diff --git a/frontend/src/app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component.ts b/frontend/src/app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component.ts index 0000790accc3..2d005a4c06a6 100644 --- a/frontend/src/app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component.ts +++ b/frontend/src/app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component.ts @@ -26,21 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterContentInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - forwardRef, - Injector, - Input, - OnInit, - Output, - ViewChild, - ViewEncapsulation, -} from '@angular/core'; +import { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, forwardRef, Injector, Input, OnInit, Output, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { onDayCreate, parseDate, setDates } from 'core-app/shared/components/datepicker/helpers/date-modal.helpers'; @@ -73,6 +59,13 @@ import { changeDetection: ChangeDetectionStrategy.Default, }) export class OpModalSingleDatePickerComponent implements ControlValueAccessor, OnInit, AfterContentInit { + readonly I18n = inject(I18nService); + readonly timezoneService = inject(TimezoneService); + readonly injector = inject(Injector); + readonly cdRef = inject(ChangeDetectorRef); + readonly elementRef = inject(ElementRef); + readonly spotDropModalTeleportationService = inject(SpotDropModalTeleportationService); + @Output() closed = new EventEmitter(); @Output() valueChange = new EventEmitter(); @@ -145,14 +138,7 @@ export class OpModalSingleDatePickerComponent implements ControlValueAccessor, O }, }; - constructor( - readonly I18n:I18nService, - readonly timezoneService:TimezoneService, - readonly injector:Injector, - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef, - readonly spotDropModalTeleportationService:SpotDropModalTeleportationService, - ) { + constructor() { populateInputsFromDataset(this); } diff --git a/frontend/src/app/shared/components/datepicker/sheet/date-picker-sheet.component.ts b/frontend/src/app/shared/components/datepicker/sheet/date-picker-sheet.component.ts index 7f465d6c187c..5bb03cd965d7 100644 --- a/frontend/src/app/shared/components/datepicker/sheet/date-picker-sheet.component.ts +++ b/frontend/src/app/shared/components/datepicker/sheet/date-picker-sheet.component.ts @@ -26,22 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - HostBinding, - Injector, - Input, - OnChanges, - Output, - SimpleChanges, - ViewChild, - ViewEncapsulation, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, Injector, Input, OnChanges, Output, SimpleChanges, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { onDayCreate } from 'core-app/shared/components/datepicker/helpers/date-modal.helpers'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; @@ -61,6 +46,13 @@ import { DeviceService } from 'core-app/core/browser/device.service'; standalone: false, }) export class OpDatePickerSheetComponent implements AfterViewInit, OnChanges { + readonly I18n = inject(I18nService); + readonly timezoneService = inject(TimezoneService); + readonly injector = inject(Injector); + readonly cdRef = inject(ChangeDetectorRef); + readonly elementRef = inject(ElementRef); + readonly deviceService = inject(DeviceService); + @HostBinding('class.op-datepicker-sheet') className = true; @Input() dates:string[]; @@ -77,16 +69,6 @@ export class OpDatePickerSheetComponent implements AfterViewInit, OnChanges { datePickerInstance:DatePicker; - constructor( - readonly I18n:I18nService, - readonly timezoneService:TimezoneService, - readonly injector:Injector, - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef, - readonly deviceService:DeviceService, - ) { - } - ngAfterViewInit():void { this.initializeDatepicker(); } diff --git a/frontend/src/app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker-instance.component.ts b/frontend/src/app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker-instance.component.ts index 927728c5fa35..3f764fc6e5ac 100644 --- a/frontend/src/app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker-instance.component.ts +++ b/frontend/src/app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker-instance.component.ts @@ -26,16 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Injector, - Input, - ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Injector, Input, ViewChild, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; import { DayElement } from 'flatpickr/dist/types/instance'; @@ -65,6 +56,15 @@ export type DateMode = 'single'|'range'; standalone: false, }) export class OpWpDatePickerInstanceComponent extends UntilDestroyedMixin implements AfterViewInit { + readonly injector = inject(Injector); + readonly cdRef = inject(ChangeDetectorRef); + readonly apiV3Service = inject(ApiV3Service); + readonly I18n = inject(I18nService); + readonly timezoneService = inject(TimezoneService); + readonly deviceService = inject(DeviceService); + readonly pathHelper = inject(PathHelperService); + readonly elementRef = inject(ElementRef); + @Input() public ignoreNonWorkingDays:boolean; @Input() public scheduleManually:boolean; @@ -89,16 +89,7 @@ export class OpWpDatePickerInstanceComponent extends UntilDestroyedMixin impleme private minimalSchedulingDate:Date|null; private onFlatpickrSetValuesBound = this.onFlatpickrSetValues.bind(this); - constructor( - readonly injector:Injector, - readonly cdRef:ChangeDetectorRef, - readonly apiV3Service:ApiV3Service, - readonly I18n:I18nService, - readonly timezoneService:TimezoneService, - readonly deviceService:DeviceService, - readonly pathHelper:PathHelperService, - readonly elementRef:ElementRef, - ) { + constructor() { super(); populateInputsFromDataset(this); this.startDateValue = this.toDate(this.startDate); diff --git a/frontend/src/app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker.modal.ts b/frontend/src/app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker.modal.ts index 81de61921482..a8a7ec2e508f 100644 --- a/frontend/src/app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker.modal.ts +++ b/frontend/src/app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker.modal.ts @@ -26,18 +26,9 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Inject, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @Component({ @@ -46,19 +37,12 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service standalone: false, }) export class OpWpDatePickerModalComponent extends OpModalComponent implements OnInit { + readonly pathHelper = inject(PathHelperService); + turboFrameSrc:string; showCloseButton = false; - constructor( - readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly pathHelper:PathHelperService, - ) { - super(locals, cdRef, elementRef); - } - ngOnInit() { super.ngOnInit(); this.updateFrameSrc(); diff --git a/frontend/src/app/shared/components/editable-toolbar-title/editable-toolbar-title.component.ts b/frontend/src/app/shared/components/editable-toolbar-title/editable-toolbar-title.component.ts index e66895b37061..4ca4b9e7364b 100644 --- a/frontend/src/app/shared/components/editable-toolbar-title/editable-toolbar-title.component.ts +++ b/frontend/src/app/shared/components/editable-toolbar-title/editable-toolbar-title.component.ts @@ -25,21 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - HostBinding, - Injector, - Input, - OnChanges, - OnInit, - Output, - SimpleChanges, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, Injector, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { whenOutside } from 'core-app/shared/directives/focus/contain-helpers'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; @@ -58,6 +44,9 @@ export const selectableTitleIdentifier = 'editable-toolbar-title'; changeDetection: ChangeDetectionStrategy.Default, }) export class EditableToolbarTitleComponent implements OnInit, OnChanges { + readonly injector = inject(Injector); + private cdRef = inject(ChangeDetectorRef); + @Input('title') public inputTitle:string; @Input() public editable = true; @@ -101,9 +90,6 @@ export class EditableToolbarTitleComponent implements OnInit, OnChanges { duplicate_query_title: this.I18n.t('js.work_packages.query.errors.duplicate_query_title'), }; - constructor(readonly injector:Injector, private cdRef:ChangeDetectorRef) { - } - ngOnInit():void { this.text.input_title = `${this.text.click_to_edit} ${this.text.press_enter_to_save}`; diff --git a/frontend/src/app/shared/components/editor/components/ckeditor-augmented-textarea/ckeditor-augmented-textarea.component.ts b/frontend/src/app/shared/components/editor/components/ckeditor-augmented-textarea/ckeditor-augmented-textarea.component.ts index fdb978ca2436..f66cd491fadb 100644 --- a/frontend/src/app/shared/components/editor/components/ckeditor-augmented-textarea/ckeditor-augmented-textarea.component.ts +++ b/frontend/src/app/shared/components/editor/components/ckeditor-augmented-textarea/ckeditor-augmented-textarea.component.ts @@ -26,16 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - ElementRef, - EventEmitter, - Input, - OnInit, - Output, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; @@ -66,6 +57,13 @@ import { attributeTokenList, ensureId } from 'core-app/shared/helpers/dom-helper standalone: false, }) export class CkeditorAugmentedTextareaComponent extends UntilDestroyedMixin implements OnInit { + readonly elementRef = inject>(ElementRef); + protected pathHelper = inject(PathHelperService); + protected halResourceService = inject(HalResourceService); + protected Notifications = inject(ToastService); + protected I18n = inject(I18nService); + protected states = inject(States); + // Track form submission "in-flight" state per form, to prevent multiple // submissions from multiple CKEditor instances on the same form. private static inFlight = new WeakMap(); @@ -135,14 +133,7 @@ export class CkeditorAugmentedTextareaComponent extends UntilDestroyedMixin impl private labelClickSubscription:Subscription; - constructor( - readonly elementRef:ElementRef, - protected pathHelper:PathHelperService, - protected halResourceService:HalResourceService, - protected Notifications:ToastService, - protected I18n:I18nService, - protected states:States, - ) { + constructor() { super(); populateInputsFromDataset(this); } diff --git a/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-preview.service.ts b/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-preview.service.ts index c9907c3507e0..b4458c945df3 100644 --- a/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-preview.service.ts +++ b/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-preview.service.ts @@ -26,16 +26,14 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ApplicationRef, ComponentFactoryResolver, ComponentRef, Injectable, Injector } from '@angular/core'; +import { ApplicationRef, ComponentFactoryResolver, ComponentRef, Injectable, Injector, inject } from '@angular/core'; @Injectable() export class CKEditorPreviewService { - constructor( - private readonly componentFactoryResolver:ComponentFactoryResolver, - private readonly appRef:ApplicationRef, - private readonly injector:Injector, - ) { - } + private readonly componentFactoryResolver = inject(ComponentFactoryResolver); + private readonly appRef = inject(ApplicationRef); + private readonly injector = inject(Injector); + /** * Render preview into the given element, return a remover function to disconnect all diff --git a/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-setup.service.ts b/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-setup.service.ts index deaa4c7f0fd8..1dc058140f80 100644 --- a/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-setup.service.ts +++ b/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-setup.service.ts @@ -1,5 +1,5 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ICKEditorContext, ICKEditorStatic, @@ -21,18 +21,15 @@ declare global { @Injectable() export class CKEditorSetupService { + readonly PathHelper = inject(PathHelperService); + readonly configurationService = inject(ConfigurationService); + /** The language CKEditor was able to load, falls back to 'en' */ private loadedLocale = 'en'; /** Prefetch ckeditor when browser is idle */ private prefetch:Promise; - constructor( - readonly PathHelper:PathHelperService, - readonly configurationService:ConfigurationService, - ) { - } - public initialize() { this.prefetch = this.load(); this.watchTopLayer(); diff --git a/frontend/src/app/shared/components/editor/openproject-editor.module.ts b/frontend/src/app/shared/components/editor/openproject-editor.module.ts index c08d0c724037..6bf4c068b257 100644 --- a/frontend/src/app/shared/components/editor/openproject-editor.module.ts +++ b/frontend/src/app/shared/components/editor/openproject-editor.module.ts @@ -86,6 +86,4 @@ export function initializeServices(injector:Injector) { ], }) export class OpenprojectEditorModule { - constructor(injector:Injector) { - } } diff --git a/frontend/src/app/shared/components/fields/display/display-field.component.ts b/frontend/src/app/shared/components/fields/display/display-field.component.ts index 75d57f6ef693..bb95ee5a5309 100644 --- a/frontend/src/app/shared/components/fields/display/display-field.component.ts +++ b/frontend/src/app/shared/components/fields/display/display-field.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, ElementRef, Injector, Input, OnInit, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Injector, Input, OnInit, ViewChild, inject } from '@angular/core'; import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; import { DisplayFieldService } from 'core-app/shared/components/fields/display/display-field.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @@ -14,6 +14,10 @@ import { SchemaResource } from 'core-app/features/hal/resources/schema-resource' standalone: false, }) export class DisplayFieldComponent implements OnInit { + private injector = inject(Injector); + private displayFieldService = inject(DisplayFieldService); + private schemaCache = inject(SchemaCacheService); + @Input() resource:HalResource; @Input() fieldName:string; @@ -26,13 +30,6 @@ export class DisplayFieldComponent implements OnInit { @ViewChild('displayFieldContainer') container:ElementRef; - constructor( - private injector:Injector, - private displayFieldService:DisplayFieldService, - private schemaCache:SchemaCacheService, - ) { - } - ngOnInit():void { void this.schemaCache .ensureLoaded(this.resource) diff --git a/frontend/src/app/shared/components/fields/display/field-types/excluded-icon-helper.service.ts b/frontend/src/app/shared/components/fields/display/field-types/excluded-icon-helper.service.ts index 8f7cd1008c54..2e7bd4c85716 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/excluded-icon-helper.service.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/excluded-icon-helper.service.ts @@ -26,16 +26,15 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { StatusResource } from 'core-app/features/hal/resources/status-resource'; @Injectable({ providedIn: 'root' }) export class ExcludedIconHelperService { - constructor( - private apiV3Service:ApiV3Service, - ) {} + private apiV3Service = inject(ApiV3Service); + public addIconIfExcludedFromTotals(element:HTMLElement, resource:WorkPackageResource):void { if (resource?.status) { diff --git a/frontend/src/app/shared/components/fields/display/field-types/hierarchy-query-link-helper.service.ts b/frontend/src/app/shared/components/fields/display/field-types/hierarchy-query-link-helper.service.ts index d06bf3c97bb0..17519fb45a6b 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/hierarchy-query-link-helper.service.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/hierarchy-query-link-helper.service.ts @@ -26,16 +26,15 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import URI from 'urijs'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Injectable({ providedIn: 'root' }) export class HierarchyQueryLinkHelperService { - constructor( - private pathHelper:PathHelperService, - ) {} + private pathHelper = inject(PathHelperService); + public addHref(link:HTMLAnchorElement, resource:HalResource):void { if (resource && resource.id) { diff --git a/frontend/src/app/shared/components/fields/display/info/op-exclusion-info.component.ts b/frontend/src/app/shared/components/fields/display/info/op-exclusion-info.component.ts index 0fba50d837ea..a59b4c5373a4 100644 --- a/frontend/src/app/shared/components/fields/display/info/op-exclusion-info.component.ts +++ b/frontend/src/app/shared/components/fields/display/info/op-exclusion-info.component.ts @@ -28,13 +28,7 @@ * ++ */ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - Input, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ @@ -45,6 +39,9 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; standalone: false, }) export class OpExclusionInfoComponent implements OnInit { + readonly I18n = inject(I18nService); + readonly cdRef = inject(ChangeDetectorRef); + public opened = false; @Input() public statusName:string; @@ -55,11 +52,6 @@ export class OpExclusionInfoComponent implements OnInit { buttonClose: this.I18n.t('js.button_close'), }; - constructor( - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - ) {} - ngOnInit() { this.text.modalContent = this.I18n.t('js.exclusion_info.modal.content', { status_name: this.statusName || '' }); } diff --git a/frontend/src/app/shared/components/fields/edit/edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/edit-field.component.ts index 273afd450144..519c506f13e0 100644 --- a/frontend/src/app/shared/components/fields/edit/edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/edit-field.component.ts @@ -26,16 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectorRef, - Directive, - ElementRef, - Inject, - InjectionToken, - Injector, - OnDestroy, - OnInit, -} from '@angular/core'; +import { ChangeDetectorRef, Directive, ElementRef, InjectionToken, Injector, OnDestroy, OnInit, inject } from '@angular/core'; import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { Field, IFieldSchema } from 'core-app/shared/components/fields/field.base'; @@ -53,21 +44,23 @@ export const editModeClassName = '-editing'; @Directive() export abstract class EditFieldComponent extends Field implements OnInit, OnDestroy { + readonly I18n = inject(I18nService); + readonly elementRef = inject(ElementRef); + protected change = inject>(OpEditingPortalChangesetToken); + schema = inject(OpEditingPortalSchemaToken); + readonly handler = inject(OpEditingPortalHandlerToken); + readonly cdRef = inject(ChangeDetectorRef); + readonly injector = inject(Injector); + /** Self reference */ public self = this; protected element:HTMLElement; - constructor( - readonly I18n:I18nService, - readonly elementRef:ElementRef, - @Inject(OpEditingPortalChangesetToken) protected change:ResourceChangeset, - @Inject(OpEditingPortalSchemaToken) public schema:IFieldSchema, - @Inject(OpEditingPortalHandlerToken) readonly handler:EditFieldHandler, - readonly cdRef:ChangeDetectorRef, - readonly injector:Injector, - ) { + constructor() { super(); + const change = this.change; + this.updateFromChangeset(change); } diff --git a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.component.ts b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.component.ts index 4739667235a2..5897a8faa793 100644 --- a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.component.ts +++ b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.component.ts @@ -26,20 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ApplicationRef, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - Injector, - Input, - OnDestroy, - OnInit, - Optional, - Output, -} from '@angular/core'; +import { ApplicationRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Injector, Input, OnDestroy, OnInit, Output, inject } from '@angular/core'; import { StateService, Transition, TransitionService } from '@uirouter/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { EditableAttributeFieldComponent } from 'core-app/shared/components/fields/edit/field/editable-attribute-field.component'; @@ -70,6 +57,18 @@ import { firstValueFrom } from 'rxjs'; changeDetection: ChangeDetectionStrategy.Default, }) export class EditFormComponent extends EditForm implements OnInit, OnDestroy { + readonly injector:Injector; + protected readonly elementRef = inject(ElementRef); + private appRef = inject(ApplicationRef); + private readonly cdRef = inject(ChangeDetectorRef); + protected readonly $transitions = inject(TransitionService); + protected readonly configurationService = inject(ConfigurationService); + protected readonly editingPortalService = inject(EditingPortalService); + protected readonly $state = inject(StateService); + protected readonly I18n = inject(I18nService); + protected readonly editFormRouting = inject(EditFormRoutingService, { optional: true }); + private globalEditFormChangesTrackerService = inject(GlobalEditFormChangesTrackerService); + @Input() resource:HalResource; @Input('inEditMode') initializeEditMode = false; @@ -84,20 +83,16 @@ export class EditFormComponent extends EditForm implements OnInit, private unregisterListener:Function; - constructor(public readonly injector:Injector, - protected readonly elementRef:ElementRef, - private appRef:ApplicationRef, - private readonly cdRef:ChangeDetectorRef, - protected readonly $transitions:TransitionService, - protected readonly ConfigurationService:ConfigurationService, - protected readonly editingPortalService:EditingPortalService, - protected readonly $state:StateService, - protected readonly I18n:I18nService, - @Optional() protected readonly editFormRouting:EditFormRoutingService, - private globalEditFormChangesTrackerService:GlobalEditFormChangesTrackerService) { + constructor() { + const injector = inject(Injector); + super(injector); + this.injector = injector; + const $transitions = this.$transitions; + const I18n = this.I18n; + const confirmText = I18n.t('js.work_packages.confirm_edit_cancel'); - const requiresConfirmation = ConfigurationService.warnOnLeavingUnsaved(); + const requiresConfirmation = this.configurationService.warnOnLeavingUnsaved(); this.unregisterListener = $transitions.onBefore({}, (transition:Transition) => { if (!this.editing) { diff --git a/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.component.ts b/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.component.ts index 10502c89c9a2..fb336e1542d9 100644 --- a/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.component.ts +++ b/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.component.ts @@ -1,16 +1,4 @@ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - Injector, - Input, - OnDestroy, - OnInit, - Output, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Injector, Input, OnDestroy, OnInit, Output, inject } from '@angular/core'; import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; import { HalResourceEditFieldHandler } from 'core-app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler'; import { takeUntil } from 'rxjs/operators'; @@ -34,6 +22,11 @@ import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/r changeDetection: ChangeDetectionStrategy.Default, }) export class EditFormPortalComponent implements OnInit, OnDestroy, AfterViewInit { + readonly injector = inject(Injector); + readonly editField = inject(EditFieldService); + readonly elementRef = inject(ElementRef); + readonly cdRef = inject(ChangeDetectorRef); + @Input() schemaInput:IFieldSchema; @Input() changeInput:ResourceChangeset; @@ -56,13 +49,6 @@ export class EditFormPortalComponent implements OnInit, OnDestroy, AfterViewInit public label:string; - constructor( - readonly injector:Injector, - readonly editField:EditFieldService, - readonly elementRef:ElementRef, - readonly cdRef:ChangeDetectorRef, - ) { } - ngOnInit() { if (this.editFieldHandler && this.schemaInput) { this.handler = this.editFieldHandler; diff --git a/frontend/src/app/shared/components/fields/edit/editing-portal/editing-portal-service.ts b/frontend/src/app/shared/components/fields/edit/editing-portal/editing-portal-service.ts index d1afb5eb3fd6..7e66e1b0a952 100644 --- a/frontend/src/app/shared/components/fields/edit/editing-portal/editing-portal-service.ts +++ b/frontend/src/app/shared/components/fields/edit/editing-portal/editing-portal-service.ts @@ -1,9 +1,7 @@ /** * A CDK portal implementation to wrap edit-fields in non-angular contexts. */ -import { - ApplicationRef, Injectable, Injector, -} from '@angular/core'; +import { ApplicationRef, Injectable, Injector, inject } from '@angular/core'; import { ComponentPortal, DomPortalOutlet } from '@angular/cdk/portal'; import { EditFormPortalComponent } from 'core-app/shared/components/fields/edit/editing-portal/edit-form-portal.component'; import { createLocalInjector } from 'core-app/shared/components/fields/edit/editing-portal/edit-form-portal.injector'; @@ -16,10 +14,9 @@ import { HalResourceEditFieldHandler } from 'core-app/shared/components/fields/e @Injectable({ providedIn: 'root' }) export class EditingPortalService { - constructor(private readonly appRef:ApplicationRef, - private readonly pathHelper:PathHelperService) { + private readonly appRef = inject(ApplicationRef); + private readonly pathHelper = inject(PathHelperService); - } public create(container:HTMLElement, injector:Injector, diff --git a/frontend/src/app/shared/components/fields/edit/field-types/date-picker-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/date-picker-edit-field.component.ts index 4af523a4c407..a09f8cdfbc0a 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/date-picker-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/date-picker-edit-field.component.ts @@ -26,35 +26,22 @@ * See COPYRIGHT and LICENSE files for more details. */ -import { - ChangeDetectorRef, - Directive, - ElementRef, - Inject, - Injector, - OnDestroy, - OnInit, -} from '@angular/core'; +import { Directive, OnDestroy, OnInit, inject } from '@angular/core'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; import { EditFieldComponent, - OpEditingPortalChangesetToken, - OpEditingPortalHandlerToken, - OpEditingPortalSchemaToken, } from 'core-app/shared/components/fields/edit/edit-field.component'; import { DeviceService } from 'core-app/core/browser/device.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; -import { HalResource } from 'core-app/features/hal/resources/hal-resource'; -import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; -import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { OpWpDatePickerModalComponent } from 'core-app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker.modal'; @Directive() export abstract class DatePickerEditFieldComponent extends EditFieldComponent implements OnInit, OnDestroy { + readonly pathHelper = inject(PathHelperService); + readonly opModalService = inject(OpModalService); + @InjectField() readonly timezoneService:TimezoneService; @InjectField() deviceService:DeviceService; @@ -65,20 +52,6 @@ export abstract class DatePickerEditFieldComponent extends EditFieldComponent im private updateHandler:EventListener = this.handleSuccessfulUpdate.bind(this); private cancelHandler:EventListener = this.cancel.bind(this); - constructor( - readonly I18n:I18nService, - readonly elementRef:ElementRef, - @Inject(OpEditingPortalChangesetToken) protected change:ResourceChangeset, - @Inject(OpEditingPortalSchemaToken) public schema:IFieldSchema, - @Inject(OpEditingPortalHandlerToken) readonly handler:EditFieldHandler, - readonly cdRef:ChangeDetectorRef, - readonly injector:Injector, - readonly pathHelper:PathHelperService, - readonly opModalService:OpModalService, - ) { - super(I18n, elementRef, change, schema, handler, cdRef, injector); - } - ngOnInit():void { super.ngOnInit(); diff --git a/frontend/src/app/shared/components/fields/edit/field-types/progress-popover-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/progress-popover-edit-field.component.ts index 831c159f5ecd..6ba0193a241b 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/progress-popover-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/progress-popover-edit-field.component.ts @@ -28,27 +28,9 @@ * ++ */ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Inject, - Injector, - OnInit, -} from '@angular/core'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { ProgressEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/progress-edit-field.component'; -import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; -import { HalResource } from 'core-app/features/hal/resources/hal-resource'; -import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; -import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; -import { - OpEditingPortalChangesetToken, - OpEditingPortalHandlerToken, - OpEditingPortalSchemaToken, -} from 'core-app/shared/components/fields/edit/edit-field.component'; import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; @@ -61,6 +43,12 @@ import { TimezoneService } from 'core-app/core/datetime/timezone.service'; standalone: false, }) export class ProgressPopoverEditFieldComponent extends ProgressEditFieldComponent implements OnInit { + readonly pathHelper = inject(PathHelperService); + private halEvents = inject(HalEventsService); + private toastService = inject(ToastService); + private apiV3Service = inject(ApiV3Service); + private timezoneService = inject(TimezoneService); + text = { title: this.I18n.t('js.work_packages.progress.title'), button_close: this.I18n.t('js.button_close'), @@ -72,23 +60,6 @@ export class ProgressPopoverEditFieldComponent extends ProgressEditFieldComponen opened = false; - constructor( - readonly I18n:I18nService, - readonly elementRef:ElementRef, - @Inject(OpEditingPortalChangesetToken) protected change:ResourceChangeset, - @Inject(OpEditingPortalSchemaToken) public schema:IFieldSchema, - @Inject(OpEditingPortalHandlerToken) readonly handler:EditFieldHandler, - readonly cdRef:ChangeDetectorRef, - readonly injector:Injector, - readonly pathHelper:PathHelperService, - private halEvents:HalEventsService, - private toastService:ToastService, - private apiV3Service:ApiV3Service, - private timezoneService:TimezoneService, - ) { - super(I18n, elementRef, change, schema, handler, cdRef, injector); - } - ngOnInit() { super.ngOnInit(); /* diff --git a/frontend/src/app/shared/components/fields/edit/field-types/project-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/project-edit-field.component.ts index f186542b2c97..f33d2c95cb76 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/project-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/project-edit-field.component.ts @@ -26,31 +26,14 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Inject, - Injector, - OnInit, - ViewEncapsulation, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { from } from 'rxjs'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; import { EditFieldComponent, - OpEditingPortalChangesetToken, - OpEditingPortalHandlerToken, - OpEditingPortalSchemaToken, } from 'core-app/shared/components/fields/edit/edit-field.component'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { IProject } from 'core-app/core/state/projects/project.model'; -import { HalResource } from 'core-app/features/hal/resources/hal-resource'; -import { ResourceChangeset } from '../../changeset/resource-changeset'; -import { IFieldSchema } from '../../field.base'; -import { EditFieldHandler } from '../editing-portal/edit-field-handler'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { take, tap } from 'rxjs/operators'; import isNewResource from 'core-app/features/hal/helpers/is-new-resource'; @@ -66,33 +49,14 @@ import { FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-bui standalone: false, }) export class ProjectEditFieldComponent extends EditFieldComponent implements OnInit { + readonly apiV3Service = inject(ApiV3Service); + readonly http = inject(HttpClient); + readonly halResourceService = inject(HalResourceService); + isNew = isNewResource(this.resource); url:string; - constructor( - readonly I18n:I18nService, - readonly elementRef:ElementRef, - @Inject(OpEditingPortalChangesetToken) protected change:ResourceChangeset, - @Inject(OpEditingPortalSchemaToken) public schema:IFieldSchema, - @Inject(OpEditingPortalHandlerToken) readonly handler:EditFieldHandler, - readonly cdRef:ChangeDetectorRef, - readonly injector:Injector, - readonly apiV3Service:ApiV3Service, - readonly http:HttpClient, - readonly halResourceService:HalResourceService, - ) { - super( - I18n, - elementRef, - change, - schema, - handler, - cdRef, - injector, - ); - } - initialize():void { if (this.schema.allowedValues) { this.setUrl(); diff --git a/frontend/src/app/shared/components/fields/edit/field-types/user-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/user-edit-field.component.ts index f74f1ffb4a86..e1e3899c8a1a 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/user-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/user-edit-field.component.ts @@ -26,29 +26,12 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Inject, - Injector, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; import { EditFieldComponent, - OpEditingPortalChangesetToken, - OpEditingPortalHandlerToken, - OpEditingPortalSchemaToken, } from 'core-app/shared/components/fields/edit/edit-field.component'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; -// import { IProject } from 'core-app/core/state/projects/project.model'; -import { HalResource } from 'core-app/features/hal/resources/hal-resource'; -import { ResourceChangeset } from '../../changeset/resource-changeset'; -import { IFieldSchema } from '../../field.base'; -import { EditFieldHandler } from '../editing-portal/edit-field-handler'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import isNewResource from 'core-app/features/hal/helpers/is-new-resource'; import { IUserAutocompleteItem } from 'core-app/shared/components/autocompleter/user-autocompleter/user-autocompleter.component'; @@ -60,33 +43,14 @@ import { CallableHalLink } from 'core-app/features/hal/hal-link/hal-link'; standalone: false, }) export class UserEditFieldComponent extends EditFieldComponent implements OnInit { + readonly apiV3Service = inject(ApiV3Service); + readonly http = inject(HttpClient); + readonly halResourceService = inject(HalResourceService); + isNew = isNewResource(this.resource); url:string; - constructor( - readonly I18n:I18nService, - readonly elementRef:ElementRef, - @Inject(OpEditingPortalChangesetToken) protected change:ResourceChangeset, - @Inject(OpEditingPortalSchemaToken) public schema:IFieldSchema, - @Inject(OpEditingPortalHandlerToken) readonly handler:EditFieldHandler, - readonly cdRef:ChangeDetectorRef, - readonly injector:Injector, - readonly apiV3Service:ApiV3Service, - readonly http:HttpClient, - readonly halResourceService:HalResourceService, - ) { - super( - I18n, - elementRef, - change, - schema, - handler, - cdRef, - injector, - ); - } - initialize():void { const link = this.schema.allowedValues as CallableHalLink|undefined; if (link) { diff --git a/frontend/src/app/shared/components/fields/edit/field/editable-attribute-field.component.ts b/frontend/src/app/shared/components/fields/edit/field/editable-attribute-field.component.ts index 9b6d036a9c78..e1382bc5143a 100644 --- a/frontend/src/app/shared/components/fields/edit/field/editable-attribute-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field/editable-attribute-field.component.ts @@ -29,18 +29,7 @@ import { HalResourceEditingService, } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Injector, - Input, - OnDestroy, - OnInit, - Optional, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Injector, Input, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { getPosition } from 'core-app/shared/helpers/set-click-position/set-click-position'; @@ -66,6 +55,16 @@ import { SchemaResource } from 'core-app/features/hal/resources/schema-resource' standalone: false, }) export class EditableAttributeFieldComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { + protected states = inject(States); + protected injector = inject(Injector); + protected elementRef = inject(ElementRef); + protected opContextMenu = inject(OPContextMenuService); + protected halEditing = inject(HalResourceEditingService); + protected schemaCache = inject(SchemaCacheService); + protected editForm = inject(EditFormComponent, { optional: true }); + protected cdRef = inject(ChangeDetectorRef); + protected I18n = inject(I18nService); + @Input() public fieldName:string; @Input() public resource:HalResource; @@ -90,21 +89,6 @@ export class EditableAttributeFieldComponent extends UntilDestroyedMixin impleme public destroyed = false; - constructor( - protected states:States, - protected injector:Injector, - protected elementRef:ElementRef, - protected opContextMenu:OPContextMenuService, - protected halEditing:HalResourceEditingService, - protected schemaCache:SchemaCacheService, - // Get parent field group from injector if we're in a form - @Optional() protected editForm:EditFormComponent, - protected cdRef:ChangeDetectorRef, - protected I18n:I18nService, - ) { - super(); - } - public setActive(active = true):void { this.active = active; if (!this.componentDestroyed) { @@ -197,7 +181,7 @@ export class EditableAttributeFieldComponent extends UntilDestroyedMixin impleme // Activate the field this.setActive(true); - return this.editForm + return this.editForm! .activate(this.fieldName, noWarnings) .catch(() => this.deactivate(true)); } diff --git a/frontend/src/app/shared/components/fields/edit/modal-with-turbo-content/modal-with-turbo-content.directive.ts b/frontend/src/app/shared/components/fields/edit/modal-with-turbo-content/modal-with-turbo-content.directive.ts index 4e414e2b4e41..cb2192c2fb21 100644 --- a/frontend/src/app/shared/components/fields/edit/modal-with-turbo-content/modal-with-turbo-content.directive.ts +++ b/frontend/src/app/shared/components/fields/edit/modal-with-turbo-content/modal-with-turbo-content.directive.ts @@ -26,16 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectorRef, - Directive, - ElementRef, - EventEmitter, - Input, - OnDestroy, - Output, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, Directive, ElementRef, EventEmitter, Input, OnDestroy, Output, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; @@ -49,6 +40,13 @@ import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/r standalone: false, }) export class ModalWithTurboContentDirective implements AfterViewInit, OnDestroy { + readonly elementRef = inject(ElementRef); + readonly cdRef = inject(ChangeDetectorRef); + readonly halEvents = inject(HalEventsService); + readonly apiV3Service = inject(ApiV3Service); + readonly toastService = inject(ToastService); + readonly I18n = inject(I18nService); + @Input() resource:HalResource; @Input() change:ResourceChangeset; @@ -60,17 +58,6 @@ export class ModalWithTurboContentDirective implements AfterViewInit, OnDestroy private preserveSegmentAttributesBound = this.preserveSegmentAttributes.bind(this); private cancelListenerBound = this.cancelListener.bind(this); - constructor( - readonly elementRef:ElementRef, - readonly cdRef:ChangeDetectorRef, - readonly halEvents:HalEventsService, - readonly apiV3Service:ApiV3Service, - readonly toastService:ToastService, - readonly I18n:I18nService, - ) { - - } - ngAfterViewInit() { (this.elementRef.nativeElement as HTMLElement) .addEventListener('turbo:submit-end', this.contextBasedListenerBound); diff --git a/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.ts b/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.ts index 07fa4924aff2..359dff8b6f0b 100644 --- a/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.ts +++ b/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -6,15 +6,15 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; providedIn: 'root', }) export class GlobalEditFormChangesTrackerService { + private i18nService = inject(I18nService); + private activeForms = new Map(); get thereAreFormsWithUnsavedChanges() { return Array.from(this.activeForms.keys()).some((form) => !form.change.isEmpty()); } - constructor( - private i18nService:I18nService, - ) { + constructor() { // Global beforeunload hook to show a data loss warn // when the user clicks on a link out of the Angular app window.addEventListener('beforeunload', (event) => { diff --git a/frontend/src/app/shared/components/fields/edit/services/hal-resource-editing.service.ts b/frontend/src/app/shared/components/fields/edit/services/hal-resource-editing.service.ts index a4373ac86601..b89940c3f4ee 100644 --- a/frontend/src/app/shared/components/fields/edit/services/hal-resource-editing.service.ts +++ b/frontend/src/app/shared/components/fields/edit/services/hal-resource-editing.service.ts @@ -30,7 +30,7 @@ import { combine, deriveRaw, InputState, multiInput, State, StatesGroup, } from '@openproject/reactivestates'; import { filter, map } from 'rxjs/operators'; -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { Subject } from 'rxjs'; import { FormResource } from 'core-app/features/hal/resources/form-resource'; import { ChangeMap } from 'core-app/shared/components/fields/changeset/changeset'; @@ -92,12 +92,14 @@ export type ResourceChangesetClass = new(...args:any[]) => ResourceChangeset; @Injectable() export class HalResourceEditingService extends StateCacheService { + protected readonly injector = inject(Injector); + protected readonly halEvents = inject(HalEventsService); + protected readonly hook = inject(HookService); + /** Committed / saved changes to work packages observable */ public committedChanges = new Subject(); - constructor(protected readonly injector:Injector, - protected readonly halEvents:HalEventsService, - protected readonly hook:HookService) { + constructor() { super(new ChangesetStates().changesets); } diff --git a/frontend/src/app/shared/components/fields/macros/attribute-label-macro.component.ts b/frontend/src/app/shared/components/fields/macros/attribute-label-macro.component.ts index 94ec525e1622..b39683a6a06c 100644 --- a/frontend/src/app/shared/components/fields/macros/attribute-label-macro.component.ts +++ b/frontend/src/app/shared/components/fields/macros/attribute-label-macro.component.ts @@ -26,15 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ Ng1FieldControlsWrapper, -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - HostBinding, - Injector, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostBinding, Injector, OnInit, inject } from '@angular/core'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; import { @@ -60,6 +52,14 @@ import { IOPFieldSchema } from 'core-app/features/hal/interfaces'; standalone: false, }) export class AttributeLabelMacroComponent implements OnInit { + readonly elementRef = inject(ElementRef); + readonly injector = inject(Injector); + readonly resourceLoader = inject(AttributeModelLoaderService); + readonly schemaCache = inject(SchemaCacheService); + readonly displayField = inject(DisplayFieldService); + readonly I18n = inject(I18nService); + readonly cdRef = inject(ChangeDetectorRef); + // Whether the value could not be loaded error:string|null = null; @@ -83,17 +83,6 @@ export class AttributeLabelMacroComponent implements OnInit { // The label to render label:string|undefined; - constructor( - readonly elementRef:ElementRef, - readonly injector:Injector, - readonly resourceLoader:AttributeModelLoaderService, - readonly schemaCache:SchemaCacheService, - readonly displayField:DisplayFieldService, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - ) { - } - ngOnInit():void { const element = this.elementRef.nativeElement as HTMLElement; const model = element.dataset.model as SupportedAttributeModels; diff --git a/frontend/src/app/shared/components/fields/macros/attribute-model-loader.service.ts b/frontend/src/app/shared/components/fields/macros/attribute-model-loader.service.ts index 4dc1e940a199..6defe9678265 100644 --- a/frontend/src/app/shared/components/fields/macros/attribute-model-loader.service.ts +++ b/frontend/src/app/shared/components/fields/macros/attribute-model-loader.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ Ng1FieldControlsWrapper, -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { firstValueFrom, @@ -51,6 +51,11 @@ export type SupportedAttributeModels = 'project'|'workPackage'; @Injectable({ providedIn: 'root' }) export class AttributeModelLoaderService { + readonly apiV3Service = inject(ApiV3Service); + readonly transitions = inject(TransitionService); + readonly currentProject = inject(CurrentProjectService); + readonly I18n = inject(I18nService); + text = { not_found: this.I18n.t('js.editor.macro.attribute_reference.not_found'), }; @@ -59,10 +64,9 @@ export class AttributeModelLoaderService { // we may need to expensively filter for them private cache$ = multiInput(); - constructor(readonly apiV3Service:ApiV3Service, - readonly transitions:TransitionService, - readonly currentProject:CurrentProjectService, - readonly I18n:I18nService) { + constructor() { + const transitions = this.transitions; + // Clear cached values whenever leaving the page transitions.onStart({}, () => { this.cache$.clear(); diff --git a/frontend/src/app/shared/components/fields/macros/attribute-value-macro.component.ts b/frontend/src/app/shared/components/fields/macros/attribute-value-macro.component.ts index 10f95d80d7d2..5da60be70b60 100644 --- a/frontend/src/app/shared/components/fields/macros/attribute-value-macro.component.ts +++ b/frontend/src/app/shared/components/fields/macros/attribute-value-macro.component.ts @@ -26,16 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ Ng1FieldControlsWrapper, -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - HostBinding, - Injector, - OnInit, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostBinding, Injector, OnInit, ViewChild, inject } from '@angular/core'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; import { @@ -63,6 +54,14 @@ export const ATTRIBUTE_MACRO_CLASS = 'op-attribute-value-macro'; standalone: false, }) export class AttributeValueMacroComponent implements OnInit { + readonly elementRef = inject(ElementRef); + readonly injector = inject(Injector); + readonly resourceLoader = inject(AttributeModelLoaderService); + readonly schemaCache = inject(SchemaCacheService); + readonly displayField = inject(DisplayFieldService); + readonly I18n = inject(I18nService); + readonly cdRef = inject(ChangeDetectorRef); + @ViewChild('displayContainer') private displayContainer:ElementRef; // Whether the value could not be loaded @@ -81,17 +80,6 @@ export class AttributeValueMacroComponent implements OnInit { fieldName:string; - constructor( - readonly elementRef:ElementRef, - readonly injector:Injector, - readonly resourceLoader:AttributeModelLoaderService, - readonly schemaCache:SchemaCacheService, - readonly displayField:DisplayFieldService, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - ) { - } - ngOnInit():void { const element = this.elementRef.nativeElement as HTMLElement; const model = element.dataset.model as SupportedAttributeModels; diff --git a/frontend/src/app/shared/components/fields/macros/work-package-quickinfo-macro.component.ts b/frontend/src/app/shared/components/fields/macros/work-package-quickinfo-macro.component.ts index e2d605349d82..20ce537537db 100644 --- a/frontend/src/app/shared/components/fields/macros/work-package-quickinfo-macro.component.ts +++ b/frontend/src/app/shared/components/fields/macros/work-package-quickinfo-macro.component.ts @@ -26,14 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ Ng1FieldControlsWrapper, -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - HostBinding, - Injector, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostBinding, Injector, OnInit, inject } from '@angular/core'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @@ -59,6 +52,15 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service standalone: false, }) export class WorkPackageQuickinfoMacroComponent implements OnInit { + readonly elementRef = inject(ElementRef); + readonly injector = inject(Injector); + readonly apiV3Service = inject(ApiV3Service); + readonly schemaCache = inject(SchemaCacheService); + readonly displayField = inject(DisplayFieldService); + readonly pathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); + readonly cdRef = inject(ChangeDetectorRef); + // Whether the value could not be loaded error:string|null = null; @@ -78,17 +80,6 @@ export class WorkPackageQuickinfoMacroComponent implements OnInit { detailed = false; - constructor(readonly elementRef:ElementRef, - readonly injector:Injector, - readonly apiV3Service:ApiV3Service, - readonly schemaCache:SchemaCacheService, - readonly displayField:DisplayFieldService, - readonly pathHelper:PathHelperService, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - ) { - } - ngOnInit() { const element = this.elementRef.nativeElement as HTMLElement; const id:string = element.dataset.id!; diff --git a/frontend/src/app/shared/components/fields/openproject-fields.module.ts b/frontend/src/app/shared/components/fields/openproject-fields.module.ts index 7e4bf930ca29..ae86b9f18535 100644 --- a/frontend/src/app/shared/components/fields/openproject-fields.module.ts +++ b/frontend/src/app/shared/components/fields/openproject-fields.module.ts @@ -131,6 +131,4 @@ import { FormsModule } from '@angular/forms'; schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class OpenprojectFieldsModule { - constructor(injector:Injector) { - } } diff --git a/frontend/src/app/shared/components/grids/grid/area.service.ts b/frontend/src/app/shared/components/grids/grid/area.service.ts index 0e62466b9cd7..6ed8e5a87d0e 100644 --- a/frontend/src/app/shared/components/grids/grid/area.service.ts +++ b/frontend/src/app/shared/components/grids/grid/area.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { GridWidgetArea } from 'core-app/shared/components/grids/areas/grid-widget-area'; import { GridArea } from 'core-app/shared/components/grids/areas/grid-area'; import { GridGap } from 'core-app/shared/components/grids/areas/grid-gap'; @@ -26,6 +26,10 @@ interface GridPatchPayload { @Injectable() export class GridAreaService { + private apiV3Service = inject(ApiV3Service); + private toastService = inject(ToastService); + private i18n = inject(I18nService); + private resource:GridResource; public schema:SchemaResource; @@ -50,13 +54,6 @@ export class GridAreaService { public helpMode = false; - constructor( - private apiV3Service:ApiV3Service, - private toastService:ToastService, - private i18n:I18nService, - ) { - } - public set gridResource(value:GridResource) { this.resource = value; this.fetchSchema(); diff --git a/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts b/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts index 7c130915d364..4007f181a98c 100644 --- a/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts +++ b/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts @@ -1,4 +1,4 @@ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { GridWidgetArea } from 'core-app/shared/components/grids/areas/grid-widget-area'; import { GridArea } from 'core-app/shared/components/grids/areas/grid-area'; import { GridAreaService } from 'core-app/shared/components/grids/grid/area.service'; @@ -8,6 +8,9 @@ import { distinctUntilChanged, filter, throttleTime } from 'rxjs/operators'; @Injectable() export class GridDragAndDropService implements OnDestroy { + readonly layout = inject(GridAreaService); + readonly move = inject(GridMoveService); + public draggedArea:GridWidgetArea|null; public placeholderArea:GridWidgetArea|null; @@ -16,8 +19,7 @@ export class GridDragAndDropService implements OnDestroy { private mousedOverAreaObserver:Subscription; - constructor(readonly layout:GridAreaService, - readonly move:GridMoveService) { + constructor() { // ngOnInit is not called on services this.setupMousedOverAreaSubscription(); } diff --git a/frontend/src/app/shared/components/grids/grid/grid.component.ts b/frontend/src/app/shared/components/grids/grid/grid.component.ts index 868383dabc60..4c0c42c60dd4 100644 --- a/frontend/src/app/shared/components/grids/grid/grid.component.ts +++ b/frontend/src/app/shared/components/grids/grid/grid.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentRef, HostListener, Input, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentRef, HostListener, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { GridResource } from 'core-app/features/hal/resources/grid-resource'; import { DomSanitizer } from '@angular/platform-browser'; import { GridWidgetsService } from 'core-app/shared/components/grids/widgets/widgets.service'; @@ -41,6 +41,16 @@ export const GRID_PROVIDERS = [ changeDetection: ChangeDetectionStrategy.Default, }) export class GridComponent implements OnDestroy, OnInit { + private sanitization = inject(DomSanitizer); + private widgetsService = inject(GridWidgetsService); + drag = inject(GridDragAndDropService); + resize = inject(GridResizeService); + layout = inject(GridAreaService); + add = inject(GridAddWidgetService); + remove = inject(GridRemoveWidgetService); + readonly browserDetector = inject(BrowserDetector); + readonly cdRef = inject(ChangeDetectorRef); + public uiWidgets:ComponentRef[] = []; public GRID_AREA_HEIGHT = 'auto'; @@ -51,18 +61,6 @@ export class GridComponent implements OnDestroy, OnInit { @Input() grid:GridResource; - constructor(private sanitization:DomSanitizer, - private widgetsService:GridWidgetsService, - public drag:GridDragAndDropService, - public resize:GridResizeService, - public layout:GridAreaService, - public add:GridAddWidgetService, - public remove:GridRemoveWidgetService, - readonly browserDetector:BrowserDetector, - readonly cdRef:ChangeDetectorRef, - ) { - } - ngOnInit() { this.layout.gridResource = this.grid; } diff --git a/frontend/src/app/shared/components/grids/grid/initialization.service.ts b/frontend/src/app/shared/components/grids/grid/initialization.service.ts index 2216f721124b..9ee7a880cc72 100644 --- a/frontend/src/app/shared/components/grids/grid/initialization.service.ts +++ b/frontend/src/app/shared/components/grids/grid/initialization.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { GridResource } from 'core-app/features/hal/resources/grid-resource'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; @@ -10,10 +10,9 @@ import { @Injectable() export class GridInitializationService { - constructor( - readonly apiV3Service:ApiV3Service, - readonly halResourceService:HalResourceService) { - } + readonly apiV3Service = inject(ApiV3Service); + readonly halResourceService = inject(HalResourceService); + // If a page with the current page exists (scoped to the current user by the backend) // that page will be used to initialized the grid. diff --git a/frontend/src/app/shared/components/grids/grid/move.service.ts b/frontend/src/app/shared/components/grids/grid/move.service.ts index b61af3aa8d6a..f78d7e486ed8 100644 --- a/frontend/src/app/shared/components/grids/grid/move.service.ts +++ b/frontend/src/app/shared/components/grids/grid/move.service.ts @@ -1,10 +1,11 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { GridWidgetArea } from 'core-app/shared/components/grids/areas/grid-widget-area'; import { GridAreaService } from 'core-app/shared/components/grids/grid/area.service'; @Injectable() export class GridMoveService { - constructor(private layout:GridAreaService) {} + private layout = inject(GridAreaService); + public down(movedArea:GridWidgetArea|null, ignoreArea:GridWidgetArea) { const movedAreas:GridWidgetArea[] = []; diff --git a/frontend/src/app/shared/components/grids/grid/resize.service.ts b/frontend/src/app/shared/components/grids/grid/resize.service.ts index 621a7596a56c..38a6f7552ee7 100644 --- a/frontend/src/app/shared/components/grids/grid/resize.service.ts +++ b/frontend/src/app/shared/components/grids/grid/resize.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { GridWidgetArea } from 'core-app/shared/components/grids/areas/grid-widget-area'; import { GridArea } from 'core-app/shared/components/grids/areas/grid-area'; import { GridAreaService } from 'core-app/shared/components/grids/grid/area.service'; @@ -8,14 +8,14 @@ import { GridDragAndDropService } from 'core-app/shared/components/grids/grid/dr @Injectable() export class GridResizeService { + readonly layout = inject(GridAreaService); + readonly move = inject(GridMoveService); + readonly drag = inject(GridDragAndDropService); + private resizedArea:GridWidgetArea|null; private targetIds:string[]; - constructor(readonly layout:GridAreaService, - readonly move:GridMoveService, - readonly drag:GridDragAndDropService) { } - public end(area:GridWidgetArea):Promise|undefined { if (!this.resizedArea) { return undefined; diff --git a/frontend/src/app/shared/components/grids/widgets/abstract-widget.component.ts b/frontend/src/app/shared/components/grids/widgets/abstract-widget.component.ts index 180c76544498..d875ec77f36a 100644 --- a/frontend/src/app/shared/components/grids/widgets/abstract-widget.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/abstract-widget.component.ts @@ -49,9 +49,6 @@ export abstract class AbstractWidgetComponent extends UntilDestroyedMixin { return true; } - /** Inserted by Angular inject() migration for backwards compatibility */ - constructor(...args:unknown[]); - constructor() { super(); } diff --git a/frontend/src/app/shared/components/grids/widgets/add/add.modal.ts b/frontend/src/app/shared/components/grids/widgets/add/add.modal.ts index dd6a474db128..80fdde916d78 100644 --- a/frontend/src/app/shared/components/grids/widgets/add/add.modal.ts +++ b/frontend/src/app/shared/components/grids/widgets/add/add.modal.ts @@ -1,9 +1,5 @@ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { WidgetRegistration } from 'core-app/shared/components/grids/grid/grid.component'; import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; @@ -21,6 +17,11 @@ import { filter, take } from 'rxjs/operators'; standalone: false, }) export class AddGridWidgetModalComponent extends OpModalComponent implements OnInit { + readonly widgetsService = inject(GridWidgetsService); + readonly i18n = inject(I18nService); + readonly bannerService = inject(BannersService); + readonly loadingIndicator = inject(LoadingIndicatorService); + text = { title: this.i18n.t('js.grid.add_widget'), close_popup: this.i18n.t('js.button_close'), @@ -33,18 +34,6 @@ export class AddGridWidgetModalComponent extends OpModalComponent implements OnI private schema:SchemaResource; - constructor( - readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) readonly locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly widgetsService:GridWidgetsService, - readonly i18n:I18nService, - readonly bannerService:BannersService, - readonly loadingIndicator:LoadingIndicatorService, - ) { - super(locals, cdRef, elementRef); - } - ngOnInit() { super.ngOnInit(); this.fetchSchema(); diff --git a/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text-edit-field.service.ts b/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text-edit-field.service.ts index 7b852cc5c275..c712d3d093d7 100644 --- a/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text-edit-field.service.ts +++ b/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text-edit-field.service.ts @@ -1,5 +1,5 @@ import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; -import { ElementRef, Injectable, Injector } from '@angular/core'; +import { ElementRef, Injectable, Injector, inject } from '@angular/core'; import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; import { BehaviorSubject, Subject } from 'rxjs'; import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; @@ -13,6 +13,11 @@ import { HalSource } from 'core-app/features/hal/interfaces'; @Injectable() export class CustomTextEditFieldService extends EditFieldHandler { + protected elementRef = inject(ElementRef); + protected injector = inject(Injector); + protected halResource = inject(HalResourceService); + protected schemaCache = inject(SchemaCacheService); + public fieldName = 'text'; public valueChanged$:BehaviorSubject; @@ -23,15 +28,6 @@ export class CustomTextEditFieldService extends EditFieldHandler { public active:boolean; - constructor( - protected elementRef:ElementRef, - protected injector:Injector, - protected halResource:HalResourceService, - protected schemaCache:SchemaCacheService, - ) { - super(); - } - public initialize(value:GridWidgetResource) { this.initializeChangeset(value); this.valueChanged$ = new BehaviorSubject(value.options.text as string); diff --git a/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text.component.ts b/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text.component.ts index a1e0608f778e..249048a27558 100644 --- a/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text.component.ts @@ -1,21 +1,8 @@ import { AbstractWidgetComponent } from 'core-app/shared/components/grids/widgets/abstract-widget.component'; -import { - ApplicationRef, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Injector, - OnChanges, - OnDestroy, - OnInit, - SimpleChanges, - ViewChild, -} from '@angular/core'; +import { ApplicationRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild, inject } from '@angular/core'; import { CustomTextEditFieldService, } from 'core-app/shared/components/grids/widgets/custom-text/custom-text-edit-field.service'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { filter } from 'rxjs/operators'; import { GridAreaService } from 'core-app/shared/components/grids/grid/area.service'; @@ -30,29 +17,23 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; standalone: false, }) export class WidgetCustomTextComponent extends AbstractWidgetComponent implements OnInit, OnChanges, OnDestroy { + handler = inject(CustomTextEditFieldService); + protected cdr = inject(ChangeDetectorRef); + protected sanitization = inject(DomSanitizer); + protected appRef = inject(ApplicationRef); + protected layout = inject(GridAreaService); + protected currentRawText:string; public customText:SafeHtml; - public text = { - attachments: this.I18n.t('js.label_attachments'), - }; + public text!:{ attachments:string }; @ViewChild('displayContainer') readonly displayContainer:ElementRef; - constructor( - protected I18n:I18nService, - protected injector:Injector, - public handler:CustomTextEditFieldService, - protected cdr:ChangeDetectorRef, - protected sanitization:DomSanitizer, - protected appRef:ApplicationRef, - protected layout:GridAreaService, - ) { - super(I18n, injector); - } - ngOnInit():void { + this.text = { attachments: this.i18n.t('js.label_attachments') }; + this.setupVariables(true); this @@ -96,7 +77,7 @@ export class WidgetCustomTextComponent extends AbstractWidgetComponent implement } public get placeholderText() { - return this.I18n.t('js.grid.widgets.work_packages_overview.placeholder'); + return this.i18n.t('js.grid.widgets.work_packages_overview.placeholder'); } public get inplaceEditClasses() { diff --git a/frontend/src/app/shared/components/grids/widgets/documents/documents.component.ts b/frontend/src/app/shared/components/grids/widgets/documents/documents.component.ts index 36c4fde4501e..7b3f57ffef42 100644 --- a/frontend/src/app/shared/components/grids/widgets/documents/documents.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/documents/documents.component.ts @@ -1,13 +1,5 @@ import { AbstractWidgetComponent } from 'core-app/shared/components/grids/widgets/abstract-widget.component'; -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - Injector, - OnInit, - SecurityContext, -} from '@angular/core'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, SecurityContext, inject } from '@angular/core'; import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -24,6 +16,14 @@ import { DocumentResource } from '../../../../../../../../modules/documents/fron standalone: false, }) export class WidgetDocumentsComponent extends AbstractWidgetComponent implements OnInit { + readonly halResource = inject(HalResourceService); + readonly pathHelper = inject(PathHelperService); + readonly apiV3Service = inject(ApiV3Service); + readonly timezone = inject(TimezoneService); + readonly domSanitizer = inject(DomSanitizer); + readonly currentProject = inject(CurrentProjectService); + readonly cdr = inject(ChangeDetectorRef); + public text = { noResults: this.i18n.t('js.grid.widgets.documents.no_results'), project: this.i18n.t('js.label_project'), @@ -33,18 +33,6 @@ export class WidgetDocumentsComponent extends AbstractWidgetComponent implements private entriesLoaded = false; - constructor(readonly halResource:HalResourceService, - readonly pathHelper:PathHelperService, - readonly apiV3Service:ApiV3Service, - readonly i18n:I18nService, - readonly timezone:TimezoneService, - readonly domSanitizer:DomSanitizer, - protected readonly injector:Injector, - readonly currentProject:CurrentProjectService, - readonly cdr:ChangeDetectorRef) { - super(i18n, injector); - } - ngOnInit() { this.halResource .get(this.documentsUrl) diff --git a/frontend/src/app/shared/components/grids/widgets/header/header.component.ts b/frontend/src/app/shared/components/grids/widgets/header/header.component.ts index 7e8f18ef9e10..0da7faea7f2c 100644 --- a/frontend/src/app/shared/components/grids/widgets/header/header.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/header/header.component.ts @@ -26,9 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, Component, EventEmitter, Input, Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { GridAreaService } from 'core-app/shared/components/grids/grid/area.service'; import { GridDragAndDropService } from 'core-app/shared/components/grids/grid/drag-and-drop.service'; @@ -40,19 +38,15 @@ import { GridDragAndDropService } from 'core-app/shared/components/grids/grid/dr standalone: false, }) export class WidgetHeaderComponent { + readonly layout = inject(GridAreaService); + readonly drag = inject(GridDragAndDropService); + @Input() name:string; @Input() editable = true; @Output() onRenamed = new EventEmitter(); - constructor( - readonly layout:GridAreaService, - readonly drag:GridDragAndDropService, - ) { - - } - public renamed(name:string) { this.onRenamed.emit(name); } diff --git a/frontend/src/app/shared/components/grids/widgets/members/members.component.ts b/frontend/src/app/shared/components/grids/widgets/members/members.component.ts index 3e7b2b125ad3..5efbe13ec147 100644 --- a/frontend/src/app/shared/components/grids/widgets/members/members.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/members/members.component.ts @@ -1,10 +1,5 @@ -import { - ChangeDetectionStrategy, - Component, - Injector, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { AbstractTurboWidgetComponent } from 'core-app/shared/components/grids/widgets/abstract-turbo-widget.component'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; @@ -15,20 +10,15 @@ import { CurrentUserService } from 'core-app/core/current-user/current-user.serv standalone: false }) export class WidgetMembersComponent extends AbstractTurboWidgetComponent { - text = { - missing_permission: this.I18n.t('js.grid.widgets.missing_permission'), - }; + protected readonly currentProject = inject(CurrentProjectService); + protected readonly currentUser = inject(CurrentUserService); - hasCapability$ = this.currentUser.hasCapabilities$('memberships/read', this.currentProject.id); - constructor( - protected readonly I18n:I18nService, - protected readonly injector:Injector, - protected readonly currentProject:CurrentProjectService, - protected readonly currentUser:CurrentUserService, - ) { - super(I18n, injector); + get text() { + return { missing_permission: this.i18n.t('js.grid.widgets.missing_permission') }; } + hasCapability$ = this.currentUser.hasCapabilities$('memberships/read', this.currentProject.id); + public get projectIdentifier() { return this.currentProject.identifier; } diff --git a/frontend/src/app/shared/components/grids/widgets/menu/widget-abstract-menu.component.ts b/frontend/src/app/shared/components/grids/widgets/menu/widget-abstract-menu.component.ts index d0c2ce5a6a30..5a6173aa5756 100644 --- a/frontend/src/app/shared/components/grids/widgets/menu/widget-abstract-menu.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/menu/widget-abstract-menu.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Directive, Injector, Input } from '@angular/core'; +import { Directive, Injector, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types'; import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; @@ -35,13 +35,12 @@ import { GridAreaService } from 'core-app/shared/components/grids/grid/area.serv @Directive() export abstract class WidgetAbstractMenuComponent { - @Input() resource:GridWidgetResource; + readonly injector = inject(Injector); + readonly i18n = inject(I18nService); + protected readonly remove = inject(GridRemoveWidgetService); + protected readonly layout = inject(GridAreaService); - constructor(readonly injector:Injector, - readonly i18n:I18nService, - protected readonly remove:GridRemoveWidgetService, - protected readonly layout:GridAreaService) { - } + @Input() resource:GridWidgetResource; public get menuItemsFactory():() => Promise { return this.buildItems.bind(this); diff --git a/frontend/src/app/shared/components/grids/widgets/project-favorites/widget-project-favorites.component.ts b/frontend/src/app/shared/components/grids/widgets/project-favorites/widget-project-favorites.component.ts index c506840db024..d70a58c21944 100644 --- a/frontend/src/app/shared/components/grids/widgets/project-favorites/widget-project-favorites.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/project-favorites/widget-project-favorites.component.ts @@ -1,14 +1,5 @@ import { AbstractWidgetComponent } from 'core-app/shared/components/grids/widgets/abstract-widget.component'; -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - HostBinding, - Injector, - OnInit, - ViewEncapsulation, -} from '@angular/core'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @@ -26,6 +17,13 @@ import { Observable } from 'rxjs'; standalone: false, }) export class WidgetProjectFavoritesComponent extends AbstractWidgetComponent implements OnInit { + readonly halResource = inject(HalResourceService); + readonly pathHelper = inject(PathHelperService); + readonly timezone = inject(TimezoneService); + readonly apiV3Service = inject(ApiV3Service); + readonly currentProject = inject(CurrentProjectService); + readonly cdr = inject(ChangeDetectorRef); + @HostBinding('class.op-widget-project-favorites') className = true; public text = { @@ -35,19 +33,6 @@ export class WidgetProjectFavoritesComponent extends AbstractWidgetComponent imp public projects$:Observable; - constructor( - readonly halResource:HalResourceService, - readonly pathHelper:PathHelperService, - readonly i18n:I18nService, - protected readonly injector:Injector, - readonly timezone:TimezoneService, - readonly apiV3Service:ApiV3Service, - readonly currentProject:CurrentProjectService, - readonly cdr:ChangeDetectorRef, - ) { - super(i18n, injector); - } - ngOnInit() { const filters = new ApiV3FilterBuilder(); filters.add('favorited', '=', true); diff --git a/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/configuration-modal/configuration.modal.ts b/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/configuration-modal/configuration.modal.ts index f86c0ab9b6e3..746bbaec6d4b 100644 --- a/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/configuration-modal/configuration.modal.ts +++ b/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/configuration-modal/configuration.modal.ts @@ -1,9 +1,5 @@ -import { - ApplicationRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, Injector, OnInit, -} from '@angular/core'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { ApplicationRef, ChangeDetectionStrategy, Component, Injector, OnInit, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -20,6 +16,14 @@ import { TimeEntriesCurrentUserConfigurationModalService } from 'core-app/shared changeDetection: ChangeDetectionStrategy.Default, }) export class TimeEntriesCurrentUserConfigurationModalComponent extends OpModalComponent implements OnInit { + readonly I18n = inject(I18nService); + readonly injector = inject(Injector); + readonly appRef = inject(ApplicationRef); + readonly loadingIndicator = inject(LoadingIndicatorService); + readonly notificationService = inject(WorkPackageNotificationService); + readonly configuration = inject(ConfigurationService); + readonly timeEntriesCurrentUserConfigurationModalService = inject(TimeEntriesCurrentUserConfigurationModalService); + public text = { displayedDays: this.I18n.t('js.grid.widgets.time_entries_current_user.displayed_days'), closePopup: this.I18n.t('js.close_popup_title'), @@ -36,19 +40,6 @@ export class TimeEntriesCurrentUserConfigurationModalComponent extends OpModalCo public days:IDayData[]; - constructor(@Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly I18n:I18nService, - readonly injector:Injector, - readonly appRef:ApplicationRef, - readonly loadingIndicator:LoadingIndicatorService, - readonly notificationService:WorkPackageNotificationService, - readonly cdRef:ChangeDetectorRef, - readonly configuration:ConfigurationService, - readonly elementRef:ElementRef, - readonly timeEntriesCurrentUserConfigurationModalService:TimeEntriesCurrentUserConfigurationModalService) { - super(locals, cdRef, elementRef); - } - ngOnInit() { const localDayOptions = this.locals.options.days; this.daysOriginalCheckedValues = localDayOptions.length > 0 ? localDayOptions : Array.from({ length: 7 }, () => true); diff --git a/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/time-entries-current-user.component.ts b/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/time-entries-current-user.component.ts index 0dd1265113ae..c421a83125ab 100644 --- a/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/time-entries-current-user.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/time-entries-current-user.component.ts @@ -1,9 +1,6 @@ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, inject } from '@angular/core'; import { TimeEntryResource } from 'core-app/features/hal/resources/time-entry-resource'; import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { AbstractWidgetComponent } from 'core-app/shared/components/grids/widgets/abstract-widget.component'; import { DisplayedDays } from 'core-app/features/calendar/te-calendar/te-calendar.component'; @@ -16,18 +13,14 @@ import { TimezoneService } from 'core-app/core/datetime/timezone.service'; standalone: false, }) export class WidgetTimeEntriesCurrentUserComponent extends AbstractWidgetComponent implements OnInit { + readonly timezone = inject(TimezoneService); + readonly pathHelper = inject(PathHelperService); + protected readonly cdr = inject(ChangeDetectorRef); + public entries:TimeEntryResource[] = []; public displayedDays:DisplayedDays; - constructor(protected readonly injector:Injector, - readonly timezone:TimezoneService, - readonly i18n:I18nService, - readonly pathHelper:PathHelperService, - protected readonly cdr:ChangeDetectorRef) { - super(i18n, injector); - } - public ngOnInit() { this.displayedDays = this.resource.options.days as DisplayedDays; } diff --git a/frontend/src/app/shared/components/grids/widgets/time-entries/list/time-entries-list.component.ts b/frontend/src/app/shared/components/grids/widgets/time-entries/list/time-entries-list.component.ts index c10f6b2e324b..6e49b9281886 100644 --- a/frontend/src/app/shared/components/grids/widgets/time-entries/list/time-entries-list.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/time-entries/list/time-entries-list.component.ts @@ -1,11 +1,4 @@ -import { - AfterViewInit, - ChangeDetectorRef, - Directive, - Injector, - OnDestroy, - OnInit, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, Directive, Injector, OnDestroy, OnInit, inject } from '@angular/core'; import { AbstractWidgetComponent } from 'core-app/shared/components/grids/widgets/abstract-widget.component'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -27,6 +20,13 @@ import { MeetingResource } from 'core-app/features/hal/resources/meeting-resourc @Directive() export abstract class WidgetTimeEntriesListComponent extends AbstractWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + readonly injector = inject(Injector); + readonly timezone = inject(TimezoneService); + readonly i18n = inject(I18nService); + readonly pathHelper = inject(PathHelperService); + readonly confirmDialog = inject(ConfirmDialogService); + protected readonly cdr = inject(ChangeDetectorRef); + public text = { edit: this.i18n.t('js.button_edit'), delete: this.i18n.t('js.button_delete'), @@ -51,17 +51,6 @@ export abstract class WidgetTimeEntriesListComponent extends AbstractWidgetCompo @InjectField() public readonly apiV3Service:ApiV3Service; @InjectField() public readonly turboRequests:TurboRequestsService; - constructor( - readonly injector:Injector, - readonly timezone:TimezoneService, - readonly i18n:I18nService, - readonly pathHelper:PathHelperService, - readonly confirmDialog:ConfirmDialogService, - protected readonly cdr:ChangeDetectorRef, - ) { - super(i18n, injector); - } - ngOnInit():void { this.loadTimeEntries(); } diff --git a/frontend/src/app/shared/components/grids/widgets/time-entries/project/time-entries-project.component.ts b/frontend/src/app/shared/components/grids/widgets/time-entries/project/time-entries-project.component.ts index bcd54ca13848..75cc35c04e17 100644 --- a/frontend/src/app/shared/components/grids/widgets/time-entries/project/time-entries-project.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/time-entries/project/time-entries-project.component.ts @@ -1,17 +1,7 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - Injector, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core'; import { WidgetTimeEntriesListComponent } from 'core-app/shared/components/grids/widgets/time-entries/list/time-entries-list.component'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; -import { TimezoneService } from 'core-app/core/datetime/timezone.service'; -import { ConfirmDialogService } from 'core-app/shared/components/modals/confirm-dialog/confirm-dialog.service'; import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; @Component({ @@ -26,15 +16,7 @@ import { HalResourceEditingService } from 'core-app/shared/components/fields/edi changeDetection: ChangeDetectionStrategy.Default, }) export class WidgetTimeEntriesProjectComponent extends WidgetTimeEntriesListComponent implements OnInit { - constructor(readonly injector:Injector, - readonly timezone:TimezoneService, - readonly i18n:I18nService, - readonly pathHelper:PathHelperService, - readonly confirmDialog:ConfirmDialogService, - protected readonly cdr:ChangeDetectorRef, - protected readonly currentProject:CurrentProjectService) { - super(injector, timezone, i18n, pathHelper, confirmDialog, cdr); - } + protected readonly currentProject = inject(CurrentProjectService); protected dmFilters():[string, FilterOperator, [string]][] { return [['spentOn', '>t-', ['7']] as [string, FilterOperator, [string]], diff --git a/frontend/src/app/shared/components/grids/widgets/wp-calendar/wp-calendar.component.ts b/frontend/src/app/shared/components/grids/widgets/wp-calendar/wp-calendar.component.ts index 3b8585606fe2..9db3668dd27f 100644 --- a/frontend/src/app/shared/components/grids/widgets/wp-calendar/wp-calendar.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/wp-calendar/wp-calendar.component.ts @@ -26,9 +26,8 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Injector } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { AbstractWidgetComponent } from 'core-app/shared/components/grids/widgets/abstract-widget.component'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { WorkPackageIsolatedQuerySpaceDirective, @@ -42,21 +41,15 @@ import { CurrentUserService } from 'core-app/core/current-user/current-user.serv standalone: false, }) export class WidgetWpCalendarComponent extends AbstractWidgetComponent { - text = { - missing_permission: this.I18n.t('js.grid.widgets.missing_permission'), - }; + protected readonly currentProject = inject(CurrentProjectService); + protected readonly currentUser = inject(CurrentUserService); - hasCapability$ = this.currentUser.hasCapabilities$('work_packages/read', this.currentProject.id); - - constructor( - protected readonly I18n:I18nService, - protected readonly injector:Injector, - protected readonly currentProject:CurrentProjectService, - protected readonly currentUser:CurrentUserService, - ) { - super(I18n, injector); + get text() { + return { missing_permission: this.i18n.t('js.grid.widgets.missing_permission') }; } + hasCapability$ = this.currentUser.hasCapabilities$('work_packages/read', this.currentProject.id); + public get projectIdentifier() { return this.currentProject.identifier; } diff --git a/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.ts b/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.ts index 2ff6bebd2e7c..89a38520eb63 100644 --- a/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph.component.ts @@ -1,8 +1,5 @@ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnDestroy, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { WorkPackageEmbeddedGraphDataset } from 'core-app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; import { AbstractWidgetComponent } from 'core-app/shared/components/grids/widgets/abstract-widget.component'; import { ChartOptions } from 'chart.js'; import { WpGraphConfigurationService } from 'core-app/shared/components/work-package-graphs/configuration/wp-graph-configuration.service'; @@ -17,14 +14,10 @@ import { WpGraphConfiguration } from 'core-app/shared/components/work-package-gr standalone: false, }) export class WidgetWpGraphComponent extends AbstractWidgetComponent implements OnInit, OnDestroy { - public datasets:WorkPackageEmbeddedGraphDataset[] = []; + protected cdr = inject(ChangeDetectorRef); + protected readonly graphConfiguration = inject(WpGraphConfigurationService); - constructor(protected i18n:I18nService, - protected injector:Injector, - protected cdr:ChangeDetectorRef, - protected readonly graphConfiguration:WpGraphConfigurationService) { - super(i18n, injector); - } + public datasets:WorkPackageEmbeddedGraphDataset[] = []; ngOnInit() { this.initializeConfiguration(); diff --git a/frontend/src/app/shared/components/header-project-select/header-project-select.component.ts b/frontend/src/app/shared/components/header-project-select/header-project-select.component.ts index c01fc2de5479..96eddcf953eb 100644 --- a/frontend/src/app/shared/components/header-project-select/header-project-select.component.ts +++ b/frontend/src/app/shared/components/header-project-select/header-project-select.component.ts @@ -28,7 +28,7 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { ChangeDetectionStrategy, Component, HostBinding, OnDestroy, OnInit, ViewEncapsulation, ElementRef, ViewChild, AfterViewInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, OnDestroy, OnInit, ViewEncapsulation, ElementRef, ViewChild, AfterViewInit, inject } from '@angular/core'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { BehaviorSubject, Observable, ReplaySubject, Subscription } from 'rxjs'; import { map, shareReplay, take, tap } from 'rxjs/operators'; @@ -56,6 +56,14 @@ import { ConfigurationService } from 'core-app/core/config/configuration.service standalone: false, }) export class OpHeaderProjectSelectComponent extends UntilDestroyedMixin implements OnInit, OnDestroy, AfterViewInit { + readonly pathHelper = inject(PathHelperService); + readonly configuration = inject(ConfigurationService); + readonly I18n = inject(I18nService); + readonly currentProject = inject(CurrentProjectService); + readonly searchableProjectListService = inject(SearchableProjectListService); + readonly currentUserService = inject(CurrentUserService); + readonly apiV3Service = inject(ApiV3Service); + @HostBinding('class.op-project-select') className = true; @ViewChild('projectSearchField', { read: ElementRef }) @@ -161,15 +169,7 @@ export class OpHeaderProjectSelectComponent extends UntilDestroyedMixin implemen private displayModeLocalStorageKey = 'openProject-project-select-display-mode'; - constructor( - readonly pathHelper:PathHelperService, - readonly configuration:ConfigurationService, - readonly I18n:I18nService, - readonly currentProject:CurrentProjectService, - readonly searchableProjectListService:SearchableProjectListService, - readonly currentUserService:CurrentUserService, - readonly apiV3Service:ApiV3Service, - ) { + constructor() { super(); if(this.currentProject.id) { diff --git a/frontend/src/app/shared/components/header-project-select/list/header-project-select-list.component.ts b/frontend/src/app/shared/components/header-project-select/list/header-project-select-list.component.ts index 8655655ef46e..9d67f1488118 100644 --- a/frontend/src/app/shared/components/header-project-select/list/header-project-select-list.component.ts +++ b/frontend/src/app/shared/components/header-project-select/list/header-project-select-list.component.ts @@ -1,17 +1,5 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - HostBinding, - Input, - OnChanges, - OnInit, - Output, - SimpleChanges, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, Input, OnChanges, OnInit, Output, SimpleChanges, inject } from '@angular/core'; import { SearchableProjectListService, } from 'core-app/shared/components/searchable-project-list/searchable-project-list.service'; @@ -29,6 +17,14 @@ import { getMetaContent } from 'core-app/core/setup/globals/global-helpers'; standalone: false, }) export class OpHeaderProjectSelectListComponent implements OnInit, OnChanges { + readonly I18n = inject(I18nService); + readonly pathHelper = inject(PathHelperService); + readonly configuration = inject(ConfigurationService); + readonly searchableProjectListService = inject(SearchableProjectListService); + readonly elementRef = inject(ElementRef); + readonly cdRef = inject(ChangeDetectorRef); + readonly currentProjectService = inject(CurrentProjectService); + @HostBinding('class.spot-list') classNameList = true; @HostBinding('class.op-header-project-select-list') className = true; @@ -64,16 +60,6 @@ export class OpHeaderProjectSelectListComponent implements OnInit, OnChanges { public portfolioModelsEnabled = this.configuration.activeFeatureFlags.includes('portfolioModels'); - constructor( - readonly I18n:I18nService, - readonly pathHelper:PathHelperService, - readonly configuration:ConfigurationService, - readonly searchableProjectListService:SearchableProjectListService, - readonly elementRef:ElementRef, - readonly cdRef:ChangeDetectorRef, - readonly currentProjectService:CurrentProjectService, - ) { } - ngOnInit():void { if (this.root) { this.searchableProjectListService.selectedItemID$.subscribe((selectedItemID) => { diff --git a/frontend/src/app/shared/components/modal/modal-overlay.component.ts b/frontend/src/app/shared/components/modal/modal-overlay.component.ts index 1d8dd4d0c16d..64bcd65198ec 100644 --- a/frontend/src/app/shared/components/modal/modal-overlay.component.ts +++ b/frontend/src/app/shared/components/modal/modal-overlay.component.ts @@ -26,14 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ComponentRef, - ElementRef, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentRef, ElementRef, ViewChild, inject } from '@angular/core'; import { CdkPortalOutlet, ComponentPortal } from '@angular/cdk/portal'; import { combineLatest } from 'rxjs'; import { distinctUntilChanged, filter } from 'rxjs/operators'; @@ -52,6 +45,10 @@ import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destr standalone: false, }) export class OpModalOverlayComponent extends UntilDestroyedMixin { + readonly modalService = inject(OpModalService); + readonly I18n = inject(I18nService); + readonly cdRef = inject(ChangeDetectorRef); + public notFullscreen = false; mobileTopPosition = false; @@ -73,14 +70,6 @@ export class OpModalOverlayComponent extends UntilDestroyedMixin { activeModalInstance$ = this.modalService.activeModalInstance$; - constructor( - readonly modalService:OpModalService, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - ) { - super(); - } - setupListener():void { combineLatest([ this.activeModalInstance$, diff --git a/frontend/src/app/shared/components/modal/modal-wrapper-augment.service.ts b/frontend/src/app/shared/components/modal/modal-wrapper-augment.service.ts index 325fb65bc772..82dc9ff0cd15 100644 --- a/frontend/src/app/shared/components/modal/modal-wrapper-augment.service.ts +++ b/frontend/src/app/shared/components/modal/modal-wrapper-augment.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Inject, Injectable, Injector, DOCUMENT } from '@angular/core'; +import { Injectable, Injector, DOCUMENT, inject } from '@angular/core'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { DynamicContentModalComponent } from 'core-app/shared/components/modals/modal-wrapper/dynamic-content.modal'; @@ -36,11 +36,14 @@ import { DynamicContentModalComponent } from 'core-app/shared/components/modals/ */ @Injectable({ providedIn: 'root' }) export class OpModalWrapperAugmentService { - constructor( - @Inject(DOCUMENT) protected documentElement:Document, - protected injector:Injector, - protected opModalService:OpModalService, - ) { + protected documentElement = inject(DOCUMENT); + protected injector = inject(Injector); + protected opModalService = inject(OpModalService); + + constructor() { + const documentElement = this.documentElement; + const opModalService = this.opModalService; + documentElement.addEventListener('turbo:before-render', () => opModalService.close()); } diff --git a/frontend/src/app/shared/components/modal/modal.component.ts b/frontend/src/app/shared/components/modal/modal.component.ts index 09ea32300b07..96b23c47acaf 100644 --- a/frontend/src/app/shared/components/modal/modal.component.ts +++ b/frontend/src/app/shared/components/modal/modal.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectorRef, Directive, ElementRef, EventEmitter, Inject, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Directive, ElementRef, EventEmitter, OnDestroy, OnInit, inject } from '@angular/core'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @@ -35,6 +35,10 @@ import { debounce } from 'lodash'; @Directive() export abstract class OpModalComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { + locals = inject(OpModalLocalsToken); + readonly cdRef = inject(ChangeDetectorRef); + readonly elementRef = inject(ElementRef); + /* Reference to service */ protected service:OpModalService = this.locals.service; @@ -51,14 +55,6 @@ export abstract class OpModalComponent extends UntilDestroyedMixin implements On /* Data to be return from this modal instance */ public data:unknown; - protected constructor( - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef, - ) { - super(); - } - ngOnInit():void { this.element = this.elementRef.nativeElement as HTMLElement; } diff --git a/frontend/src/app/shared/components/modal/modal.service.ts b/frontend/src/app/shared/components/modal/modal.service.ts index 20cd2d8e480b..d28c569d2e7f 100644 --- a/frontend/src/app/shared/components/modal/modal.service.ts +++ b/frontend/src/app/shared/components/modal/modal.service.ts @@ -27,11 +27,7 @@ //++ import { ComponentType } from '@angular/cdk/portal'; -import { - Injectable, - InjectionToken, - Injector, -} from '@angular/core'; +import { Injectable, InjectionToken, Injector, inject } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { filter, take } from 'rxjs/operators'; @@ -50,13 +46,13 @@ export interface ModalData { @Injectable({ providedIn: 'root' }) export class OpModalService { + private readonly injector = inject(Injector); + public activeModalInstance$ = new BehaviorSubject(null); public activeModalData$ = new BehaviorSubject(null); - constructor( - private readonly injector:Injector, - ) { + constructor() { // Listen to keystrokes on window to close context menus window.addEventListener('keydown', (evt:KeyboardEvent) => { if (evt.key !== 'Escape' || evt.defaultPrevented) { diff --git a/frontend/src/app/shared/components/modals/confirm-dialog/confirm-dialog.modal.ts b/frontend/src/app/shared/components/modals/confirm-dialog/confirm-dialog.modal.ts index 755b7bb67ed0..23f59d5c1328 100644 --- a/frontend/src/app/shared/components/modals/confirm-dialog/confirm-dialog.modal.ts +++ b/frontend/src/app/shared/components/modals/confirm-dialog/confirm-dialog.modal.ts @@ -26,17 +26,9 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Inject, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { I18nService } from 'core-app/core/i18n/i18n.service'; export interface ConfirmDialogOptions { @@ -69,6 +61,8 @@ export interface ConfirmDialogOptions { standalone: false, }) export class ConfirmDialogModalComponent extends OpModalComponent { + readonly I18n = inject(I18nService); + public showClose:boolean; public showListData:boolean; @@ -101,14 +95,10 @@ export class ConfirmDialogModalComponent extends OpModalComponent { public dangerHighlighting:boolean; - constructor( - readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - ) { - super(locals, cdRef, elementRef); - this.options = (locals.options || {}) as ConfirmDialogOptions; + constructor() { + super(); + + this.options = (this.locals.options ?? {}) as ConfirmDialogOptions; this.dangerHighlighting = _.defaultTo(this.options.dangerHighlighting, false); this.showListData = _.defaultTo(this.options.showListData, false); diff --git a/frontend/src/app/shared/components/modals/editor/editor-macros.service.ts b/frontend/src/app/shared/components/modals/editor/editor-macros.service.ts index 2e146315ada3..9890ca6f436a 100644 --- a/frontend/src/app/shared/components/modals/editor/editor-macros.service.ts +++ b/frontend/src/app/shared/components/modals/editor/editor-macros.service.ts @@ -27,7 +27,7 @@ //++ import { OpModalService } from 'core-app/shared/components/modal/modal.service'; -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { WpButtonMacroModalComponent, } from 'core-app/shared/components/modals/editor/macro-wp-button-modal/wp-button-macro.modal'; @@ -44,11 +44,9 @@ import { PortalOutletTarget } from 'core-app/shared/components/modal/portal-outl @Injectable() export class EditorMacrosService { - constructor( - readonly opModalService:OpModalService, - readonly injector:Injector, - ) { - } + readonly opModalService = inject(OpModalService); + readonly injector = inject(Injector); + /** * Show a modal to edit the work package button macro settings. diff --git a/frontend/src/app/shared/components/modals/editor/macro-child-pages-modal/child-pages-macro.modal.ts b/frontend/src/app/shared/components/modals/editor/macro-child-pages-modal/child-pages-macro.modal.ts index 145ae4b9cdf3..d4df749bca62 100644 --- a/frontend/src/app/shared/components/modals/editor/macro-child-pages-modal/child-pages-macro.modal.ts +++ b/frontend/src/app/shared/components/modals/editor/macro-child-pages-modal/child-pages-macro.modal.ts @@ -26,12 +26,8 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, ViewChild, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ @@ -43,6 +39,8 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class ChildPagesMacroModalComponent extends OpModalComponent implements AfterViewInit { + readonly I18n = inject(I18nService); + public changed = false; public showClose = true; @@ -67,11 +65,9 @@ export class ChildPagesMacroModalComponent extends OpModalComponent implements A close_popup: this.I18n.t('js.close_popup_title'), }; - constructor(readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService) { - super(locals, cdRef, elementRef); + constructor() { + super(); + this.selectedPage = this.page = this.locals.page; this.selectedIncludeParent = this.includeParent = this.locals.includeParent; diff --git a/frontend/src/app/shared/components/modals/editor/macro-code-block-modal/code-block-macro.modal.ts b/frontend/src/app/shared/components/modals/editor/macro-code-block-modal/code-block-macro.modal.ts index 96dc1f9f07ca..fa6d358152d4 100644 --- a/frontend/src/app/shared/components/modals/editor/macro-code-block-modal/code-block-macro.modal.ts +++ b/frontend/src/app/shared/components/modals/editor/macro-code-block-modal/code-block-macro.modal.ts @@ -27,11 +27,9 @@ //++ import { - AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, ViewChild, inject, + AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, ViewChild, inject, } from '@angular/core'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { CodeMirrorLoaderService } from 'core-app/shared/components/editor/components/ckeditor/codemirror-loader.service'; import type { Editor as CodeMirrorEditor } from 'codemirror'; @@ -66,9 +64,6 @@ export class CodeBlockMacroModalComponent extends OpModalComponent implements Af @ViewChild('codeMirrorPane', { static: true }) codeMirrorPane:ElementRef; - readonly elementRef = inject(ElementRef); - public locals = inject(OpModalLocalsToken) as OpModalLocalsMap; - readonly cdRef = inject(ChangeDetectorRef); readonly I18n = inject(I18nService); readonly codeMirrorLoader = inject(CodeMirrorLoaderService); @@ -82,11 +77,7 @@ export class CodeBlockMacroModalComponent extends OpModalComponent implements Af }; constructor() { - super( - inject(OpModalLocalsToken) as OpModalLocalsMap, - inject(ChangeDetectorRef), - inject(ElementRef), - ); + super(); this.languageClass = (this.locals.languageClass as string | undefined) ?? 'language-text'; this.content = this.locals.content as string; diff --git a/frontend/src/app/shared/components/modals/editor/macro-wiki-include-page-modal/wiki-include-page-macro.modal.ts b/frontend/src/app/shared/components/modals/editor/macro-wiki-include-page-modal/wiki-include-page-macro.modal.ts index ebc108054ec0..61a4fc4a55d5 100644 --- a/frontend/src/app/shared/components/modals/editor/macro-wiki-include-page-modal/wiki-include-page-macro.modal.ts +++ b/frontend/src/app/shared/components/modals/editor/macro-wiki-include-page-modal/wiki-include-page-macro.modal.ts @@ -26,12 +26,8 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, ViewChild, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ @@ -43,6 +39,8 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class WikiIncludePageMacroModalComponent extends OpModalComponent implements AfterViewInit { + readonly I18n = inject(I18nService); + public changed = false; public showClose = true; @@ -62,11 +60,9 @@ export class WikiIncludePageMacroModalComponent extends OpModalComponent impleme close_popup: this.I18n.t('js.close_popup_title'), }; - constructor(readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService) { - super(locals, cdRef, elementRef); + constructor() { + super(); + this.selectedPage = this.page = this.locals.page; // We could provide an autocompleter here to get correct page names diff --git a/frontend/src/app/shared/components/modals/editor/macro-wp-button-modal/wp-button-macro.modal.ts b/frontend/src/app/shared/components/modals/editor/macro-wp-button-modal/wp-button-macro.modal.ts index 38f4de54614b..e63f3300aa04 100644 --- a/frontend/src/app/shared/components/modals/editor/macro-wp-button-modal/wp-button-macro.modal.ts +++ b/frontend/src/app/shared/components/modals/editor/macro-wp-button-modal/wp-button-macro.modal.ts @@ -26,18 +26,8 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Inject, - ViewChild, -} from '@angular/core'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, ViewChild, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TypeResource } from 'core-app/features/hal/resources/type-resource'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @@ -50,6 +40,10 @@ import { FormResource } from 'core-app/features/hal/resources/form-resource'; standalone: false, }) export class WpButtonMacroModalComponent extends OpModalComponent implements AfterViewInit { + protected currentProject = inject(CurrentProjectService); + protected apiV3Service = inject(ApiV3Service); + readonly I18n = inject(I18nService); + public changed = false; public showClose = true; @@ -77,13 +71,9 @@ export class WpButtonMacroModalComponent extends OpModalComponent implements Aft close_popup: this.I18n.t('js.close_popup_title'), }; - constructor(readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - protected currentProject:CurrentProjectService, - protected apiV3Service:ApiV3Service, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService) { - super(locals, cdRef, elementRef); + constructor() { + super(); + this.selectedType = this.type = this.locals.type; this.classes = this.locals.classes; this.buttonStyle = this.classes === 'button'; diff --git a/frontend/src/app/shared/components/modals/get-ical-url-modal/query-get-ical-url.modal.ts b/frontend/src/app/shared/components/modals/get-ical-url-modal/query-get-ical-url.modal.ts index e7e57a8eb656..74bc9864f1b1 100644 --- a/frontend/src/app/shared/components/modals/get-ical-url-modal/query-get-ical-url.modal.ts +++ b/frontend/src/app/shared/components/modals/get-ical-url-modal/query-get-ical-url.modal.ts @@ -31,10 +31,8 @@ import { States } from 'core-app/core/states/states.service'; import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, inject, + ChangeDetectionStrategy, Component, OnInit, inject, } from '@angular/core'; import { UntypedFormGroup, @@ -59,12 +57,9 @@ interface TokenNameFormValue { standalone: false, }) export class QueryGetIcalUrlModalComponent extends OpModalComponent implements OnInit { - public readonly elementRef = inject(ElementRef); - public locals:OpModalLocalsMap = inject(OpModalLocalsToken); public readonly I18n = inject(I18nService); public readonly states = inject(States); public readonly querySpace = inject(IsolatedQuerySpace); - public readonly cdRef = inject(ChangeDetectorRef); public readonly wpListService = inject(WorkPackagesListService); public readonly halNotification = inject(HalResourceNotificationService); public readonly toastService = inject(ToastService); @@ -103,15 +98,6 @@ export class QueryGetIcalUrlModalComponent extends OpModalComponent implements O return this.tokenNameForm.get('name'); } - constructor() { - // Pass required deps to the base class using inject() - super( - inject(OpModalLocalsToken), - inject(ChangeDetectorRef), - inject(ElementRef), - ); - } - ngOnInit():void { super.ngOnInit(); diff --git a/frontend/src/app/shared/components/modals/modal-wrapper/dynamic-content.modal.ts b/frontend/src/app/shared/components/modals/modal-wrapper/dynamic-content.modal.ts index aaeb738273ed..3bd2f42d5a45 100644 --- a/frontend/src/app/shared/components/modals/modal-wrapper/dynamic-content.modal.ts +++ b/frontend/src/app/shared/components/modals/modal-wrapper/dynamic-content.modal.ts @@ -26,17 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Inject, - OnDestroy, - OnInit, -} from '@angular/core'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -46,14 +36,7 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; standalone: false, }) export class DynamicContentModalComponent extends OpModalComponent implements OnInit, OnDestroy { - constructor( - readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - ) { - super(locals, cdRef, elementRef); - } + readonly I18n = inject(I18nService); ngOnInit():void { super.ngOnInit(); diff --git a/frontend/src/app/shared/components/modals/save-modal/save-query.modal.ts b/frontend/src/app/shared/components/modals/save-modal/save-query.modal.ts index 4d995a893c5a..57d15b7c8707 100644 --- a/frontend/src/app/shared/components/modals/save-modal/save-query.modal.ts +++ b/frontend/src/app/shared/components/modals/save-modal/save-query.modal.ts @@ -26,15 +26,11 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, ViewChild, inject } from '@angular/core'; import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { QuerySharingChange } from 'core-app/shared/components/modals/share-modal/query-sharing-form.component'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; @@ -50,6 +46,13 @@ import { States } from 'core-app/core/states/states.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class SaveQueryModalComponent extends OpModalComponent { + readonly I18n = inject(I18nService); + readonly states = inject(States); + readonly querySpace = inject(IsolatedQuerySpace); + readonly wpListService = inject(WorkPackagesListService); + readonly halNotification = inject(HalResourceNotificationService); + readonly toastService = inject(ToastService); + public queryName = ''; public isStarred = false; @@ -71,20 +74,6 @@ export class SaveQueryModalComponent extends OpModalComponent { close_popup: this.I18n.t('js.close_popup_title'), }; - constructor( - readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly I18n:I18nService, - readonly states:States, - readonly querySpace:IsolatedQuerySpace, - readonly wpListService:WorkPackagesListService, - readonly halNotification:HalResourceNotificationService, - readonly cdRef:ChangeDetectorRef, - readonly toastService:ToastService, - ) { - super(locals, cdRef, elementRef); - } - public setValues(change:QuerySharingChange):void { this.isStarred = change.isStarred; this.isPublic = change.isPublic; diff --git a/frontend/src/app/shared/components/modals/share-modal/query-sharing-form.component.ts b/frontend/src/app/shared/components/modals/share-modal/query-sharing-form.component.ts index 85f4ebb2b8e3..8e88f54c4e64 100644 --- a/frontend/src/app/shared/components/modals/share-modal/query-sharing-form.component.ts +++ b/frontend/src/app/shared/components/modals/share-modal/query-sharing-form.component.ts @@ -1,8 +1,6 @@ import { States } from 'core-app/core/states/states.service'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; -import { - ChangeDetectionStrategy, Component, EventEmitter, Input, Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; @@ -21,6 +19,11 @@ export interface QuerySharingChange { changeDetection: ChangeDetectionStrategy.Default, }) export class QuerySharingFormComponent { + readonly states = inject(States); + readonly querySpace = inject(IsolatedQuerySpace); + readonly authorisationService = inject(AuthorisationService); + readonly I18n = inject(I18nService); + @Input() public isSave:boolean; @Input() public isStarred:boolean; @@ -37,12 +40,6 @@ export class QuerySharingFormComponent { visibleForOthersText: this.I18n.t('js.work_packages.query.public_text'), }; - constructor(readonly states:States, - readonly querySpace:IsolatedQuerySpace, - readonly authorisationService:AuthorisationService, - readonly I18n:I18nService) { - } - public get canStar() { return this.isSave || this.authorisationService.can('query', 'star') diff --git a/frontend/src/app/shared/components/modals/share-modal/query-sharing.modal.ts b/frontend/src/app/shared/components/modals/share-modal/query-sharing.modal.ts index 26f2ba6be327..cdf9e7febae9 100644 --- a/frontend/src/app/shared/components/modals/share-modal/query-sharing.modal.ts +++ b/frontend/src/app/shared/components/modals/share-modal/query-sharing.modal.ts @@ -32,11 +32,7 @@ import { HalResourceNotificationService } from 'core-app/features/hal/services/h import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core'; import { QuerySharingChange } from 'core-app/shared/components/modals/share-modal/query-sharing-form.component'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; @@ -50,6 +46,13 @@ import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/q changeDetection: ChangeDetectionStrategy.Default, }) export class QuerySharingModalComponent extends OpModalComponent implements OnInit { + readonly I18n = inject(I18nService); + readonly states = inject(States); + readonly querySpace = inject(IsolatedQuerySpace); + readonly wpListService = inject(WorkPackagesListService); + readonly halNotification = inject(HalResourceNotificationService); + readonly toastService = inject(ToastService); + public query:QueryResource; public isStarred = false; @@ -69,20 +72,6 @@ export class QuerySharingModalComponent extends OpModalComponent implements OnIn close_popup: this.I18n.t('js.close_popup_title'), }; - constructor( - readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly I18n:I18nService, - readonly states:States, - readonly querySpace:IsolatedQuerySpace, - readonly cdRef:ChangeDetectorRef, - readonly wpListService:WorkPackagesListService, - readonly halNotification:HalResourceNotificationService, - readonly toastService:ToastService, - ) { - super(locals, cdRef, elementRef); - } - ngOnInit():void { super.ngOnInit(); diff --git a/frontend/src/app/shared/components/modals/view-settings-modal/view-settings.modal.ts b/frontend/src/app/shared/components/modals/view-settings-modal/view-settings.modal.ts index 5ca35da8f1b2..b6fa3dd5aa16 100644 --- a/frontend/src/app/shared/components/modals/view-settings-modal/view-settings.modal.ts +++ b/frontend/src/app/shared/components/modals/view-settings-modal/view-settings.modal.ts @@ -26,13 +26,11 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, ViewChild, inject } from '@angular/core'; import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { QuerySharingChange } from 'core-app/shared/components/modals/share-modal/query-sharing-form.component'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; @@ -48,6 +46,14 @@ import { WorkPackagesQueryViewService } from 'core-app/features/work-packages/co changeDetection: ChangeDetectionStrategy.Default, }) export class ViewSettingsModalComponent extends OpModalComponent { + readonly I18n = inject(I18nService); + readonly states = inject(States); + readonly querySpace = inject(IsolatedQuerySpace); + readonly wpListService = inject(WorkPackagesListService); + readonly wpView = inject(WorkPackagesQueryViewService); + readonly halNotification = inject(HalResourceNotificationService); + readonly toastService = inject(ToastService); + public queryName = ''; public isStarred = false; @@ -69,21 +75,6 @@ export class ViewSettingsModalComponent extends OpModalComponent { close_popup: this.I18n.t('js.close_popup_title'), }; - constructor( - readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly I18n:I18nService, - readonly states:States, - readonly querySpace:IsolatedQuerySpace, - readonly wpListService:WorkPackagesListService, - readonly wpView:WorkPackagesQueryViewService, - readonly halNotification:HalResourceNotificationService, - readonly cdRef:ChangeDetectorRef, - readonly toastService:ToastService, - ) { - super(locals, cdRef, elementRef); - } - public setValues(change:QuerySharingChange):void { this.isStarred = change.isStarred; this.isPublic = change.isPublic; diff --git a/frontend/src/app/shared/components/no-results/no-results.component.ts b/frontend/src/app/shared/components/no-results/no-results.component.ts index e2cd35986f30..30166e10dddc 100644 --- a/frontend/src/app/shared/components/no-results/no-results.component.ts +++ b/frontend/src/app/shared/components/no-results/no-results.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, ElementRef, HostBinding, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, HostBinding, Input, inject } from '@angular/core'; import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; @@ -37,6 +37,8 @@ import { populateInputsFromDataset } from 'core-app/shared/components/dataset-in standalone: false, }) export class NoResultsComponent { + readonly elementRef = inject(ElementRef); + @Input() title:string; @Input() description:string; @@ -45,9 +47,7 @@ export class NoResultsComponent { @HostBinding('class.generic-table--no-results-container') setHostClass = true; - constructor( - readonly elementRef:ElementRef, - ) { + constructor() { populateInputsFromDataset(this); } } diff --git a/frontend/src/app/shared/components/op-context-menu/handlers/op-columns-context-menu.directive.ts b/frontend/src/app/shared/components/op-context-menu/handlers/op-columns-context-menu.directive.ts index 36746d38b6de..17599bf9545a 100644 --- a/frontend/src/app/shared/components/op-context-menu/handlers/op-columns-context-menu.directive.ts +++ b/frontend/src/app/shared/components/op-context-menu/handlers/op-columns-context-menu.directive.ts @@ -26,13 +26,10 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - Directive, ElementRef, Injector, Input, -} from '@angular/core'; +import { Directive, Injector, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; @@ -50,6 +47,15 @@ import { computePosition, ComputePositionReturn, flip, shift } from '@floating-u standalone: false, }) export class OpColumnsContextMenu extends OpContextMenuTrigger { + readonly wpTableColumns = inject(WorkPackageViewColumnsService); + readonly wpTableSortBy = inject(WorkPackageViewSortByService); + readonly wpTableGroupBy = inject(WorkPackageViewGroupByService); + readonly wpTableHierarchies = inject(WorkPackageViewHierarchiesService); + readonly opModalService = inject(OpModalService); + readonly injector = inject(Injector); + readonly I18n = inject(I18nService); + readonly confirmDialog = inject(ConfirmDialogService); + @Input('opColumnsContextMenu-column') public column:QueryColumn; @Input('opColumnsContextMenu-table') public table:WorkPackageTable; @@ -61,19 +67,6 @@ export class OpColumnsContextMenu extends OpContextMenuTrigger { }, }; - constructor(readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly wpTableColumns:WorkPackageViewColumnsService, - readonly wpTableSortBy:WorkPackageViewSortByService, - readonly wpTableGroupBy:WorkPackageViewGroupByService, - readonly wpTableHierarchies:WorkPackageViewHierarchiesService, - readonly opModalService:OpModalService, - readonly injector:Injector, - readonly I18n:I18nService, - readonly confirmDialog:ConfirmDialogService) { - super(elementRef, opContextMenu); - } - protected open(evt:Event) { if (!this.table.configuration.columnMenuEnabled) { return; diff --git a/frontend/src/app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive.ts b/frontend/src/app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive.ts index 37a1abf8d20c..5cb23213f89b 100644 --- a/frontend/src/app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive.ts +++ b/frontend/src/app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Directive, ElementRef } from '@angular/core'; +import { AfterViewInit, Directive, ElementRef, inject } from '@angular/core'; import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; import { OpContextMenuHandler } from 'core-app/shared/components/op-context-menu/op-context-menu-handler'; import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types'; @@ -10,15 +10,19 @@ import { computePosition, ComputePositionReturn, flip, shift } from '@floating-u standalone: false, }) export class OpContextMenuTrigger extends OpContextMenuHandler implements AfterViewInit { + readonly elementRef = inject(ElementRef); + readonly opContextMenu:OPContextMenuService; + protected element:HTMLElement; protected items:OpContextMenuItem[] = []; - constructor( - readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - ) { + constructor() { + const opContextMenu = inject(OPContextMenuService); + super(opContextMenu); + + this.opContextMenu = opContextMenu; } ngAfterViewInit():void { diff --git a/frontend/src/app/shared/components/op-context-menu/handlers/op-settings-dropdown-menu.directive.ts b/frontend/src/app/shared/components/op-context-menu/handlers/op-settings-dropdown-menu.directive.ts index 063aceef3edd..af330ba11007 100644 --- a/frontend/src/app/shared/components/op-context-menu/handlers/op-settings-dropdown-menu.directive.ts +++ b/frontend/src/app/shared/components/op-context-menu/handlers/op-settings-dropdown-menu.directive.ts @@ -26,13 +26,12 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Directive, ElementRef, Injector, Input, AfterViewInit } from '@angular/core'; +import { Directive, Injector, Input, AfterViewInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { OpContextMenuTrigger, } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; import { States } from 'core-app/core/states/states.service'; import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; @@ -67,6 +66,18 @@ import { TurboRequestsService } from 'core-app/core/turbo/turbo-requests.service standalone: false, }) export class OpSettingsMenuDirective extends OpContextMenuTrigger implements AfterViewInit { + readonly opModalService = inject(OpModalService); + readonly wpListService = inject(WorkPackagesListService); + readonly authorisationService = inject(AuthorisationService); + readonly states = inject(States); + readonly injector = inject(Injector); + readonly querySpace = inject(IsolatedQuerySpace); + readonly wpTableColumns = inject(WorkPackageViewColumnsService); + readonly urlParamsHelper = inject(UrlParamsHelperService); + readonly opStaticQueries = inject(StaticQueriesService); + readonly turboRequests = inject(TurboRequestsService); + readonly I18n = inject(I18nService); + @Input('opSettingsContextMenu-query') public query:QueryResource; @Input() public hideTableOptions:boolean; @@ -79,24 +90,6 @@ export class OpSettingsMenuDirective extends OpContextMenuTrigger implements Aft override readonly placement = 'bottom-end'; - constructor( - readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly opModalService:OpModalService, - readonly wpListService:WorkPackagesListService, - readonly authorisationService:AuthorisationService, - readonly states:States, - readonly injector:Injector, - readonly querySpace:IsolatedQuerySpace, - readonly wpTableColumns:WorkPackageViewColumnsService, - readonly urlParamsHelper:UrlParamsHelperService, - readonly opStaticQueries:StaticQueriesService, - readonly turboRequests:TurboRequestsService, - readonly I18n:I18nService, - ) { - super(elementRef, opContextMenu); - } - ngAfterViewInit():void { super.ngAfterViewInit(); diff --git a/frontend/src/app/shared/components/op-context-menu/handlers/op-types-context-menu.directive.ts b/frontend/src/app/shared/components/op-context-menu/handlers/op-types-context-menu.directive.ts index c1686c10b760..8a82c7633094 100644 --- a/frontend/src/app/shared/components/op-context-menu/handlers/op-types-context-menu.directive.ts +++ b/frontend/src/app/shared/components/op-context-menu/handlers/op-types-context-menu.directive.ts @@ -28,12 +28,7 @@ import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types'; import { StateService } from '@uirouter/core'; -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; -import { - Directive, - ElementRef, - Input, AfterViewInit, -} from '@angular/core'; +import { Directive, Input, AfterViewInit, inject } from '@angular/core'; import { isClickedWithModifier } from 'core-app/shared/helpers/link-handling/link-handling'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; import { WorkPackageCreateService } from 'core-app/features/work-packages/components/wp-new/wp-create.service'; @@ -49,6 +44,12 @@ import { BrowserDetector } from 'core-app/core/browser/browser-detector.service' standalone: false, }) export class OpTypesContextMenuDirective extends OpContextMenuTrigger implements AfterViewInit { + readonly wpCreate = inject(WorkPackageCreateService); + readonly $state = inject(StateService); + readonly pathHelper = inject(PathHelperService); + readonly currentProject = inject(CurrentProjectService); + readonly browser = inject(BrowserDetector); + @Input() public projectIdentifier:string|null|undefined; @Input() public stateName:string; @@ -59,18 +60,6 @@ export class OpTypesContextMenuDirective extends OpContextMenuTrigger implements public isOpen = false; - constructor( - readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly wpCreate:WorkPackageCreateService, - readonly $state:StateService, - readonly pathHelper:PathHelperService, - readonly currentProject:CurrentProjectService, - readonly browser:BrowserDetector, - ) { - super(elementRef, opContextMenu); - } - ngAfterViewInit():void { super.ngAfterViewInit(); diff --git a/frontend/src/app/shared/components/op-context-menu/handlers/wp-create-settings-menu.directive.ts b/frontend/src/app/shared/components/op-context-menu/handlers/wp-create-settings-menu.directive.ts index cded83e7fa9e..407e97aa6af7 100644 --- a/frontend/src/app/shared/components/op-context-menu/handlers/wp-create-settings-menu.directive.ts +++ b/frontend/src/app/shared/components/op-context-menu/handlers/wp-create-settings-menu.directive.ts @@ -26,8 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; -import { Directive, ElementRef } from '@angular/core'; +import { Directive, inject } from '@angular/core'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; import { States } from 'core-app/core/states/states.service'; @@ -38,15 +37,11 @@ import { FormResource } from 'core-app/features/hal/resources/form-resource'; standalone: false, }) export class WorkPackageCreateSettingsMenuDirective extends OpContextMenuTrigger { + readonly states = inject(States); + readonly halEditing = inject(HalResourceEditingService); + override readonly placement = 'bottom-end'; - constructor(readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly states:States, - readonly halEditing:HalResourceEditingService) { - super(elementRef, opContextMenu); - } - protected open(evt:Event) { const wp = this.states.workPackages.get('new').value; diff --git a/frontend/src/app/shared/components/op-context-menu/handlers/wp-group-toggle-dropdown-menu.directive.ts b/frontend/src/app/shared/components/op-context-menu/handlers/wp-group-toggle-dropdown-menu.directive.ts index 56087eca2ebe..9ada72eeb952 100644 --- a/frontend/src/app/shared/components/op-context-menu/handlers/wp-group-toggle-dropdown-menu.directive.ts +++ b/frontend/src/app/shared/components/op-context-menu/handlers/wp-group-toggle-dropdown-menu.directive.ts @@ -26,8 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; -import { Directive, ElementRef } from '@angular/core'; +import { Directive, inject } from '@angular/core'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageViewCollapsedGroupsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service'; @@ -37,12 +36,8 @@ import { WorkPackageViewCollapsedGroupsService } from 'core-app/features/work-pa standalone: false, }) export class WorkPackageGroupToggleDropdownMenuDirective extends OpContextMenuTrigger { - constructor(readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly I18n:I18nService, - readonly wpViewCollapsedGroups:WorkPackageViewCollapsedGroupsService) { - super(elementRef, opContextMenu); - } + readonly I18n = inject(I18nService); + readonly wpViewCollapsedGroups = inject(WorkPackageViewCollapsedGroupsService); protected open(evt:Event) { this.buildItems(); diff --git a/frontend/src/app/shared/components/op-context-menu/handlers/wp-status-dropdown-menu.directive.ts b/frontend/src/app/shared/components/op-context-menu/handlers/wp-status-dropdown-menu.directive.ts index 2e62a54fd94c..e19d5be5c45f 100644 --- a/frontend/src/app/shared/components/op-context-menu/handlers/wp-status-dropdown-menu.directive.ts +++ b/frontend/src/app/shared/components/op-context-menu/handlers/wp-status-dropdown-menu.directive.ts @@ -27,8 +27,7 @@ //++ import { StateService } from '@uirouter/core'; -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; -import { Directive, ElementRef, Input } from '@angular/core'; +import { Directive, Input, inject } from '@angular/core'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; @@ -56,18 +55,15 @@ import { HalError } from 'core-app/features/hal/services/hal-error'; standalone: false, }) export class WorkPackageStatusDropdownDirective extends OpContextMenuTrigger { - @Input('wpStatusDropdown-workPackage') public workPackage:WorkPackageResource; + readonly $state = inject(StateService); + protected workPackageNotificationService = inject(WorkPackageNotificationService); + protected halEditing = inject(HalResourceEditingService); + protected toastService = inject(ToastService); + protected I18n = inject(I18nService); + protected halEvents = inject(HalEventsService); - constructor(readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly $state:StateService, - protected workPackageNotificationService:WorkPackageNotificationService, - protected halEditing:HalResourceEditingService, - protected toastService:ToastService, - protected I18n:I18nService, - protected halEvents:HalEventsService) { - super(elementRef, opContextMenu); - } + // eslint-disable-next-line @angular-eslint/no-input-rename + @Input('wpStatusDropdown-workPackage') public workPackage:WorkPackageResource; protected open(evt:Event) { const change = this.halEditing.changeFor(this.workPackage); diff --git a/frontend/src/app/shared/components/op-context-menu/handlers/wp-view-dropdown-menu.directive.ts b/frontend/src/app/shared/components/op-context-menu/handlers/wp-view-dropdown-menu.directive.ts index 83ab62e6d0a0..246d7d86743d 100644 --- a/frontend/src/app/shared/components/op-context-menu/handlers/wp-view-dropdown-menu.directive.ts +++ b/frontend/src/app/shared/components/op-context-menu/handlers/wp-view-dropdown-menu.directive.ts @@ -26,8 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; -import { Directive, ElementRef } from '@angular/core'; +import { Directive, inject } from '@angular/core'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { @@ -42,15 +41,9 @@ import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/ standalone: false, }) export class WorkPackageViewDropdownMenuDirective extends OpContextMenuTrigger { - constructor( - readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly I18n:I18nService, - readonly wpDisplayRepresentationService:WorkPackageViewDisplayRepresentationService, - readonly wpTableTimeline:WorkPackageViewTimelineService, - ) { - super(elementRef, opContextMenu); - } + readonly I18n = inject(I18nService); + readonly wpDisplayRepresentationService = inject(WorkPackageViewDisplayRepresentationService); + readonly wpTableTimeline = inject(WorkPackageViewTimelineService); public isOpen = false; diff --git a/frontend/src/app/shared/components/op-context-menu/icon-triggered-context-menu/icon-triggered-context-menu.component.ts b/frontend/src/app/shared/components/op-context-menu/icon-triggered-context-menu/icon-triggered-context-menu.component.ts index e44f1a57d635..b131d02b941b 100644 --- a/frontend/src/app/shared/components/op-context-menu/icon-triggered-context-menu/icon-triggered-context-menu.component.ts +++ b/frontend/src/app/shared/components/op-context-menu/icon-triggered-context-menu/icon-triggered-context-menu.component.ts @@ -26,12 +26,11 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Injector, Input } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, Input, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OpContextMenuTrigger, } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types'; @@ -46,18 +45,12 @@ import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op changeDetection: ChangeDetectionStrategy.Default, }) export class IconTriggeredContextMenuComponent extends OpContextMenuTrigger { - override readonly placement = 'bottom-end'; + readonly opModalService = inject(OpModalService); + readonly injector = inject(Injector); + readonly cdRef = inject(ChangeDetectorRef); + readonly I18n = inject(I18nService); - constructor( - readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly opModalService:OpModalService, - readonly injector:Injector, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - ) { - super(elementRef, opContextMenu); - } + override readonly placement = 'bottom-end'; @Input() menuItemsFactory:() => Promise; @Input() customAriaLabel:string = this.I18n.t('js.label_open_menu'); diff --git a/frontend/src/app/shared/components/op-context-menu/op-context-menu.component.ts b/frontend/src/app/shared/components/op-context-menu/op-context-menu.component.ts index 9da13c47753f..4e38ece9f22b 100644 --- a/frontend/src/app/shared/components/op-context-menu/op-context-menu.component.ts +++ b/frontend/src/app/shared/components/op-context-menu/op-context-menu.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { OpContextMenuItem, OpContextMenuLocalsMap, @@ -15,11 +15,13 @@ import { OPContextMenuService } from 'core-app/shared/components/op-context-menu changeDetection: ChangeDetectionStrategy.Default, }) export class OPContextMenuComponent { + locals = inject(OpContextMenuLocalsToken); + public items:OpContextMenuItem[]; public service:OPContextMenuService; - constructor(@Inject(OpContextMenuLocalsToken) public locals:OpContextMenuLocalsMap) { + constructor() { this.items = this.locals.items.filter((item) => !item?.hidden); this.service = this.locals.service; } diff --git a/frontend/src/app/shared/components/op-context-menu/op-context-menu.service.ts b/frontend/src/app/shared/components/op-context-menu/op-context-menu.service.ts index a03b9a474e51..76c4a2ad7a0e 100644 --- a/frontend/src/app/shared/components/op-context-menu/op-context-menu.service.ts +++ b/frontend/src/app/shared/components/op-context-menu/op-context-menu.service.ts @@ -1,4 +1,4 @@ -import { ApplicationRef, Injectable, Injector } from '@angular/core'; +import { ApplicationRef, Injectable, Injector, inject } from '@angular/core'; import { ComponentPortal, ComponentType, DomPortalOutlet } from '@angular/cdk/portal'; import { TransitionService } from '@uirouter/core'; import { OpContextMenuHandler } from 'core-app/shared/components/op-context-menu/op-context-menu-handler'; @@ -11,6 +11,11 @@ import { FocusHelperService } from 'core-app/shared/directives/focus/focus-helpe @Injectable({ providedIn: 'root' }) export class OPContextMenuService { + readonly FocusHelper = inject(FocusHelperService); + private appRef = inject(ApplicationRef); + private $transitions = inject(TransitionService); + private injector = inject(Injector); + public active:OpContextMenuHandler|null = null; // Hold a reference to the DOM node we're using as a host @@ -23,14 +28,6 @@ export class OPContextMenuService { private isOpening = false; private openSeq = 0; - constructor( - readonly FocusHelper:FocusHelperService, - private appRef:ApplicationRef, - private $transitions:TransitionService, - private injector:Injector, - ) { - } - public register() { const existing = document.querySelector('.op-context-menu--overlay'); existing?.remove(); diff --git a/frontend/src/app/shared/components/op-non-working-days-list/op-non-working-days-list.component.ts b/frontend/src/app/shared/components/op-non-working-days-list/op-non-working-days-list.component.ts index 014372b5a131..c545e4107d53 100644 --- a/frontend/src/app/shared/components/op-non-working-days-list/op-non-working-days-list.component.ts +++ b/frontend/src/app/shared/components/op-non-working-days-list/op-non-working-days-list.component.ts @@ -1,16 +1,4 @@ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - HostBinding, - Injector, - Input, - OnInit, - ViewChild, - ViewEncapsulation, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostBinding, Injector, Input, OnInit, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { BannersService } from 'core-app/core/enterprise/banners.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OpModalService } from '../modal/modal.service'; @@ -47,6 +35,18 @@ export interface INonWorkingDay { standalone: false, }) export class OpNonWorkingDaysListComponent implements OnInit, AfterViewInit { + readonly elementRef = inject>(ElementRef); + protected I18n = inject(I18nService); + readonly bannersService = inject(BannersService); + readonly opModalService = inject(OpModalService); + readonly injector = inject(Injector); + readonly pathHelper = inject(PathHelperService); + readonly apiV3Service = inject(ApiV3Service); + readonly dayService = inject(DayResourceService); + readonly confirmDialogService = inject(ConfirmDialogService); + readonly toast = inject(ToastService); + readonly cdRef = inject(ChangeDetectorRef); + @ViewChild(FullCalendarComponent) ucCalendar:FullCalendarComponent; @HostBinding('class.op-non-working-days-list') className = true; @@ -119,19 +119,7 @@ export class OpNonWorkingDaysListComponent implements OnInit, AfterViewInit { }; - constructor( - readonly elementRef:ElementRef, - protected I18n:I18nService, - readonly bannersService:BannersService, - readonly opModalService:OpModalService, - readonly injector:Injector, - readonly pathHelper:PathHelperService, - readonly apiV3Service:ApiV3Service, - readonly dayService:DayResourceService, - readonly confirmDialogService:ConfirmDialogService, - readonly toast:ToastService, - readonly cdRef:ChangeDetectorRef, - ) { + constructor() { populateInputsFromDataset(this); this.listenToFormSubmit(); } diff --git a/frontend/src/app/shared/components/op-view-select/op-static-queries.service.ts b/frontend/src/app/shared/components/op-view-select/op-static-queries.service.ts index 3cf7f4db7de3..3057256ae53a 100644 --- a/frontend/src/app/shared/components/op-view-select/op-static-queries.service.ts +++ b/frontend/src/app/shared/components/op-view-select/op-static-queries.service.ts @@ -28,16 +28,14 @@ import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { StateService } from '@uirouter/core'; @Injectable() export class StaticQueriesService { - constructor( - private readonly I18n:I18nService, - private readonly $state:StateService, - ) { - } + private readonly I18n = inject(I18nService); + private readonly $state = inject(StateService); + public text = { work_packages: this.I18n.t('js.label_work_package_plural'), diff --git a/frontend/src/app/shared/components/option-list/option-list.component.ts b/frontend/src/app/shared/components/option-list/option-list.component.ts index c0ec3addba0f..a92024561880 100644 --- a/frontend/src/app/shared/components/option-list/option-list.component.ts +++ b/frontend/src/app/shared/components/option-list/option-list.component.ts @@ -1,13 +1,4 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - EventEmitter, - forwardRef, - HostBinding, - Input, - Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, forwardRef, HostBinding, Input, Output, inject } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; export interface IOpOptionListOption { @@ -35,9 +26,9 @@ export type IOpOptionListValue = T|null; changeDetection: ChangeDetectionStrategy.Default, }) export class OpOptionListComponent implements ControlValueAccessor { - @HostBinding('class.op-option-list') className = true; + private cdRef = inject(ChangeDetectorRef); - constructor(private cdRef:ChangeDetectorRef) {} + @HostBinding('class.op-option-list') className = true; @Input() options:IOpOptionListOption[] = []; diff --git a/frontend/src/app/shared/components/persistent-toggle/persistent-toggle.component.ts b/frontend/src/app/shared/components/persistent-toggle/persistent-toggle.component.ts index 7b3d3497f2db..4f55e5920b6a 100644 --- a/frontend/src/app/shared/components/persistent-toggle/persistent-toggle.component.ts +++ b/frontend/src/app/shared/components/persistent-toggle/persistent-toggle.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, ElementRef, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, OnInit, inject } from '@angular/core'; import { slideDown, slideUp } from 'es6-slide-up-down'; @@ -40,6 +40,8 @@ import { slideDown, slideUp } from 'es6-slide-up-down'; changeDetection: ChangeDetectionStrategy.Default, }) export class PersistentToggleComponent implements OnInit { + private elementRef = inject>(ElementRef); + /** Unique identifier of the toggle */ private identifier:string; @@ -51,9 +53,6 @@ export class PersistentToggleComponent implements OnInit { private targetNotification:HTMLElement|null; - constructor(private elementRef:ElementRef) { - } - ngOnInit():void { this.element = this.elementRef.nativeElement; this.targetNotification = this.getTargetNotification(); diff --git a/frontend/src/app/shared/components/principal/principal-renderer.service.ts b/frontend/src/app/shared/components/principal/principal-renderer.service.ts index c2df62d63607..23fb5f1b5bcd 100644 --- a/frontend/src/app/shared/components/principal/principal-renderer.service.ts +++ b/frontend/src/app/shared/components/principal/principal-renderer.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { colorModes, ColorsService } from 'core-app/shared/components/colors/colors.service'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; @@ -28,12 +28,10 @@ export interface NameOptions { @Injectable({ providedIn: 'root' }) export class PrincipalRendererService { - constructor( - private pathHelper:PathHelperService, - private apiV3Service:ApiV3Service, - private colors:ColorsService, - ) { - } + private pathHelper = inject(PathHelperService); + private apiV3Service = inject(ApiV3Service); + private colors = inject(ColorsService); + renderAbbreviated( container:HTMLElement, diff --git a/frontend/src/app/shared/components/principal/principal-rendering.module.ts b/frontend/src/app/shared/components/principal/principal-rendering.module.ts index 18573122c0e5..526d0db71620 100644 --- a/frontend/src/app/shared/components/principal/principal-rendering.module.ts +++ b/frontend/src/app/shared/components/principal/principal-rendering.module.ts @@ -1,4 +1,4 @@ -import { Injector, NgModule } from '@angular/core'; +import { Injector, NgModule, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { OpPrincipalComponent } from './principal.component'; import { PrincipalRendererService } from './principal-renderer.service'; @@ -18,6 +18,5 @@ import { PrincipalRendererService } from './principal-renderer.service'; ], }) export class OpenprojectPrincipalRenderingModule { - constructor(readonly injector:Injector) { - } + readonly injector = inject(Injector); } diff --git a/frontend/src/app/shared/components/principal/principal.component.ts b/frontend/src/app/shared/components/principal/principal.component.ts index 069affe9e5fd..a65a1bfa7af7 100644 --- a/frontend/src/app/shared/components/principal/principal.component.ts +++ b/frontend/src/app/shared/components/principal/principal.component.ts @@ -26,15 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Input, - OnInit, - ViewEncapsulation, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; @@ -62,6 +54,15 @@ export interface PrincipalInput { standalone: false, }) export class OpPrincipalComponent implements OnInit { + readonly elementRef = inject(ElementRef); + readonly PathHelper = inject(PathHelperService); + readonly principalRenderer = inject(PrincipalRendererService); + readonly principalResourceService = inject(PrincipalsResourceService); + readonly I18n = inject(I18nService); + readonly apiV3Service = inject(ApiV3Service); + readonly timezoneService = inject(TimezoneService); + readonly cdRef = inject(ChangeDetectorRef); + @Input() principal:PrincipalLike; @Input() hideAvatar = false; @@ -83,16 +84,7 @@ export class OpPrincipalComponent implements OnInit { @Input() title = ''; - public constructor( - readonly elementRef:ElementRef, - readonly PathHelper:PathHelperService, - readonly principalRenderer:PrincipalRendererService, - readonly principalResourceService:PrincipalsResourceService, - readonly I18n:I18nService, - readonly apiV3Service:ApiV3Service, - readonly timezoneService:TimezoneService, - readonly cdRef:ChangeDetectorRef, - ) { + public constructor() { populateInputsFromDataset(this); } diff --git a/frontend/src/app/shared/components/project-include/list/project-include-list.component.ts b/frontend/src/app/shared/components/project-include/list/project-include-list.component.ts index 395f916cdd67..aa0b211d3766 100644 --- a/frontend/src/app/shared/components/project-include/list/project-include-list.component.ts +++ b/frontend/src/app/shared/components/project-include/list/project-include-list.component.ts @@ -26,14 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - HostBinding, - Input, - Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, Output, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @@ -54,6 +47,11 @@ import { getMetaContent } from 'core-app/core/setup/globals/global-helpers'; standalone: false, }) export class OpProjectIncludeListComponent { + readonly I18n = inject(I18nService); + readonly currentProjectService = inject(CurrentProjectService); + readonly pathHelper = inject(PathHelperService); + readonly searchableProjectListService = inject(SearchableProjectListService); + @HostBinding('class.spot-list') classNameList = true; @HostBinding('class.op-project-include-list') className = true; @@ -80,13 +78,6 @@ export class OpProjectIncludeListComponent { current_project: this.I18n.t('js.include_projects.tooltip.current_project'), }; - constructor( - readonly I18n:I18nService, - readonly currentProjectService:CurrentProjectService, - readonly pathHelper:PathHelperService, - readonly searchableProjectListService:SearchableProjectListService, - ) { } - public updateList(selected:string[]):void { this.update.emit(selected); } diff --git a/frontend/src/app/shared/components/project-include/project-include.component.ts b/frontend/src/app/shared/components/project-include/project-include.component.ts index 88cc7e873e25..a231658a75f7 100644 --- a/frontend/src/app/shared/components/project-include/project-include.component.ts +++ b/frontend/src/app/shared/components/project-include/project-include.component.ts @@ -26,13 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - HostBinding, - OnDestroy, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, OnDestroy, OnInit, inject } from '@angular/core'; import { BehaviorSubject, combineLatest, Subscription } from 'rxjs'; import { debounceTime, @@ -56,6 +50,7 @@ import { import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { IProject } from 'core-app/core/state/projects/project.model'; import { SearchableProjectListService, @@ -77,6 +72,13 @@ import { calculatePositions } from 'core-app/shared/components/project-include/c standalone: false, }) export class OpProjectIncludeComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { + readonly I18n = inject(I18nService); + readonly wpTableFilters = inject(WorkPackageViewFiltersService); + readonly wpIncludeSubprojects = inject(WorkPackageViewIncludeSubprojectsService); + readonly halResourceService = inject(HalResourceService); + readonly searchableProjectListService = inject(SearchableProjectListService); + readonly querySpace = inject(IsolatedQuerySpace); + @HostBinding('class.op-project-include') className = true; public text = { @@ -95,7 +97,7 @@ export class OpProjectIncludeComponent extends UntilDestroyedMixin implements On public textFieldFocused = false; - public query$ = this.wpTableFilters.querySpace.query.values$(); + public query$ = this.querySpace.query.values$(); public displayModeOptions = [ { value: 'all', title: this.text.filter_all }, @@ -258,13 +260,7 @@ export class OpProjectIncludeComponent extends UntilDestroyedMixin implements On public loading$ = new BehaviorSubject(false); - constructor( - readonly I18n:I18nService, - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly wpIncludeSubprojects:WorkPackageViewIncludeSubprojectsService, - readonly halResourceService:HalResourceService, - readonly searchableProjectListService:SearchableProjectListService, - ) { + constructor() { super(); this.projects$ diff --git a/frontend/src/app/shared/components/remote-field-updater/remote-field-updater.component.ts b/frontend/src/app/shared/components/remote-field-updater/remote-field-updater.component.ts index 55606662e319..d4f4d52e9dcc 100644 --- a/frontend/src/app/shared/components/remote-field-updater/remote-field-updater.component.ts +++ b/frontend/src/app/shared/components/remote-field-updater/remote-field-updater.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, ElementRef, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, OnDestroy, OnInit, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; export const remoteFieldUpdaterSelector = 'remote-field-updater'; @@ -38,11 +38,9 @@ export const remoteFieldUpdaterSelector = 'remote-field-updater'; standalone: false, }) export class RemoteFieldUpdaterComponent implements OnInit, OnDestroy { - constructor( - private elementRef:ElementRef, - private http:HttpClient, - ) { - } + private elementRef = inject(ElementRef); + private http = inject(HttpClient); + private url:string; diff --git a/frontend/src/app/shared/components/resizer/resizer/wp-resizer.component.ts b/frontend/src/app/shared/components/resizer/resizer/wp-resizer.component.ts index 85eae84d39a5..fe333a67d1ce 100644 --- a/frontend/src/app/shared/components/resizer/resizer/wp-resizer.component.ts +++ b/frontend/src/app/shared/components/resizer/resizer/wp-resizer.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, OnDestroy } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, OnDestroy, inject } from '@angular/core'; import { debounceTime } from 'rxjs/operators'; import { TransitionService } from '@uirouter/core'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @@ -47,6 +47,9 @@ import { fromEvent } from 'rxjs'; standalone: false, }) export class WpResizerComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit, OnDestroy { + private elementRef = inject>(ElementRef); + readonly $transitions = inject(TransitionService); + @Input() elementClass:string; @Input() resizeEvent:string; @@ -70,13 +73,6 @@ export class WpResizerComponent extends UntilDestroyedMixin implements OnInit, A public resizerClass = 'work-packages--resizer icon-resizer-vertical-lines'; - constructor( - private elementRef:ElementRef, - readonly $transitions:TransitionService, - ) { - super(); - } - ngOnInit() { // Get element // We use this more complicated approach of taking the last element of the class as it allows diff --git a/frontend/src/app/shared/components/searchable-project-list/searchable-project-list.service.ts b/frontend/src/app/shared/components/searchable-project-list/searchable-project-list.service.ts index e83ce6f29149..04055898ec0b 100644 --- a/frontend/src/app/shared/components/searchable-project-list/searchable-project-list.service.ts +++ b/frontend/src/app/shared/components/searchable-project-list/searchable-project-list.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ApiV3ListFilter, ApiV3ListParameters, @@ -20,6 +20,11 @@ const UNDISCLOSED_ANCESTOR = 'urn:openproject-org:api:v3:undisclosed'; @Injectable() export class SearchableProjectListService { + readonly http = inject(HttpClient); + readonly apiV3Service = inject(ApiV3Service); + readonly currentProjectService = inject(CurrentProjectService); + readonly configurationService = inject(ConfigurationService); + private _searchText = ''; private searchText$ = new BehaviorSubject(''); private loadingEnabled$ = new BehaviorSubject(false); @@ -123,13 +128,6 @@ export class SearchableProjectListService { }) ); - constructor( - readonly http:HttpClient, - readonly apiV3Service:ApiV3Service, - readonly currentProjectService:CurrentProjectService, - readonly configurationService:ConfigurationService, - ) { } - /** Causes fetching of a new project list and enables reloads of the project list, when the searchText changes. */ public enableLoading():void { this.loadingEnabled$.next(true); diff --git a/frontend/src/app/shared/components/storages/file-link-list-item/file-link-list-item.component.ts b/frontend/src/app/shared/components/storages/file-link-list-item/file-link-list-item.component.ts index b417a7f50d01..0c7751347874 100644 --- a/frontend/src/app/shared/components/storages/file-link-list-item/file-link-list-item.component.ts +++ b/frontend/src/app/shared/components/storages/file-link-list-item/file-link-list-item.component.ts @@ -26,19 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - Component, - ElementRef, - EventEmitter, - Input, - OnChanges, - OnInit, - Output, - SimpleChanges, - ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; @@ -64,6 +52,11 @@ import SpotDropAlignmentOption from 'core-app/spot/drop-alignment-options'; standalone: false, }) export class FileLinkListItemComponent implements OnInit, OnChanges, AfterViewInit { + private readonly i18n = inject(I18nService); + private readonly timezoneService = inject(TimezoneService); + private readonly confirmDialogService = inject(ConfirmDialogService); + private readonly principalRendererService = inject(PrincipalRendererService); + @Input() public fileLink:IFileLink; @Input() public allowEditing = false; @@ -99,13 +92,6 @@ export class FileLinkListItemComponent implements OnInit, OnChanges, AfterViewIn notFoundTooltipText: this.i18n.t('js.storages.file_links.tooltip.not_found'), }; - constructor( - private readonly i18n:I18nService, - private readonly timezoneService:TimezoneService, - private readonly confirmDialogService:ConfirmDialogService, - private readonly principalRendererService:PrincipalRendererService, - ) {} - public get hasTooltip():boolean { return this.tooltip !== ''; } diff --git a/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.spec.ts b/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.spec.ts index 34bdab2a5f5a..25778cd214b4 100644 --- a/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.spec.ts +++ b/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.spec.ts @@ -26,9 +26,10 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectorRef, ElementRef } from '@angular/core'; +import { ChangeDetectorRef, Component, ElementRef } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; import { Observable, config, throwError } from 'rxjs'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { SortFilesPipe } from 'core-app/shared/components/storages/pipes/sort-files.pipe'; import { StorageFilesResourceService } from 'core-app/core/state/storage-files/storage-files.service'; import { IStorageFile } from 'core-app/core/state/storage-files/storage-file.model'; @@ -36,11 +37,9 @@ import { FilePickerBaseModalComponent, } from 'core-app/shared/components/storag import { StorageFileListItem } from 'core-app/shared/components/storages/storage-file-list-item/storage-file-list-item'; import type { Mock } from 'vitest'; +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection,@angular-eslint/component-selector +@Component({ selector: 'test-file-picker', template: '', standalone: false }) class TestFilePickerBaseModalComponent extends FilePickerBaseModalComponent { - constructor(locals:OpModalLocalsMap, elementRef:ElementRef, cdRef:ChangeDetectorRef, sortFilesPipe:SortFilesPipe, storageFilesResourceService:StorageFilesResourceService) { - super(locals, elementRef, cdRef, sortFilesPipe, storageFilesResourceService); - } - public loadDirectory(directory:IStorageFile):void { this.changeLevel(directory); } @@ -59,8 +58,6 @@ describe('FilePickerBaseModalComponent', () => { } function buildComponent(spies:Spies) { - const cdRef = { detectChanges: spies.detectChanges } as unknown as ChangeDetectorRef; - const elementRef = { nativeElement: document.createElement('div') } as ElementRef; const locals = { service: { close: spies.close }, storage: { @@ -71,19 +68,27 @@ describe('FilePickerBaseModalComponent', () => { }, }, projectFolderMode: 'inactive', - } as unknown as OpModalLocalsMap; - const sortFilesPipe = { transform: (files:IStorageFile[]) => files } as SortFilesPipe; - const storageFilesResourceService = { - files: spies.files, - reset: spies.reset, - } as unknown as StorageFilesResourceService; - const component = new TestFilePickerBaseModalComponent(locals, elementRef, cdRef, sortFilesPipe, storageFilesResourceService); + }; + + TestBed.configureTestingModule({ + declarations: [TestFilePickerBaseModalComponent], + providers: [ + { provide: OpModalLocalsToken, useValue: locals }, + { provide: ChangeDetectorRef, useValue: { detectChanges: spies.detectChanges } }, + { provide: ElementRef, useValue: { nativeElement: document.createElement('div') } }, + { provide: SortFilesPipe, useValue: { transform: (files:IStorageFile[]) => files } }, + { provide: StorageFilesResourceService, useValue: { files: spies.files, reset: spies.reset } }, + ], + }); + const component = TestBed.runInInjectionContext(() => new TestFilePickerBaseModalComponent()); component.ngOnInit(); - return { component, cdRef, storageFilesResourceService }; + return { component }; } + afterEach(() => TestBed.resetTestingModule()); + it('cancels pending directory loading on destroy', () => { const teardown = vi.fn(); const files$ = new Observable(() => teardown); diff --git a/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.ts b/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.ts index 81a47109c5d7..7cc2ca720cfd 100644 --- a/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.ts +++ b/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.ts @@ -26,14 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectorRef, - Directive, - ElementRef, - Inject, - OnDestroy, - OnInit, -} from '@angular/core'; +import { Directive, OnDestroy, OnInit, inject } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { BehaviorSubject, @@ -46,9 +39,7 @@ import { catchError, map } from 'rxjs/operators'; import { IStorage } from 'core-app/core/state/storages/storage.model'; import { IStorageFile } from 'core-app/core/state/storage-files/storage-file.model'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { SortFilesPipe } from 'core-app/shared/components/storages/pipes/sort-files.pipe'; import { StorageFilesResourceService } from 'core-app/core/state/storage-files/storage-files.service'; import { Breadcrumb, BreadcrumbsContent } from 'core-app/spot/components/breadcrumbs/breadcrumbs-content'; @@ -69,6 +60,9 @@ type Alert = 'none'|'noAccess'|'managedFolderNoAccess'|'managedFolderNotFound'|' @Directive() export abstract class FilePickerBaseModalComponent extends OpModalComponent implements OnInit, OnDestroy { + protected readonly sortFilesPipe = inject(SortFilesPipe); + protected readonly storageFilesResourceService = inject(StorageFilesResourceService); + private loadingSubscription:Subscription; protected readonly storageFiles$ = new BehaviorSubject([]); @@ -96,16 +90,6 @@ export abstract class FilePickerBaseModalComponent extends OpModalComponent impl public readonly loading$ = new BehaviorSubject<'loading'|'success'|'error'>('loading'); - protected constructor( - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly elementRef:ElementRef, - readonly cdRef:ChangeDetectorRef, - protected readonly sortFilesPipe:SortFilesPipe, - protected readonly storageFilesResourceService:StorageFilesResourceService, - ) { - super(locals, cdRef, elementRef); - } - ngOnInit():void { super.ngOnInit(); diff --git a/frontend/src/app/shared/components/storages/file-picker-modal/file-picker-modal.component.ts b/frontend/src/app/shared/components/storages/file-picker-modal/file-picker-modal.component.ts index ee0f9478391d..c0cdb850a139 100644 --- a/frontend/src/app/shared/components/storages/file-picker-modal/file-picker-modal.component.ts +++ b/frontend/src/app/shared/components/storages/file-picker-modal/file-picker-modal.component.ts @@ -26,9 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map, take } from 'rxjs/operators'; @@ -37,14 +35,10 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; import { IFileLink } from 'core-app/core/state/file-links/file-link.model'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { IStorageFile } from 'core-app/core/state/storage-files/storage-file.model'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { SortFilesPipe } from 'core-app/shared/components/storages/pipes/sort-files.pipe'; import { FileLinksResourceService } from 'core-app/core/state/file-links/file-links.service'; import { isDirectory, storageLocaleString } from 'core-app/shared/components/storages/functions/storages.functions'; -import { StorageFilesResourceService } from 'core-app/core/state/storage-files/storage-files.service'; import { StorageFileListItem, StorageFileListItemCheckbox, } from 'core-app/shared/components/storages/storage-file-list-item/storage-file-list-item'; @@ -58,6 +52,12 @@ import { standalone: false, }) export class FilePickerModalComponent extends FilePickerBaseModalComponent { + private readonly i18n = inject(I18nService); + private readonly toastService = inject(ToastService); + private readonly timezoneService = inject(TimezoneService); + private readonly configuration = inject(ConfigurationService); + private readonly fileLinksResourceService = inject(FileLinksResourceService); + public readonly text = { header: this.i18n.t('js.storages.file_links.select'), alertNoAccess: this.i18n.t('js.storages.files.project_folder_no_access'), @@ -117,25 +117,8 @@ export class FilePickerModalComponent extends FilePickerBaseModalComponent { private readonly fileMap:Record = {}; - constructor( - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly elementRef:ElementRef, - readonly cdRef:ChangeDetectorRef, - protected readonly sortFilesPipe:SortFilesPipe, - protected readonly storageFilesResourceService:StorageFilesResourceService, - private readonly i18n:I18nService, - private readonly toastService:ToastService, - private readonly timezoneService:TimezoneService, - private readonly configuration:ConfigurationService, - private readonly fileLinksResourceService:FileLinksResourceService, - ) { - super( - locals, - elementRef, - cdRef, - sortFilesPipe, - storageFilesResourceService, - ); + constructor() { + super(); this.showSelectAll = this.configuration.activeFeatureFlags.includes('storageFilePickingSelectAll'); } diff --git a/frontend/src/app/shared/components/storages/location-picker-modal/location-picker-modal.component.ts b/frontend/src/app/shared/components/storages/location-picker-modal/location-picker-modal.component.ts index 5a996e398bdb..c4a506950f70 100644 --- a/frontend/src/app/shared/components/storages/location-picker-modal/location-picker-modal.component.ts +++ b/frontend/src/app/shared/components/storages/location-picker-modal/location-picker-modal.component.ts @@ -26,26 +26,16 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Inject, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; import { IStorageFile } from 'core-app/core/state/storage-files/storage-file.model'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { SortFilesPipe } from 'core-app/shared/components/storages/pipes/sort-files.pipe'; import { isDirectory, makeFilesCollectionLink, storageLocaleString, } from 'core-app/shared/components/storages/functions/storages.functions'; -import { StorageFilesResourceService } from 'core-app/core/state/storage-files/storage-files.service'; import { StorageFileListItem, } from 'core-app/shared/components/storages/storage-file-list-item/storage-file-list-item'; @@ -61,6 +51,9 @@ import { map } from 'rxjs/operators'; standalone: false, }) export class LocationPickerModalComponent extends FilePickerBaseModalComponent { + private readonly i18n = inject(I18nService); + private readonly timezoneService = inject(TimezoneService); + public submitted = false; public readonly text = { @@ -138,24 +131,6 @@ export class LocationPickerModalComponent extends FilePickerBaseModalComponent { ); } - constructor( - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly elementRef:ElementRef, - readonly cdRef:ChangeDetectorRef, - protected sortFilesPipe:SortFilesPipe, - protected readonly storageFilesResourceService:StorageFilesResourceService, - private readonly i18n:I18nService, - private readonly timezoneService:TimezoneService, - ) { - super( - locals, - elementRef, - cdRef, - sortFilesPipe, - storageFilesResourceService, - ); - } - public chooseLocation():void { this.submitted = true; this.service.close(); diff --git a/frontend/src/app/shared/components/storages/storage-information/storage-information.service.ts b/frontend/src/app/shared/components/storages/storage-information/storage-information.service.ts index d0b521a1d805..c1513f2a8804 100644 --- a/frontend/src/app/shared/components/storages/storage-information/storage-information.service.ts +++ b/frontend/src/app/shared/components/storages/storage-information/storage-information.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -46,6 +46,9 @@ import { @Injectable() export class StorageInformationService { + private readonly i18n = inject(I18nService); + private readonly currentUserService = inject(CurrentUserService); + private text = { fileLinkErrorHeader: this.i18n.t('js.storages.information.live_data_error'), fileLinkErrorContent: (storageType:string):string => this.i18n.t('js.storages.information.live_data_error_description', { storageType }), @@ -60,11 +63,6 @@ export class StorageInformationService { suggestRelink: this.i18n.t('js.storages.information.suggest_relink'), }; - constructor( - private readonly i18n:I18nService, - private readonly currentUserService:CurrentUserService, - ) {} - public storageInformation(storage:IStorage, fileLinks:IFileLink[]):Observable { return this.currentUserService.isLoggedIn$ .pipe( diff --git a/frontend/src/app/shared/components/storages/storage-login-button/storage-login-button.component.ts b/frontend/src/app/shared/components/storages/storage-login-button/storage-login-button.component.ts index 03aecc7be535..65abde2e5717 100644 --- a/frontend/src/app/shared/components/storages/storage-login-button/storage-login-button.component.ts +++ b/frontend/src/app/shared/components/storages/storage-login-button/storage-login-button.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, ElementRef, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, inject } from '@angular/core'; import { CookieService } from 'ngx-cookie-service'; import { v4 as uuidv4 } from 'uuid'; @@ -43,15 +43,15 @@ import { populateInputsFromDataset } from 'core-app/shared/components/dataset-in standalone: false, }) export class StorageLoginButtonComponent implements OnInit { + elementRef = inject(ElementRef); + private readonly i18n = inject(I18nService); + private readonly cookieService = inject(CookieService); + @Input() input:IStorageLoginInput; label:string; - constructor( - public elementRef:ElementRef, - private readonly i18n:I18nService, - private readonly cookieService:CookieService, - ) { + constructor() { populateInputsFromDataset(this); } diff --git a/frontend/src/app/shared/components/storages/storage/storage.component.ts b/frontend/src/app/shared/components/storages/storage/storage.component.ts index 40d47240737d..1d5b29e17c0d 100644 --- a/frontend/src/app/shared/components/storages/storage/storage.component.ts +++ b/frontend/src/app/shared/components/storages/storage/storage.component.ts @@ -26,18 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - Input, - OnDestroy, - OnInit, - Output, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild, inject } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { combineLatest, @@ -113,6 +102,18 @@ import { standalone: false, }) export class StorageComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { + private readonly i18n = inject(I18nService); + private readonly cdRef = inject(ChangeDetectorRef); + private readonly toastService = inject(ToastService); + private readonly uploadService = inject(OpUploadService); + private readonly opModalService = inject(OpModalService); + private readonly timezoneService = inject(TimezoneService); + private readonly pathHelperService = inject(PathHelperService); + private readonly storagesResourceService = inject(StoragesResourceService); + private readonly fileLinkResourceService = inject(FileLinksResourceService); + private readonly storageInformationService = inject(StorageInformationService); + private readonly storageFilesResourceService = inject(StorageFilesResourceService); + @Input() public resource:HalResource; @Input() public projectStorage:IProjectStorage; @@ -218,22 +219,6 @@ export class StorageComponent extends UntilDestroyedMixin implements OnInit, OnD return this.projectStorage._links.open?.href; } - constructor( - private readonly i18n:I18nService, - private readonly cdRef:ChangeDetectorRef, - private readonly toastService:ToastService, - private readonly uploadService:OpUploadService, - private readonly opModalService:OpModalService, - private readonly timezoneService:TimezoneService, - private readonly pathHelperService:PathHelperService, - private readonly storagesResourceService:StoragesResourceService, - private readonly fileLinkResourceService:FileLinksResourceService, - private readonly storageInformationService:StorageInformationService, - private readonly storageFilesResourceService:StorageFilesResourceService, - ) { - super(); - } - ngOnInit():void { this.storage = this.storagesResourceService.requireEntity(this.projectStorage._links.storage.href); diff --git a/frontend/src/app/shared/components/storages/upload-conflict-modal/upload-conflict-modal.component.ts b/frontend/src/app/shared/components/storages/upload-conflict-modal/upload-conflict-modal.component.ts index ae7d1082c1e8..c319da37f5a8 100644 --- a/frontend/src/app/shared/components/storages/upload-conflict-modal/upload-conflict-modal.component.ts +++ b/frontend/src/app/shared/components/storages/upload-conflict-modal/upload-conflict-modal.component.ts @@ -26,18 +26,10 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Inject, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; @Component({ templateUrl: 'upload-conflict-modal.component.html', @@ -45,6 +37,8 @@ import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.servi standalone: false, }) export class UploadConflictModalComponent extends OpModalComponent { + private readonly i18n = inject(I18nService); + public overwrite:boolean|null = null; public text = { @@ -57,15 +51,6 @@ export class UploadConflictModalComponent extends OpModalComponent { }, }; - public constructor( - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef, - private readonly i18n:I18nService, - ) { - super(locals, cdRef, elementRef); - } - public close(overwrite:boolean):void { this.overwrite = overwrite; this.service.close(); diff --git a/frontend/src/app/shared/components/storages/upload/storage-upload.service.ts b/frontend/src/app/shared/components/storages/upload/storage-upload.service.ts index 13920ff44e99..c4c0dd64808f 100644 --- a/frontend/src/app/shared/components/storages/upload/storage-upload.service.ts +++ b/frontend/src/app/shared/components/storages/upload/storage-upload.service.ts @@ -28,7 +28,7 @@ import { Observable } from 'rxjs'; import { ID } from '@datorama/akita'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpEvent } from '@angular/common/http'; import { IUploadFile, OpUploadService } from 'core-app/core/upload/upload.service'; @@ -47,13 +47,9 @@ export interface IStorageFileUploadResponse { @Injectable() export class StorageUploadService extends OpUploadService { - private uploadStrategy:IUploadStrategy; + private readonly http = inject(HttpClient); - constructor( - private readonly http:HttpClient, - ) { - super(); - } + private uploadStrategy:IUploadStrategy; public upload( href:string, diff --git a/frontend/src/app/shared/components/table-pagination/pagination-service.ts b/frontend/src/app/shared/components/table-pagination/pagination-service.ts index c9e80fb99326..f6463e1fe48f 100644 --- a/frontend/src/app/shared/components/table-pagination/pagination-service.ts +++ b/frontend/src/app/shared/components/table-pagination/pagination-service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; export const DEFAULT_PAGINATION_OPTIONS = { @@ -48,9 +48,11 @@ export interface PaginationObject { @Injectable() export class PaginationService { + private configuration = inject(ConfigurationService); + private paginationOptions:IPaginationOptions; - constructor(private configuration:ConfigurationService) { + constructor() { this.loadPaginationOptions(); } diff --git a/frontend/src/app/shared/components/table-pagination/table-pagination.component.ts b/frontend/src/app/shared/components/table-pagination/table-pagination.component.ts index 838e27dd569e..5ce33d98d566 100644 --- a/frontend/src/app/shared/components/table-pagination/table-pagination.component.ts +++ b/frontend/src/app/shared/components/table-pagination/table-pagination.component.ts @@ -26,15 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - EventEmitter, - Input, - OnInit, - Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { PaginationInstance } from 'core-app/shared/components/table-pagination/pagination-instance'; @@ -47,6 +39,10 @@ import { PaginationService } from 'core-app/shared/components/table-pagination/p standalone: false, }) export class TablePaginationComponent extends UntilDestroyedMixin implements OnInit { + protected paginationService = inject(PaginationService); + protected cdRef = inject(ChangeDetectorRef); + protected I18n = inject(I18nService); + @Input() totalEntries:string; @Input() hideForSinglePageResults = false; @@ -83,14 +79,6 @@ export class TablePaginationComponent extends UntilDestroyedMixin implements OnI public perPageOptions:number[] = []; - constructor( - protected paginationService:PaginationService, - protected cdRef:ChangeDetectorRef, - protected I18n:I18nService, - ) { - super(); - } - ngOnInit():void { const paginationOptions = this.paginationService.getPaginationOptions(); this.perPageOptions = paginationOptions.perPageOptions; diff --git a/frontend/src/app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component.ts b/frontend/src/app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component.ts index b86417fe0aa0..66d81fc4360e 100644 --- a/frontend/src/app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component.ts +++ b/frontend/src/app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component.ts @@ -1,17 +1,4 @@ -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - Input, - Injector, - OnChanges, - Output, - SimpleChanges, - ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, Injector, OnChanges, Output, SimpleChanges, ViewChild, inject } from '@angular/core'; import { TabDefinition } from 'core-app/shared/components/tabs/tab.interface'; import { RawParams, @@ -31,6 +18,10 @@ import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decora standalone: false, }) export class ScrollableTabsComponent extends UntilDestroyedMixin implements AfterViewInit, OnChanges { + protected readonly $state = inject(StateService); + private cdRef = inject(ChangeDetectorRef); + injector = inject(Injector); + @ViewChild('scrollContainer', { static: true }) scrollContainer:ElementRef; @ViewChild('scrollPane', { static: true }) scrollPane:ElementRef; @@ -65,14 +56,6 @@ export class ScrollableTabsComponent extends UntilDestroyedMixin implements Afte private dragTargetStack = 0; - constructor( - protected readonly $state:StateService, - private cdRef:ChangeDetectorRef, - public injector:Injector, - ) { - super(); - } - ngAfterViewInit():void { this.container = this.scrollContainer.nativeElement as HTMLElement; this.pane = this.scrollPane.nativeElement as HTMLElement; diff --git a/frontend/src/app/shared/components/time_entries/edit/trigger-actions-entry.component.ts b/frontend/src/app/shared/components/time_entries/edit/trigger-actions-entry.component.ts index 90fb7116b2be..4fc79783b3c2 100644 --- a/frontend/src/app/shared/components/time_entries/edit/trigger-actions-entry.component.ts +++ b/frontend/src/app/shared/components/time_entries/edit/trigger-actions-entry.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Injector } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Injector, inject } from '@angular/core'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; @@ -34,6 +34,8 @@ import { TurboRequestsService } from 'core-app/core/turbo/turbo-requests.service standalone: false, }) export class TriggerActionsEntryComponent { + readonly injector = inject(Injector); + @InjectField() readonly apiv3Service:ApiV3Service; @InjectField() readonly toastService:ToastService; @@ -55,9 +57,6 @@ export class TriggerActionsEntryComponent { areYouSure: this.i18n.t('js.text_are_you_sure'), }; - constructor(readonly injector:Injector) { - } - editTimeEntry() { void this.loadEntry().subscribe((entry:TimeEntryResource) => { document.addEventListener('dialog:close', (event:CustomEvent) => { diff --git a/frontend/src/app/shared/components/time_entries/timer/stop-existing-timer-modal.component.ts b/frontend/src/app/shared/components/time_entries/timer/stop-existing-timer-modal.component.ts index 4e723268c257..12d3d60454c0 100644 --- a/frontend/src/app/shared/components/time_entries/timer/stop-existing-timer-modal.component.ts +++ b/frontend/src/app/shared/components/time_entries/timer/stop-existing-timer-modal.component.ts @@ -26,19 +26,8 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - HostBinding, - Inject, - OnInit, - ViewEncapsulation, -} from '@angular/core'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { ChangeDetectionStrategy, Component, HostBinding, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { TimeEntryResource } from 'core-app/features/hal/resources/time-entry-resource'; import { Observable, @@ -59,6 +48,9 @@ import { StateService } from '@uirouter/core'; standalone: false, }) export class StopExistingTimerModalComponent extends OpModalComponent implements OnInit { + readonly state = inject(StateService); + readonly I18n = inject(I18nService); + @HostBinding('class.op-timer-stop-modal') className = true; public active:TimeEntryResource; @@ -80,16 +72,6 @@ export class StopExistingTimerModalComponent extends OpModalComponent implements tracking_time: this.I18n.t('js.timer.tracking_time'), }; - constructor( - readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly state:StateService, - readonly I18n:I18nService, - ) { - super(locals, cdRef, elementRef); - } - ngOnInit() { super.ngOnInit(); this.active = this.locals.timer as TimeEntryResource; diff --git a/frontend/src/app/shared/components/time_entries/timer/timer-account-menu.component.ts b/frontend/src/app/shared/components/time_entries/timer/timer-account-menu.component.ts index 29e3e6df8eb1..86a9dd959cf7 100644 --- a/frontend/src/app/shared/components/time_entries/timer/timer-account-menu.component.ts +++ b/frontend/src/app/shared/components/time_entries/timer/timer-account-menu.component.ts @@ -1,13 +1,4 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - HostBinding, - Injector, - OnInit, - ViewEncapsulation, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostBinding, Injector, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { TimeEntryTimerService } from 'core-app/shared/components/time_entries/services/time-entry-timer.service'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { TimeEntryResource } from 'core-app/features/hal/resources/time-entry-resource'; @@ -31,6 +22,13 @@ export const timerAccountSelector = 'op-timer-account-menu'; standalone: false, }) export class TimerAccountMenuComponent extends UntilDestroyedMixin implements OnInit { + readonly injector = inject(Injector); + readonly elementRef = inject>(ElementRef); + readonly timeEntryService = inject(TimeEntryTimerService); + readonly cdRef = inject(ChangeDetectorRef); + readonly I18n = inject(I18nService); + readonly toastService = inject(ToastService); + @HostBinding('class.op-timer-account-menu') className = true; @InjectField() PathHelper:PathHelperService; @InjectField() TurboRequests:TurboRequestsService; @@ -50,17 +48,6 @@ export class TimerAccountMenuComponent extends UntilDestroyedMixin implements On timer_already_stopped: this.I18n.t('js.timer.timer_already_stopped'), }; - constructor( - readonly injector:Injector, - readonly elementRef:ElementRef, - readonly timeEntryService:TimeEntryTimerService, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - readonly toastService:ToastService, - ) { - super(); - } - ngOnInit() { const parent = this.elementRef.nativeElement.parentElement!; parent.hidden = true; diff --git a/frontend/src/app/shared/components/toaster/toast.component.ts b/frontend/src/app/shared/components/toaster/toast.component.ts index 0d6748304fbc..30924c5e4c57 100644 --- a/frontend/src/app/shared/components/toaster/toast.component.ts +++ b/frontend/src/app/shared/components/toaster/toast.component.ts @@ -26,12 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - Input, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core'; import { BehaviorSubject, Observable, @@ -52,6 +47,9 @@ import { IToast, ToastService, ToastType } from 'core-app/shared/components/toas standalone: false, }) export class ToastComponent implements OnInit { + readonly I18n = inject(I18nService); + readonly toastService = inject(ToastService); + @Input() public toast:IToast; public text = { @@ -72,12 +70,6 @@ export class ToastComponent implements OnInit { public loading$ = new BehaviorSubject(false); - constructor( - readonly I18n:I18nService, - readonly toastService:ToastService, - ) { - } - ngOnInit():void { this.type = this.toast.type; this.icon = this.toast.icon ? `toast-icon icon-${this.toast.icon}` : ''; diff --git a/frontend/src/app/shared/components/toaster/toast.service.ts b/frontend/src/app/shared/components/toaster/toast.service.ts index c1e6192c713b..2acf4fd63884 100644 --- a/frontend/src/app/shared/components/toaster/toast.service.ts +++ b/frontend/src/app/shared/components/toaster/toast.service.ts @@ -29,7 +29,7 @@ import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; import { input, State } from '@openproject/reactivestates'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpErrorResponse, HttpEvent } from '@angular/common/http'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -54,13 +54,13 @@ export interface IToast { @Injectable({ providedIn: 'root' }) export class ToastService { + readonly configurationService = inject(ConfigurationService); + readonly I18n = inject(I18nService); + // The current stack of toasters private stack = input([]); - constructor( - readonly configurationService:ConfigurationService, - readonly I18n:I18nService, - ) { + constructor() { window.addEventListener(OPToastEvent, ({ detail:toast }:CustomEvent) => { this.add(toast); }); diff --git a/frontend/src/app/shared/components/toaster/toasts-container.component.ts b/frontend/src/app/shared/components/toaster/toasts-container.component.ts index 38d915291913..d8d1a4590824 100644 --- a/frontend/src/app/shared/components/toaster/toasts-container.component.ts +++ b/frontend/src/app/shared/components/toaster/toasts-container.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, inject } from '@angular/core'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { IToast, ToastService } from './toast.service'; @@ -46,14 +46,10 @@ import { IToast, ToastService } from './toast.service'; standalone: false, }) export class ToastsContainerComponent extends UntilDestroyedMixin implements OnInit { - public stack:IToast[] = []; + readonly toastService = inject(ToastService); + readonly cdRef = inject(ChangeDetectorRef); - constructor( - readonly toastService:ToastService, - readonly cdRef:ChangeDetectorRef, - ) { - super(); - } + public stack:IToast[] = []; ngOnInit():void { this.toastService diff --git a/frontend/src/app/shared/components/user-link/user-link.component.ts b/frontend/src/app/shared/components/user-link/user-link.component.ts index e8b5e83576a2..611989dcfd35 100644 --- a/frontend/src/app/shared/components/user-link/user-link.component.ts +++ b/frontend/src/app/shared/components/user-link/user-link.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, inject } from '@angular/core'; import { UserResource } from 'core-app/features/hal/resources/user-resource'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -51,10 +51,10 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service standalone: false, }) export class UserLinkComponent { - @Input() user:UserResource; + readonly I18n = inject(I18nService); + readonly pathHelperService = inject(PathHelperService); - constructor(readonly I18n:I18nService, readonly pathHelperService:PathHelperService) { - } + @Input() user:UserResource; public get href() { return this.user && this.user.showUserPath; diff --git a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab-inner.component.ts b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab-inner.component.ts index 14e2c569689b..71b46953c8df 100644 --- a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab-inner.component.ts +++ b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab-inner.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; @@ -18,19 +18,29 @@ import { WorkPackageFiltersService } from 'core-app/features/work-packages/compo changeDetection: ChangeDetectionStrategy.Default, }) export class WpGraphConfigurationFiltersTabInnerComponent extends QuerySpacedTabComponent implements TabComponent, OnInit { + readonly I18n:I18nService; + readonly wpTableFilters = inject(WorkPackageViewFiltersService); + readonly wpFiltersService = inject(WorkPackageFiltersService); + readonly wpStatesInitialization:WorkPackageStatesInitializationService; + readonly wpGraphConfiguration:WpGraphConfigurationService; + private cdRef = inject(ChangeDetectorRef); + public filters:QueryFilterInstanceResource[] = []; public text = { multiSelectLabel: this.I18n.t('js.work_packages.label_column_multiselect'), }; - constructor(readonly I18n:I18nService, - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly wpFiltersService:WorkPackageFiltersService, - readonly wpStatesInitialization:WorkPackageStatesInitializationService, - readonly wpGraphConfiguration:WpGraphConfigurationService, - private cdRef:ChangeDetectorRef) { + constructor() { + const I18n = inject(I18nService); + const wpStatesInitialization = inject(WorkPackageStatesInitializationService); + const wpGraphConfiguration = inject(WpGraphConfigurationService); + super(I18n, wpStatesInitialization, wpGraphConfiguration); + + this.I18n = I18n; + this.wpStatesInitialization = wpStatesInitialization; + this.wpGraphConfiguration = wpGraphConfiguration; } ngOnInit() { diff --git a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab-inner.component.ts b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab-inner.component.ts index baffe7b4c010..c16f66b8c0e1 100644 --- a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab-inner.component.ts +++ b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab-inner.component.ts @@ -1,6 +1,6 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, inject } from '@angular/core'; import { WpGraphConfigurationService } from 'core-app/shared/components/work-package-graphs/configuration/wp-graph-configuration.service'; import { WorkPackageStatesInitializationService } from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; @@ -23,6 +23,12 @@ interface OpChartType { changeDetection: ChangeDetectionStrategy.Default, }) export class WpGraphConfigurationSettingsTabInnerComponent extends QuerySpacedTabComponent implements TabComponent, OnInit { + readonly I18n:I18nService; + readonly wpTableGroupBy = inject(WorkPackageViewGroupByService); + readonly wpStatesInitialization:WorkPackageStatesInitializationService; + readonly wpGraphConfiguration:WpGraphConfigurationService; + private cdRef = inject(ChangeDetectorRef); + // Grouping public availableGroups:QueryGroupByResource[] = []; @@ -35,12 +41,16 @@ export class WpGraphConfigurationSettingsTabInnerComponent extends QuerySpacedTa chart_type: this.I18n.t('js.chart.type'), }; - constructor(readonly I18n:I18nService, - readonly wpTableGroupBy:WorkPackageViewGroupByService, - readonly wpStatesInitialization:WorkPackageStatesInitializationService, - readonly wpGraphConfiguration:WpGraphConfigurationService, - private cdRef:ChangeDetectorRef) { + constructor() { + const I18n = inject(I18nService); + const wpStatesInitialization = inject(WorkPackageStatesInitializationService); + const wpGraphConfiguration = inject(WpGraphConfigurationService); + super(I18n, wpStatesInitialization, wpGraphConfiguration); + + this.I18n = I18n; + this.wpStatesInitialization = wpStatesInitialization; + this.wpGraphConfiguration = wpGraphConfiguration; } public onSave() { diff --git a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/wp-graph-configuration.modal.ts b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/wp-graph-configuration.modal.ts index d7b4b9d6ad8a..bdbbe1f1c625 100644 --- a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/wp-graph-configuration.modal.ts +++ b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/wp-graph-configuration.modal.ts @@ -1,21 +1,5 @@ -import { - ApplicationRef, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ComponentFactoryResolver, - ElementRef, - Inject, - InjectionToken, - Injector, - OnDestroy, - OnInit, - Optional, - ViewChild, -} from '@angular/core'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { ApplicationRef, ChangeDetectionStrategy, Component, ComponentFactoryResolver, ElementRef, InjectionToken, Injector, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { ActiveTabInterface, @@ -38,7 +22,15 @@ export const WpTableConfigurationModalPrependToken = new InjectionToken | null>(WpTableConfigurationModalPrependToken, { optional: true }); + readonly I18n = inject(I18nService); + readonly injector = inject(Injector); + readonly appRef = inject(ApplicationRef); + readonly componentFactoryResolver = inject(ComponentFactoryResolver); + readonly loadingIndicator = inject(LoadingIndicatorService); + readonly notificationService = inject(WorkPackageNotificationService); + readonly configurationService = inject(ConfigurationService); + readonly graphConfiguration = inject(WpGraphConfigurationService); public text = { title: this.I18n.t('js.chart.modal_title'), @@ -56,23 +48,6 @@ export class WpGraphConfigurationModalComponent extends OpModalComponent impleme // And a reference to the actual portal host interface public tabPortalHost:TabPortalOutlet; - constructor( - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - @Optional() @Inject(WpTableConfigurationModalPrependToken) public prependModalComponent:ComponentType|null, - readonly I18n:I18nService, - readonly injector:Injector, - readonly appRef:ApplicationRef, - readonly componentFactoryResolver:ComponentFactoryResolver, - readonly loadingIndicator:LoadingIndicatorService, - readonly notificationService:WorkPackageNotificationService, - readonly cdRef:ChangeDetectorRef, - readonly configurationService:ConfigurationService, - readonly elementRef:ElementRef, - readonly graphConfiguration:WpGraphConfigurationService, - ) { - super(locals, cdRef, elementRef); - } - ngOnInit():void { this.element = this.elementRef.nativeElement as HTMLElement; diff --git a/frontend/src/app/shared/components/work-package-graphs/configuration/wp-graph-configuration.service.ts b/frontend/src/app/shared/components/work-package-graphs/configuration/wp-graph-configuration.service.ts index fe2deda2b2c5..728866c06606 100644 --- a/frontend/src/app/shared/components/work-package-graphs/configuration/wp-graph-configuration.service.ts +++ b/frontend/src/app/shared/components/work-package-graphs/configuration/wp-graph-configuration.service.ts @@ -2,7 +2,7 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WpGraphConfigurationSettingsTabComponent } from 'core-app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab.component'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { TabInterface } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { WpGraphConfigurationFiltersTabComponent } from 'core-app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab.component'; import { ChartOptions } from 'chart.js'; import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; @@ -22,6 +22,11 @@ import { @Injectable() export class WpGraphConfigurationService { + readonly I18n = inject(I18nService); + readonly apiv3Service = inject(ApiV3Service); + readonly notificationService = inject(WorkPackageNotificationService); + readonly currentProject = inject(CurrentProjectService); + private _configuration:WpGraphConfiguration; private _globalScope = false; @@ -30,13 +35,6 @@ export class WpGraphConfigurationService { private _formsPromise:Promise|null; - constructor( - readonly I18n:I18nService, - readonly apiv3Service:ApiV3Service, - readonly notificationService:WorkPackageNotificationService, - readonly currentProject:CurrentProjectService, - ) { } - public persistAndReload():Promise { return this .persistChanges() diff --git a/frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component.ts b/frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component.ts index 1361dd9e7fec..df65d4d73999 100644 --- a/frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component.ts +++ b/frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input, SimpleChanges, OnChanges } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, SimpleChanges, OnChanges, inject } from '@angular/core'; import { WorkPackageTableConfiguration } from 'core-app/features/work-packages/components/wp-table/wp-table-configuration'; import { ChartOptions } from 'chart.js'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -35,6 +35,8 @@ interface ChartDataSet { changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageEmbeddedGraphComponent implements OnChanges { + readonly i18n = inject(I18nService); + @Input() public datasets:WorkPackageEmbeddedGraphDataset[]; @Input() public chartOptions:ChartOptions; @@ -59,8 +61,6 @@ export class WorkPackageEmbeddedGraphComponent implements OnChanges { noResults: this.i18n.t('js.work_packages.no_results.title'), }; - constructor(readonly i18n:I18nService) {} - ngOnChanges(changes:SimpleChanges) { if (changes.datasets) { this.setChartOptions(); diff --git a/frontend/src/app/shared/components/work-package-graphs/overview/wp-overview-graph.component.ts b/frontend/src/app/shared/components/work-package-graphs/overview/wp-overview-graph.component.ts index 0147697aa8db..593632cf1f11 100644 --- a/frontend/src/app/shared/components/work-package-graphs/overview/wp-overview-graph.component.ts +++ b/frontend/src/app/shared/components/work-package-graphs/overview/wp-overview-graph.component.ts @@ -1,12 +1,4 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Input, - OnInit, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild, inject } from '@angular/core'; import { WorkPackageEmbeddedGraphComponent, WorkPackageEmbeddedGraphDataset, @@ -33,6 +25,11 @@ import { standalone: false, }) export class WorkPackageOverviewGraphComponent implements OnInit { + readonly elementRef = inject>(ElementRef); + readonly I18n = inject(I18nService); + readonly graphConfigurationService = inject(WpGraphConfigurationService); + protected readonly cdr = inject(ChangeDetectorRef); + @Input() initialFilters:any; @Input() globalScope:boolean; @@ -53,12 +50,9 @@ export class WorkPackageOverviewGraphComponent implements OnInit { public error:string|null = null; - constructor( - readonly elementRef:ElementRef, - readonly I18n:I18nService, - readonly graphConfigurationService:WpGraphConfigurationService, - protected readonly cdr:ChangeDetectorRef, - ) { + constructor() { + const I18n = this.I18n; + this.availableGroupBy = [{ label: I18n.t('js.work_packages.properties.category'), key: 'category' }, { label: I18n.t('js.work_packages.properties.type'), key: 'type' }, { label: I18n.t('js.work_packages.properties.status'), key: 'status' }, diff --git a/frontend/src/app/shared/components/work-packages/alternative-search.service.ts b/frontend/src/app/shared/components/work-packages/alternative-search.service.ts index feb4c7a877ee..01ab8c7e6703 100644 --- a/frontend/src/app/shared/components/work-packages/alternative-search.service.ts +++ b/frontend/src/app/shared/components/work-packages/alternative-search.service.ts @@ -1,12 +1,11 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { QueryFilterResource } from 'core-app/features/hal/resources/query-filter-resource'; @Injectable({ providedIn: 'root' }) export class AlternativeSearchService { - constructor( - readonly I18n:I18nService, - ) { } + readonly I18n = inject(I18nService); + private specialSearchStrings = { percentComplete: this.I18n.t('js.work_packages.properties.percentComplete'), diff --git a/frontend/src/app/shared/directives/a11y/keyboard-shortcut.service.ts b/frontend/src/app/shared/directives/a11y/keyboard-shortcut.service.ts index 25018e69176b..071c02d885d7 100644 --- a/frontend/src/app/shared/directives/a11y/keyboard-shortcut.service.ts +++ b/frontend/src/app/shared/directives/a11y/keyboard-shortcut.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { FocusHelperService } from 'core-app/shared/directives/focus/focus-helper'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @@ -51,6 +51,11 @@ const accessibleListSelector = 'table.keyboard-accessible-list'; providedIn: 'root', }) export class KeyboardShortcutService { + private readonly PathHelper = inject(PathHelperService); + private readonly FocusHelper = inject(FocusHelperService); + private readonly currentProject = inject(CurrentProjectService); + private readonly configurationService = inject(ConfigurationService); + // maybe move it to a .constant private shortcuts:Record void> = { '?': () => this.showHelpModal(), @@ -73,13 +78,6 @@ export class KeyboardShortcutService { 'j': () => this.focusNextItem(), }; - constructor( - private readonly PathHelper:PathHelperService, - private readonly FocusHelper:FocusHelperService, - private readonly currentProject:CurrentProjectService, - private readonly configurationService:ConfigurationService, - ) {} - /** * Register the keyboard shortcuts. */ diff --git a/frontend/src/app/shared/directives/focus/autofocus.directive.ts b/frontend/src/app/shared/directives/focus/autofocus.directive.ts index 36d595d636fc..16b37c040255 100644 --- a/frontend/src/app/shared/directives/focus/autofocus.directive.ts +++ b/frontend/src/app/shared/directives/focus/autofocus.directive.ts @@ -1,9 +1,4 @@ -import { - AfterViewInit, - Directive, - ElementRef, - Input, -} from '@angular/core'; +import { AfterViewInit, Directive, ElementRef, Input, inject } from '@angular/core'; import { FocusHelperService } from './focus-helper'; @Directive({ @@ -11,12 +6,10 @@ import { FocusHelperService } from './focus-helper'; standalone: false, }) export class AutofocusDirective implements AfterViewInit { - @Input('opAutofocus') public condition:string|boolean = true; + readonly FocusHelper = inject(FocusHelperService); + readonly elementRef = inject(ElementRef); - constructor( - readonly FocusHelper:FocusHelperService, - readonly elementRef:ElementRef, - ) { } + @Input('opAutofocus') public condition:string|boolean = true; ngAfterViewInit():void { this.updateFocus(); diff --git a/frontend/src/app/shared/directives/focus/focus-within.directive.ts b/frontend/src/app/shared/directives/focus/focus-within.directive.ts index 2e54670550bc..286a38bb181a 100644 --- a/frontend/src/app/shared/directives/focus/focus-within.directive.ts +++ b/frontend/src/app/shared/directives/focus/focus-within.directive.ts @@ -28,9 +28,7 @@ import { BehaviorSubject } from 'rxjs'; import { auditTime } from 'rxjs/operators'; -import { - Directive, ElementRef, Input, OnInit, -} from '@angular/core'; +import { Directive, ElementRef, Input, OnInit, inject } from '@angular/core'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; // with courtesy of http://stackoverflow.com/a/29722694/3206935 @@ -40,11 +38,9 @@ import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destr standalone: false, }) export class FocusWithinDirective extends UntilDestroyedMixin implements OnInit { - @Input() public selector:string; + readonly elementRef = inject>(ElementRef); - constructor(readonly elementRef:ElementRef) { - super(); - } + @Input() public selector:string; ngOnInit() { const element = this.elementRef.nativeElement; diff --git a/frontend/src/app/shared/directives/op-drag-scroll/op-drag-scroll.directive.ts b/frontend/src/app/shared/directives/op-drag-scroll/op-drag-scroll.directive.ts index 49996b826f74..978d454a9ba2 100644 --- a/frontend/src/app/shared/directives/op-drag-scroll/op-drag-scroll.directive.ts +++ b/frontend/src/app/shared/directives/op-drag-scroll/op-drag-scroll.directive.ts @@ -25,7 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { Directive, ElementRef, OnInit } from '@angular/core'; +import { Directive, ElementRef, OnInit, inject } from '@angular/core'; declare global { interface GlobalEventHandlersEventMap { @@ -38,8 +38,8 @@ declare global { standalone: false, }) export class OpDragScrollDirective implements OnInit { - constructor(readonly elementRef:ElementRef) { - } + readonly elementRef = inject>(ElementRef); + ngOnInit() { const element = this.elementRef.nativeElement; diff --git a/frontend/src/app/shared/directives/search-highlight.directive.ts b/frontend/src/app/shared/directives/search-highlight.directive.ts index 8bfcffa3b0ce..68280c727a41 100644 --- a/frontend/src/app/shared/directives/search-highlight.directive.ts +++ b/frontend/src/app/shared/directives/search-highlight.directive.ts @@ -1,18 +1,13 @@ -import { - AfterViewChecked, - Directive, - ElementRef, - Input, -} from '@angular/core'; +import { AfterViewChecked, Directive, ElementRef, Input, inject } from '@angular/core'; @Directive({ selector: '[opSearchHighlight]', standalone: false, }) export class OpSearchHighlightDirective implements AfterViewChecked { - @Input('opSearchHighlight') public query = ''; + readonly elementRef = inject(ElementRef); - constructor(readonly elementRef:ElementRef) { } + @Input('opSearchHighlight') public query = ''; ngAfterViewChecked():void { let el = this.elementRef.nativeElement as HTMLElement; diff --git a/frontend/src/app/shared/helpers/drag-and-drop/drag-and-drop.service.ts b/frontend/src/app/shared/helpers/drag-and-drop/drag-and-drop.service.ts index 21e60dba4b51..686d9606d9fd 100644 --- a/frontend/src/app/shared/helpers/drag-and-drop/drag-and-drop.service.ts +++ b/frontend/src/app/shared/helpers/drag-and-drop/drag-and-drop.service.ts @@ -1,6 +1,4 @@ -import { - Inject, Injectable, Injector, OnDestroy, DOCUMENT -} from '@angular/core'; +import { Injectable, Injector, OnDestroy, DOCUMENT, inject } from '@angular/core'; import { DomAutoscrollService } from 'core-app/shared/helpers/drag-and-drop/dom-autoscroll.service'; import { findIndex, reinsert } from 'core-app/shared/helpers/drag-and-drop/drag-and-drop.helpers'; import dragula, { Drake } from 'dragula'; @@ -32,6 +30,9 @@ export interface DragMember { @Injectable() export class DragAndDropService implements OnDestroy { + private document = inject(DOCUMENT); + readonly injector = inject(Injector); + public drake:Drake|null = null; public members:DragMember[] = []; @@ -44,8 +45,7 @@ export class DragAndDropService implements OnDestroy { } }; - constructor(@Inject(DOCUMENT) private document:Document, - readonly injector:Injector) { + constructor() { this.document.documentElement.addEventListener('keydown', this.escapeListener); } diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index e8059dc30777..0775f06eb3ef 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ import { FormsModule } from '@angular/forms'; -import { Injector, NgModule } from '@angular/core'; +import { Injector, NgModule, inject } from '@angular/core'; import { A11yModule } from '@angular/cdk/a11y'; import { UIRouterGlobals } from '@uirouter/core'; import { NgSelectModule } from '@ng-select/ng-select'; @@ -222,7 +222,9 @@ export function bootstrapModule(injector:Injector):void { ], }) export class OpSharedModule { - constructor(injector:Injector) { + constructor() { + const injector = inject(Injector); + bootstrapModule(injector); } } diff --git a/frontend/src/app/spot/components/checkbox/checkbox.component.ts b/frontend/src/app/spot/components/checkbox/checkbox.component.ts index f27bcc6ddd03..7d9e44a56582 100644 --- a/frontend/src/app/spot/components/checkbox/checkbox.component.ts +++ b/frontend/src/app/spot/components/checkbox/checkbox.component.ts @@ -1,15 +1,4 @@ -import { - Component, - ElementRef, - EventEmitter, - forwardRef, - HostBinding, - Input, - Output, - ViewChild, - ChangeDetectionStrategy, - ChangeDetectorRef, -} from '@angular/core'; +import { Component, ElementRef, EventEmitter, forwardRef, HostBinding, Input, Output, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef, inject } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -29,6 +18,8 @@ export type SpotCheckboxState = true|false|null; standalone: false, }) export class SpotCheckboxComponent implements ControlValueAccessor { + readonly cdRef = inject(ChangeDetectorRef); + @HostBinding('class.spot-checkbox') public className = true; @ViewChild('input') public input:ElementRef; @@ -61,10 +52,6 @@ export class SpotCheckboxComponent implements ControlValueAccessor { */ @Output() checkedChange = new EventEmitter(); - constructor( - readonly cdRef:ChangeDetectorRef, - ) {} - onStateChange():void { const value = (this.input.nativeElement as HTMLInputElement).checked; this.checkedChange.emit(value); diff --git a/frontend/src/app/spot/components/drop-modal/drop-modal-portal.component.ts b/frontend/src/app/spot/components/drop-modal/drop-modal-portal.component.ts index 1e1289370d8c..354a23199c76 100644 --- a/frontend/src/app/spot/components/drop-modal/drop-modal-portal.component.ts +++ b/frontend/src/app/spot/components/drop-modal/drop-modal-portal.component.ts @@ -1,11 +1,4 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - HostBinding, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostBinding, OnInit, inject } from '@angular/core'; import { SpotDropModalTeleportationService, TeleportInstance } from './drop-modal-teleportation.service'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @@ -16,18 +9,14 @@ import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destr standalone: false, }) export class SpotDropModalPortalComponent extends UntilDestroyedMixin implements OnInit { + readonly cdRef = inject(ChangeDetectorRef); + readonly template$ = inject(SpotDropModalTeleportationService); + readonly elementRef = inject>(ElementRef); + @HostBinding('class.spot-drop-modal-portal') className = true; template:TeleportInstance|null = null; - constructor( - readonly cdRef:ChangeDetectorRef, - readonly template$:SpotDropModalTeleportationService, - readonly elementRef:ElementRef, - ) { - super(); - } - ngOnInit() { this .template$ diff --git a/frontend/src/app/spot/components/drop-modal/drop-modal.component.ts b/frontend/src/app/spot/components/drop-modal/drop-modal.component.ts index b2b3fa3f6bbd..111d71550646 100644 --- a/frontend/src/app/spot/components/drop-modal/drop-modal.component.ts +++ b/frontend/src/app/spot/components/drop-modal/drop-modal.component.ts @@ -1,16 +1,4 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - HostBinding, - Input, - OnDestroy, - Output, - TemplateRef, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, Input, OnDestroy, Output, TemplateRef, ViewChild, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { findAllFocusableElementsWithin } from 'core-app/shared/helpers/focus-helpers'; import { SpotDropModalTeleportationService } from './drop-modal-teleportation.service'; @@ -25,6 +13,11 @@ import { autoUpdate, computePosition, flip, limitShift, Placement, shift } from standalone: false, }) export class SpotDropModalComponent implements OnDestroy { + readonly i18n = inject(I18nService); + readonly elementRef = inject(ElementRef); + readonly cdRef = inject(ChangeDetectorRef); + private teleportationService = inject(SpotDropModalTeleportationService); + @HostBinding('class.spot-drop-modal') public className = true; /** @@ -99,13 +92,6 @@ export class SpotDropModalComponent implements OnDestroy { @ViewChild('focusGrabber') focusGrabber:ElementRef; - constructor( - readonly i18n:I18nService, - readonly elementRef:ElementRef, - readonly cdRef:ChangeDetectorRef, - private teleportationService:SpotDropModalTeleportationService, - ) {} - open() { this._opened = true; this.updateAppHeight(); diff --git a/frontend/src/app/spot/components/form-field/form-field.component.ts b/frontend/src/app/spot/components/form-field/form-field.component.ts index 03bbd08d3737..92bcbcb1eec6 100644 --- a/frontend/src/app/spot/components/form-field/form-field.component.ts +++ b/frontend/src/app/spot/components/form-field/form-field.component.ts @@ -1,6 +1,4 @@ -import { - ChangeDetectionStrategy, Component, ContentChild, HostBinding, Input, Optional, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ContentChild, HostBinding, Input, inject } from '@angular/core'; import { AbstractControl, FormGroupDirective, NgControl } from '@angular/forms'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -14,6 +12,9 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; changeDetection: ChangeDetectionStrategy.Default, }) export class SpotFormFieldComponent { + private _formGroupDirective = inject(FormGroupDirective, { optional: true }); + readonly I18n = inject(I18nService); + @HostBinding('class.spot-form-field') className = true; @HostBinding('class.spot-form-field_invalid') get errorClassName():boolean { @@ -93,7 +94,7 @@ export class SpotFormFieldComponent { } if (this.showValidationErrorOn === 'submit') { - return this.formControl.invalid && this._formGroupDirective?.submitted; + return this.formControl.invalid && (this._formGroupDirective?.submitted ?? false); } if (this.showValidationErrorOn === 'blur') { return this.formControl.invalid && this.formControl.touched; } if (this.showValidationErrorOn === 'change') { @@ -102,9 +103,4 @@ export class SpotFormFieldComponent { return false; } - - constructor( - @Optional() private _formGroupDirective:FormGroupDirective, - readonly I18n:I18nService, - ) {} } diff --git a/frontend/src/app/spot/components/selector-field/selector-field.component.ts b/frontend/src/app/spot/components/selector-field/selector-field.component.ts index 4c5346ebd2b6..0db7cac25882 100644 --- a/frontend/src/app/spot/components/selector-field/selector-field.component.ts +++ b/frontend/src/app/spot/components/selector-field/selector-field.component.ts @@ -1,11 +1,4 @@ -import { - ChangeDetectionStrategy, - Component, - ContentChild, - HostBinding, - Input, - Optional, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ContentChild, HostBinding, Input, inject } from '@angular/core'; import { AbstractControl, FormGroupDirective, @@ -22,6 +15,8 @@ import { changeDetection: ChangeDetectionStrategy.Default, }) export class SpotSelectorFieldComponent { + private formGroupDirective = inject(FormGroupDirective, { optional: true }); + @HostBinding('class.spot-form-field') className = true; @HostBinding('class.spot-selector-field') classNameCheckbox = true; @@ -96,7 +91,7 @@ export class SpotSelectorFieldComponent { } if (this.showValidationErrorOn === 'submit') { - return this.formControl.invalid && this.formGroupDirective?.submitted; + return this.formControl.invalid && (this.formGroupDirective?.submitted ?? false); } if (this.showValidationErrorOn === 'blur') { return this.formControl.invalid && this.formControl.touched; @@ -107,8 +102,4 @@ export class SpotSelectorFieldComponent { return false; } - - constructor( - @Optional() private formGroupDirective:FormGroupDirective, - ) {} } diff --git a/frontend/src/app/spot/components/switch/switch.component.ts b/frontend/src/app/spot/components/switch/switch.component.ts index 87e98e8e9c80..4a66ba3bc16b 100644 --- a/frontend/src/app/spot/components/switch/switch.component.ts +++ b/frontend/src/app/spot/components/switch/switch.component.ts @@ -1,15 +1,4 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - forwardRef, - HostBinding, - Input, - Output, - ViewChild, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, forwardRef, HostBinding, Input, Output, ViewChild, inject } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; @@ -28,6 +17,9 @@ export type SpotSwitchState = boolean; standalone: false, }) export class SpotSwitchComponent implements ControlValueAccessor { + elementRef = inject(ElementRef); + cdRef = inject(ChangeDetectorRef); + @HostBinding('class.spot-switch') public className = true; @ViewChild('input') public input:ElementRef; @@ -60,10 +52,7 @@ export class SpotSwitchComponent implements ControlValueAccessor { */ @Output() checkedChange = new EventEmitter(); - constructor( - public elementRef:ElementRef, - public cdRef:ChangeDetectorRef, - ) { + constructor() { populateInputsFromDataset(this); } diff --git a/frontend/src/app/spot/components/text-field/text-field.component.ts b/frontend/src/app/spot/components/text-field/text-field.component.ts index 1b28eb903f2f..fa5002e32576 100644 --- a/frontend/src/app/spot/components/text-field/text-field.component.ts +++ b/frontend/src/app/spot/components/text-field/text-field.component.ts @@ -1,16 +1,4 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - HostBinding, - HostListener, - Input, - Output, - ViewChild, - forwardRef, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, Output, ViewChild, forwardRef, inject } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, @@ -31,6 +19,8 @@ import { changeDetection: ChangeDetectionStrategy.Default, }) export class SpotTextFieldComponent implements ControlValueAccessor { + private cdRef = inject(ChangeDetectorRef); + @HostBinding('class.spot-text-field') public className = true; @HostBinding('class.spot-text-field_focused') public focused = false; @@ -99,10 +89,6 @@ export class SpotTextFieldComponent implements ControlValueAccessor { @Output() public inputBlur = new EventEmitter(); - constructor( - private cdRef:ChangeDetectorRef, - ) {} - onInputFocus(event:FocusEvent):void { this.focused = true; this.cdRef.markForCheck(); diff --git a/frontend/src/app/spot/components/toggle/toggle.component.ts b/frontend/src/app/spot/components/toggle/toggle.component.ts index 673738c6b2e4..051e954cecab 100644 --- a/frontend/src/app/spot/components/toggle/toggle.component.ts +++ b/frontend/src/app/spot/components/toggle/toggle.component.ts @@ -1,13 +1,4 @@ -import { - ChangeDetectorRef, - ChangeDetectionStrategy, - Component, - EventEmitter, - forwardRef, - HostBinding, - Input, - Output, -} from '@angular/core'; +import { ChangeDetectorRef, ChangeDetectionStrategy, Component, EventEmitter, forwardRef, HostBinding, Input, Output, inject } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; export interface SpotToggleOption { @@ -27,6 +18,8 @@ export interface SpotToggleOption { standalone: false, }) export class SpotToggleComponent implements ControlValueAccessor { + private cdRef = inject(ChangeDetectorRef); + // TODO: These old styles will need to be replaced @HostBinding('class.form--field-inline-buttons-container') public classNameOld = true; @@ -70,10 +63,6 @@ export class SpotToggleComponent implements ControlValueAccessor { */ @Output() valueChange = new EventEmitter(); - constructor( - private cdRef:ChangeDetectorRef, - ) {} - writeValue(value:T):void { this.value = value; this.cdRef.markForCheck(); diff --git a/modules/avatars/frontend/module/avatar-upload-form/avatar-upload-form.component.ts b/modules/avatars/frontend/module/avatar-upload-form/avatar-upload-form.component.ts index 2d47a6ac7353..1ddb98d6269b 100644 --- a/modules/avatars/frontend/module/avatar-upload-form/avatar-upload-form.component.ts +++ b/modules/avatars/frontend/module/avatar-upload-form/avatar-upload-form.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild, } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild, inject } from '@angular/core'; import { resizeFile } from 'core-app/shared/helpers/images/resizer'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -44,6 +44,12 @@ import { HttpErrorResponse } from '@angular/common/http'; standalone: false, }) export class AvatarUploadFormComponent implements OnInit { + protected I18n = inject(I18nService); + protected elementRef = inject(ElementRef); + protected cdRef = inject(ChangeDetectorRef); + protected toastService = inject(ToastService); + protected uploadService = inject(OpUploadService); + public form:any; public target:string; @@ -71,14 +77,6 @@ export class AvatarUploadFormComponent implements OnInit { preview: this.I18n.t('js.label_preview'), }; - public constructor( - protected I18n:I18nService, - protected elementRef:ElementRef, - protected cdRef:ChangeDetectorRef, - protected toastService:ToastService, - protected uploadService:OpUploadService, - ) { } - public ngOnInit() { const element = this.elementRef.nativeElement as HTMLElement; this.target = element.getAttribute('target') || ''; diff --git a/modules/avatars/frontend/module/avatar-upload.service.ts b/modules/avatars/frontend/module/avatar-upload.service.ts index 64d16ad71644..b96bcd1d9124 100644 --- a/modules/avatars/frontend/module/avatar-upload.service.ts +++ b/modules/avatars/frontend/module/avatar-upload.service.ts @@ -28,7 +28,7 @@ import { Observable } from 'rxjs'; import { share } from 'rxjs/operators'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpEvent } from '@angular/common/http'; import { IUploadFile, OpUploadService } from 'core-app/core/upload/upload.service'; @@ -39,11 +39,8 @@ export interface AvatarUploadFile extends IUploadFile { @Injectable() export class AvatarUploadService extends OpUploadService { - constructor( - private readonly http:HttpClient, - ) { - super(); - } + private readonly http = inject(HttpClient); + public upload( href:string, diff --git a/modules/avatars/frontend/module/main.ts b/modules/avatars/frontend/module/main.ts index f61d36470913..94e85a02d4a2 100644 --- a/modules/avatars/frontend/module/main.ts +++ b/modules/avatars/frontend/module/main.ts @@ -24,7 +24,7 @@ // // See COPYRIGHT and LICENSE files for more details. -import { Injector, NgModule } from '@angular/core'; +import { Injector, NgModule, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AvatarUploadFormComponent } from './avatar-upload-form/avatar-upload-form.component'; import { registerCustomElement } from 'core-app/shared/helpers/angular/custom-elements.helper'; @@ -38,7 +38,9 @@ import { registerCustomElement } from 'core-app/shared/helpers/angular/custom-el ], }) export class PluginModule { - constructor(injector:Injector) { + constructor() { + const injector = inject(Injector); + registerCustomElement('opce-avatar-upload-form', AvatarUploadFormComponent, { injector }); } } diff --git a/modules/budgets/frontend/module/main.ts b/modules/budgets/frontend/module/main.ts index 34c44dbc399a..09feb4e0e29c 100644 --- a/modules/budgets/frontend/module/main.ts +++ b/modules/budgets/frontend/module/main.ts @@ -24,7 +24,7 @@ // // See COPYRIGHT and LICENSE files for more details. -import { Injector, NgModule } from '@angular/core'; +import { Injector, NgModule, inject } from '@angular/core'; import { OpenProjectPluginContext } from 'core-app/features/plugins/plugin-context'; import { multiInput } from '@openproject/reactivestates'; import { PlannedCostsFormAugment } from 'core-app/features/plugins/linked/budgets/augment/planned-costs-form'; @@ -49,7 +49,9 @@ export function initializeCostsPlugin(injector:Injector) { @NgModule({}) export class PluginModule { - constructor(injector:Injector) { + constructor() { + const injector = inject(Injector); + initializeCostsPlugin(injector); } } diff --git a/modules/costs/frontend/module/main.ts b/modules/costs/frontend/module/main.ts index 5756afa08eed..6df0bfba8ad5 100644 --- a/modules/costs/frontend/module/main.ts +++ b/modules/costs/frontend/module/main.ts @@ -24,7 +24,7 @@ // // See COPYRIGHT and LICENSE files for more details. -import { Injector, NgModule } from '@angular/core'; +import { Injector, NgModule, inject } from '@angular/core'; import { OpenProjectPluginContext } from 'core-app/features/plugins/plugin-context'; import { CostsByTypeDisplayField } from './wp-display/costs-by-type-display-field.module'; import { CurrencyDisplayField } from './wp-display/currency-display-field.module'; @@ -64,7 +64,9 @@ export function initializeCostsPlugin(injector:Injector) { ], }) export class PluginModule { - constructor(injector:Injector) { + constructor() { + const injector = inject(Injector); + initializeCostsPlugin(injector); } } diff --git a/modules/github_integration/frontend/module/git-actions-menu/git-actions-menu.component.ts b/modules/github_integration/frontend/module/git-actions-menu/git-actions-menu.component.ts index e63848fd4f5f..aaa7b9275a8d 100644 --- a/modules/github_integration/frontend/module/git-actions-menu/git-actions-menu.component.ts +++ b/modules/github_integration/frontend/module/git-actions-menu/git-actions-menu.component.ts @@ -27,18 +27,10 @@ //++ import copy from 'copy-text-to-clipboard'; -import { - Component, - Inject, - Input, -} from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { GitActionsService } from '../git-actions/git-actions.service'; import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { OPContextMenuComponent } from "core-app/shared/components/op-context-menu/op-context-menu.component"; -import { - OpContextMenuLocalsMap, - OpContextMenuLocalsToken, -} from "core-app/shared/components/op-context-menu/op-context-menu.types"; import { I18nService } from "core-app/core/i18n/i18n.service"; import { ISnippet } from 'core-app/features/plugins/linked/openproject-github_integration/state/github-pull-request.model'; @@ -52,6 +44,9 @@ import { ISnippet } from 'core-app/features/plugins/linked/openproject-github_in standalone: false, }) export class GitActionsMenuComponent extends OPContextMenuComponent { + readonly I18n = inject(I18nService); + readonly gitActions = inject(GitActionsService); + @Input() public workPackage:WorkPackageResource; public text = { @@ -90,13 +85,8 @@ export class GitActionsMenuComponent extends OPContextMenuComponent { }, ]; - constructor( - @Inject(OpContextMenuLocalsToken) - public locals:OpContextMenuLocalsMap, - readonly I18n:I18nService, - readonly gitActions:GitActionsService, - ) { - super(locals); + constructor() { + super(); this.workPackage = this.locals.workPackage as WorkPackageResource; } diff --git a/modules/github_integration/frontend/module/git-actions-menu/git-actions-menu.directive.ts b/modules/github_integration/frontend/module/git-actions-menu/git-actions-menu.directive.ts index 5e446816f142..369ac12b767f 100644 --- a/modules/github_integration/frontend/module/git-actions-menu/git-actions-menu.directive.ts +++ b/modules/github_integration/frontend/module/git-actions-menu/git-actions-menu.directive.ts @@ -27,8 +27,7 @@ //++ import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types'; -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; -import { Directive, ElementRef, Input } from '@angular/core'; +import { Directive, Input } from '@angular/core'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { GitActionsMenuComponent } from './git-actions-menu.component'; @@ -40,11 +39,6 @@ import { GitActionsMenuComponent } from './git-actions-menu.component'; export class GitActionsMenuDirective extends OpContextMenuTrigger { @Input('gitActionsCopyDropdown-workPackage') public workPackage:WorkPackageResource; - constructor(readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService) { - super(elementRef, opContextMenu); - } - protected open(evt:Event) { this.opContextMenu.show(this, evt, GitActionsMenuComponent); } diff --git a/modules/github_integration/frontend/module/github-tab/github-tab.component.ts b/modules/github_integration/frontend/module/github-tab/github-tab.component.ts index 304511710708..d8ec7b690a97 100644 --- a/modules/github_integration/frontend/module/github-tab/github-tab.component.ts +++ b/modules/github_integration/frontend/module/github-tab/github-tab.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { TabComponent } from "core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab"; import { I18nService } from "core-app/core/i18n/i18n.service"; @@ -38,9 +38,8 @@ import { PathHelperService } from "core-app/core/path-helper/path-helper.service standalone: false, }) export class GitHubTabComponent implements TabComponent { - @Input() public workPackage:WorkPackageResource; + readonly PathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); - constructor(readonly PathHelper:PathHelperService, - readonly I18n:I18nService) { - } + @Input() public workPackage:WorkPackageResource; } diff --git a/modules/github_integration/frontend/module/main.ts b/modules/github_integration/frontend/module/main.ts index abc89b6f8765..06e3d3ae5031 100644 --- a/modules/github_integration/frontend/module/main.ts +++ b/modules/github_integration/frontend/module/main.ts @@ -24,7 +24,7 @@ // // See COPYRIGHT and LICENSE files for more details. -import { Injector, NgModule } from '@angular/core'; +import { Injector, NgModule, inject } from '@angular/core'; import { OpSharedModule } from 'core-app/shared/shared.module'; import { OpenprojectTabsModule } from 'core-app/shared/components/tabs/openproject-tabs.module'; import { @@ -98,7 +98,9 @@ export function initializeGithubIntegrationPlugin(injector:Injector) { ], }) export class PluginModule { - constructor(injector:Injector) { + constructor() { + const injector = inject(Injector); + initializeGithubIntegrationPlugin(injector); registerCustomElement('opce-github-pull-request', PullRequestMacroComponent, { injector }); } diff --git a/modules/github_integration/frontend/module/pull-request/pull-request-macro.component.ts b/modules/github_integration/frontend/module/pull-request/pull-request-macro.component.ts index bae44cb774a5..339aeb525e27 100644 --- a/modules/github_integration/frontend/module/pull-request/pull-request-macro.component.ts +++ b/modules/github_integration/frontend/module/pull-request/pull-request-macro.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ Ng1FieldControlsWrapper, -import { ChangeDetectionStrategy, Component, ElementRef, Injector, Input, OnInit, } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Injector, Input, OnInit, inject } from '@angular/core'; import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; @@ -48,6 +48,11 @@ import { PullRequestState } from './pull-request-state.component'; standalone: false, }) export class PullRequestMacroComponent implements OnInit { + readonly elementRef = inject(ElementRef); + readonly injector = inject(Injector); + readonly pullRequests = inject(GithubPullRequestResourceService); + readonly I18n = inject(I18nService); + @Input() pullRequestId:string; @Input() pullRequestState:PullRequestState; @@ -56,12 +61,7 @@ export class PullRequestMacroComponent implements OnInit { displayText$:Observable; - constructor( - readonly elementRef:ElementRef, - readonly injector:Injector, - readonly pullRequests:GithubPullRequestResourceService, - readonly I18n:I18nService, - ) { + constructor() { populateInputsFromDataset(this); } diff --git a/modules/github_integration/frontend/module/pull-request/pull-request-state.component.ts b/modules/github_integration/frontend/module/pull-request/pull-request-state.component.ts index 73819995ba6b..8cc84107164d 100644 --- a/modules/github_integration/frontend/module/pull-request/pull-request-state.component.ts +++ b/modules/github_integration/frontend/module/pull-request/pull-request-state.component.ts @@ -26,12 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - Input, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -47,18 +42,15 @@ export type PullRequestState = 'opened'|'closed'|'referenced'|'ready_for_review' standalone: false, }) export class PullRequestStateComponent implements OnInit { + readonly PathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); + @Input() state:PullRequestState; @Input() small = false; displayText:string; - constructor( - readonly PathHelper:PathHelperService, - readonly I18n:I18nService, - ) { - } - ngOnInit():void { this.displayText = this.I18n.t( `js.github_integration.pull_requests.states.${this.state}`, diff --git a/modules/github_integration/frontend/module/pull-request/pull-request.component.ts b/modules/github_integration/frontend/module/pull-request/pull-request.component.ts index e7bf35fc1832..782cf15ca18a 100644 --- a/modules/github_integration/frontend/module/pull-request/pull-request.component.ts +++ b/modules/github_integration/frontend/module/pull-request/pull-request.component.ts @@ -26,12 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - HostBinding, - Input, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, Input, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { @@ -51,6 +46,9 @@ import { }) export class PullRequestComponent { + readonly PathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); + @HostBinding('class.op-pull-request') className = true; @Input() public pullRequest:IGithubPullRequest; @@ -62,12 +60,6 @@ export class PullRequestComponent { label_actions: this.I18n.t('js.github_integration.github_actions'), }; - constructor( - readonly PathHelper:PathHelperService, - readonly I18n:I18nService, - ) { - } - get state():string { if (this.pullRequest.state === 'open') { return (this.pullRequest.draft ? 'draft' : 'open'); diff --git a/modules/github_integration/frontend/module/tab-header/tab-header.component.ts b/modules/github_integration/frontend/module/tab-header/tab-header.component.ts index de0ccd025e47..febffe3ea089 100644 --- a/modules/github_integration/frontend/module/tab-header/tab-header.component.ts +++ b/modules/github_integration/frontend/module/tab-header/tab-header.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { I18nService } from "core-app/core/i18n/i18n.service"; @@ -39,6 +39,8 @@ import { I18nService } from "core-app/core/i18n/i18n.service"; standalone: false, }) export class TabHeaderComponent { + readonly I18n = inject(I18nService); + @Input() public workPackage:WorkPackageResource; public text = { @@ -48,7 +50,4 @@ export class TabHeaderComponent { gitMenuLabel: this.I18n.t('js.github_integration.tab_header.copy_menu.label'), gitMenuDescription: this.I18n.t('js.github_integration.tab_header.copy_menu.description'), }; - - constructor(readonly I18n:I18nService) { - } } diff --git a/modules/github_integration/frontend/module/tab-prs/tab-prs.component.ts b/modules/github_integration/frontend/module/tab-prs/tab-prs.component.ts index ae0825da1d5f..d553eacc43eb 100644 --- a/modules/github_integration/frontend/module/tab-prs/tab-prs.component.ts +++ b/modules/github_integration/frontend/module/tab-prs/tab-prs.component.ts @@ -26,13 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - Component, - HostBinding, - Input, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, Input, OnInit, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -51,6 +45,10 @@ import { Observable } from 'rxjs'; standalone: false, }) export class TabPrsComponent implements OnInit { + readonly I18n = inject(I18nService); + readonly apiV3Service = inject(ApiV3Service); + readonly githubPullRequests = inject(GithubPullRequestResourceService); + @HostBinding('class.op-github-prs') className = true; @Input() workPackage:WorkPackageResource; @@ -59,12 +57,6 @@ export class TabPrsComponent implements OnInit { emptyText:string; - constructor( - readonly I18n:I18nService, - readonly apiV3Service:ApiV3Service, - readonly githubPullRequests:GithubPullRequestResourceService, - ) {} - ngOnInit():void { this.emptyText = this.I18n.t('js.github_integration.tab_prs.empty', { wp_id: this.workPackage.id }); this.pullRequests$ = this diff --git a/modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.component.ts b/modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.component.ts index 5c35d5665728..83ebf990addd 100644 --- a/modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.component.ts +++ b/modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.component.ts @@ -28,15 +28,11 @@ //++ import copy from 'copy-text-to-clipboard'; -import { Component, Inject, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { GitActionsService } from '../git-actions/git-actions.service'; import { ISnippet } from "core-app/features/plugins/linked/openproject-gitlab_integration/typings"; import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { OPContextMenuComponent } from "core-app/shared/components/op-context-menu/op-context-menu.component"; -import { - OpContextMenuLocalsMap, - OpContextMenuLocalsToken -} from "core-app/shared/components/op-context-menu/op-context-menu.types"; import { I18nService } from "core-app/core/i18n/i18n.service"; @@ -49,6 +45,9 @@ import { I18nService } from "core-app/core/i18n/i18n.service"; standalone: false, }) export class GitActionsMenuComponent extends OPContextMenuComponent { + readonly I18n = inject(I18nService); + readonly gitActions = inject(GitActionsService); + @Input() public workPackage:WorkPackageResource; public text = { @@ -85,12 +84,9 @@ export class GitActionsMenuComponent extends OPContextMenuComponent { }, ]; - constructor(@Inject(OpContextMenuLocalsToken) - public locals:OpContextMenuLocalsMap, - readonly I18n:I18nService, - readonly gitActions:GitActionsService) { - super(locals); - this.workPackage = this.locals.workPackage; + constructor() { + super(); + this.workPackage = this.locals.workPackage as WorkPackageResource; } public onCopyButtonClick(snippet:ISnippet):void { diff --git a/modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.directive.ts b/modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.directive.ts index b13a59037b35..63caaafe99ab 100644 --- a/modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.directive.ts +++ b/modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.directive.ts @@ -28,8 +28,7 @@ //++ import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types'; -import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; -import { Directive, ElementRef, Input } from '@angular/core'; +import { Directive, Input } from '@angular/core'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { GitActionsMenuComponent } from './git-actions-menu.component'; @@ -42,11 +41,6 @@ import { GitActionsMenuComponent } from './git-actions-menu.component'; export class GitActionsMenuDirective extends OpContextMenuTrigger { @Input('gitActionsCopyDropdown-workPackage') public workPackage:WorkPackageResource; - constructor(readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService) { - super(elementRef, opContextMenu); - } - protected open(evt:Event) { this.opContextMenu.show(this, evt, GitActionsMenuComponent); } diff --git a/modules/gitlab_integration/frontend/module/gitlab-tab/gitlab-tab.component.ts b/modules/gitlab_integration/frontend/module/gitlab-tab/gitlab-tab.component.ts index cef09d99e13e..2780f238df03 100644 --- a/modules/gitlab_integration/frontend/module/gitlab-tab/gitlab-tab.component.ts +++ b/modules/gitlab_integration/frontend/module/gitlab-tab/gitlab-tab.component.ts @@ -27,7 +27,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { TabComponent } from "core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab"; import { I18nService } from "core-app/core/i18n/i18n.service"; @@ -42,9 +42,8 @@ import { PathHelperService } from "core-app/core/path-helper/path-helper.service standalone: false, }) export class GitlabTabComponent implements TabComponent { - @Input() public workPackage:WorkPackageResource; + readonly PathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); - constructor(readonly PathHelper:PathHelperService, - readonly I18n:I18nService) { - } + @Input() public workPackage:WorkPackageResource; } diff --git a/modules/gitlab_integration/frontend/module/issue/issue.component.ts b/modules/gitlab_integration/frontend/module/issue/issue.component.ts index d835bd068391..965e09b4ba62 100644 --- a/modules/gitlab_integration/frontend/module/issue/issue.component.ts +++ b/modules/gitlab_integration/frontend/module/issue/issue.component.ts @@ -27,7 +27,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import {IGitlabIssueResource} from 'core-app/features/plugins/linked/openproject-gitlab_integration/typings'; @@ -43,6 +43,9 @@ import {IGitlabIssueResource} from 'core-app/features/plugins/linked/openproject }) export class IssueComponent { + readonly PathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); + @Input() public gitlabIssue:IGitlabIssueResource; public text = { @@ -51,10 +54,6 @@ export class IssueComponent { label_details: this.I18n.t('js.label_details'), }; - constructor(readonly PathHelper:PathHelperService, - readonly I18n:I18nService) { - } - get state() { if (this.gitlabIssue.state === 'opened') { diff --git a/modules/gitlab_integration/frontend/module/main.ts b/modules/gitlab_integration/frontend/module/main.ts index 3b532e100866..115affd61796 100644 --- a/modules/gitlab_integration/frontend/module/main.ts +++ b/modules/gitlab_integration/frontend/module/main.ts @@ -27,7 +27,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injector, NgModule } from '@angular/core'; +import { Injector, NgModule, inject } from '@angular/core'; import { OpSharedModule } from 'core-app/shared/shared.module'; import { OpenprojectTabsModule } from 'core-app/shared/components/tabs/openproject-tabs.module'; import { @@ -116,7 +116,9 @@ export function initializeGitlabIntegrationPlugin(injector:Injector) { ], }) export class PluginModule { - constructor(injector:Injector) { + constructor() { + const injector = inject(Injector); + initializeGitlabIntegrationPlugin(injector); } } diff --git a/modules/gitlab_integration/frontend/module/merge-request/merge-request.component.ts b/modules/gitlab_integration/frontend/module/merge-request/merge-request.component.ts index 64dcdfe606d2..b3ecca8b9891 100644 --- a/modules/gitlab_integration/frontend/module/merge-request/merge-request.component.ts +++ b/modules/gitlab_integration/frontend/module/merge-request/merge-request.component.ts @@ -27,7 +27,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import {IGitlabMergeRequestResource} from 'core-app/features/plugins/linked/openproject-gitlab_integration/typings'; @@ -44,6 +44,9 @@ import {IGitlabMergeRequestResource} from 'core-app/features/plugins/linked/open }) export class MergeRequestComponent { + readonly PathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); + @Input() public mergeRequest:IGitlabMergeRequestResource; public text = { @@ -53,10 +56,6 @@ export class MergeRequestComponent { label_pipelines: this.I18n.t('js.gitlab_integration.gitlab_pipelines'), }; - constructor(readonly PathHelper:PathHelperService, - readonly I18n:I18nService) { - } - get state() { if (this.mergeRequest.state === 'opened') { diff --git a/modules/gitlab_integration/frontend/module/tab-header-issue/tab-header-issue.component.ts b/modules/gitlab_integration/frontend/module/tab-header-issue/tab-header-issue.component.ts index bc1a26da867f..97f85e1b770d 100644 --- a/modules/gitlab_integration/frontend/module/tab-header-issue/tab-header-issue.component.ts +++ b/modules/gitlab_integration/frontend/module/tab-header-issue/tab-header-issue.component.ts @@ -27,7 +27,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { I18nService } from "core-app/core/i18n/i18n.service"; @@ -40,12 +40,11 @@ import { I18nService } from "core-app/core/i18n/i18n.service"; standalone: false, }) export class TabHeaderIssueComponent { + readonly I18n = inject(I18nService); + @Input() public workPackage:WorkPackageResource; public text = { title: this.I18n.t('js.gitlab_integration.tab_header_issue.title'), }; - - constructor(readonly I18n:I18nService) { - } } diff --git a/modules/gitlab_integration/frontend/module/tab-header-mr/tab-header-mr.component.ts b/modules/gitlab_integration/frontend/module/tab-header-mr/tab-header-mr.component.ts index 25c5e99b4438..53c50f40e023 100644 --- a/modules/gitlab_integration/frontend/module/tab-header-mr/tab-header-mr.component.ts +++ b/modules/gitlab_integration/frontend/module/tab-header-mr/tab-header-mr.component.ts @@ -27,7 +27,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { I18nService } from "core-app/core/i18n/i18n.service"; @@ -40,6 +40,8 @@ import { I18nService } from "core-app/core/i18n/i18n.service"; standalone: false, }) export class TabHeaderMrsComponent { + readonly I18n = inject(I18nService); + @Input() public workPackage:WorkPackageResource; public text = { @@ -49,7 +51,4 @@ export class TabHeaderMrsComponent { gitMenuLabel: this.I18n.t('js.gitlab_integration.tab_header_mr.copy_menu.label'), gitMenuDescription: this.I18n.t('js.gitlab_integration.tab_header_mr.copy_menu.description'), }; - - constructor(readonly I18n:I18nService) { - } } diff --git a/modules/gitlab_integration/frontend/module/tab-issue/tab-issue.component.ts b/modules/gitlab_integration/frontend/module/tab-issue/tab-issue.component.ts index 4b4fbcdae532..a26de1027bcd 100644 --- a/modules/gitlab_integration/frontend/module/tab-issue/tab-issue.component.ts +++ b/modules/gitlab_integration/frontend/module/tab-issue/tab-issue.component.ts @@ -27,7 +27,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core'; import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; @@ -42,17 +42,15 @@ import {ApiV3Service} from "core-app/core/apiv3/api-v3.service"; standalone: false, }) export class TabIssueComponent implements OnInit { + readonly I18n = inject(I18nService); + readonly apiV3Service = inject(ApiV3Service); + readonly halResourceService = inject(HalResourceService); + readonly changeDetector = inject(ChangeDetectorRef); + @Input() public workPackage:WorkPackageResource; public gitlabIssues:IGitlabIssueResource[] = []; - constructor( - readonly I18n:I18nService, - readonly apiV3Service:ApiV3Service, - readonly halResourceService:HalResourceService, - readonly changeDetector:ChangeDetectorRef, - ) {} - ngOnInit(): void { const basePath = this.apiV3Service.work_packages.id(this.workPackage.id as string).path; const gitlabIssuePath = `${basePath}/gitlab_issues`; diff --git a/modules/gitlab_integration/frontend/module/tab-issue/wp-gitlab-issue.service.ts b/modules/gitlab_integration/frontend/module/tab-issue/wp-gitlab-issue.service.ts index 28d236668c05..729b9376f96b 100644 --- a/modules/gitlab_integration/frontend/module/tab-issue/wp-gitlab-issue.service.ts +++ b/modules/gitlab_integration/frontend/module/tab-issue/wp-gitlab-issue.service.ts @@ -29,16 +29,14 @@ import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ConfigurationService } from "core-app/core/config/configuration.service"; import { WorkPackageLinkedResourceCache } from 'core-app/features/work-packages/components/wp-single-view-tabs/wp-linked-resource-cache.service'; @Injectable() export class WorkPackagesGitlabIssueService extends WorkPackageLinkedResourceCache { + ConfigurationService = inject(ConfigurationService); - constructor(public ConfigurationService:ConfigurationService) { - super(); - } protected load(workPackage:WorkPackageResource):Promise { return workPackage.gitlab_issues.$update().then((data:any) => { diff --git a/modules/gitlab_integration/frontend/module/tab-mrs/tab-mrs.component.ts b/modules/gitlab_integration/frontend/module/tab-mrs/tab-mrs.component.ts index 4cae77cf3596..24869c3c2755 100644 --- a/modules/gitlab_integration/frontend/module/tab-mrs/tab-mrs.component.ts +++ b/modules/gitlab_integration/frontend/module/tab-mrs/tab-mrs.component.ts @@ -27,7 +27,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core'; import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; @@ -42,17 +42,15 @@ import {ApiV3Service} from "core-app/core/apiv3/api-v3.service"; standalone: false, }) export class TabMrsComponent implements OnInit { + readonly I18n = inject(I18nService); + readonly apiV3Service = inject(ApiV3Service); + readonly halResourceService = inject(HalResourceService); + readonly changeDetector = inject(ChangeDetectorRef); + @Input() public workPackage:WorkPackageResource; public mergeRequests:IGitlabMergeRequestResource[] = []; - constructor( - readonly I18n:I18nService, - readonly apiV3Service:ApiV3Service, - readonly halResourceService:HalResourceService, - readonly changeDetector:ChangeDetectorRef, - ) {} - ngOnInit(): void { const basePath = this.apiV3Service.work_packages.id(this.workPackage.id as string).path; const mergeRequestsPath = `${basePath}/gitlab_merge_requests`; diff --git a/modules/gitlab_integration/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts b/modules/gitlab_integration/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts index dab5e7f528dc..3c8d1fa8e3c6 100644 --- a/modules/gitlab_integration/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts +++ b/modules/gitlab_integration/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts @@ -29,16 +29,14 @@ import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ConfigurationService } from "core-app/core/config/configuration.service"; import { WorkPackageLinkedResourceCache } from 'core-app/features/work-packages/components/wp-single-view-tabs/wp-linked-resource-cache.service'; @Injectable() export class WorkPackagesGitlabMrsService extends WorkPackageLinkedResourceCache { + ConfigurationService = inject(ConfigurationService); - constructor(public ConfigurationService:ConfigurationService) { - super(); - } protected load(workPackage:WorkPackageResource):Promise { return workPackage.gitlab_merge_requests.$update().then((data:any) => { diff --git a/modules/meeting/frontend/module/main.ts b/modules/meeting/frontend/module/main.ts index 9cc3d46e6267..75f288ea526e 100644 --- a/modules/meeting/frontend/module/main.ts +++ b/modules/meeting/frontend/module/main.ts @@ -24,7 +24,7 @@ // // See COPYRIGHT and LICENSE files for more details. -import { CUSTOM_ELEMENTS_SCHEMA, Injector, NgModule } from '@angular/core'; +import { CUSTOM_ELEMENTS_SCHEMA, Injector, NgModule, inject } from '@angular/core'; import { OpSharedModule } from 'core-app/shared/shared.module'; import { OpenprojectTabsModule } from 'core-app/shared/components/tabs/openproject-tabs.module'; import { @@ -104,7 +104,9 @@ export function initializeMeetingPlugin(injector:Injector) { schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class PluginModule { - constructor(injector:Injector) { + constructor() { + const injector = inject(Injector); + initializeMeetingPlugin(injector); } } diff --git a/modules/meeting/frontend/module/meetings-tab/meetings-tab.component.ts b/modules/meeting/frontend/module/meetings-tab/meetings-tab.component.ts index 96d1f1ed93d7..a700fb205ee6 100644 --- a/modules/meeting/frontend/module/meetings-tab/meetings-tab.component.ts +++ b/modules/meeting/frontend/module/meetings-tab/meetings-tab.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, ElementRef, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { TabComponent } from 'core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -39,15 +39,13 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service standalone: false, }) export class MeetingsTabComponent implements OnInit, TabComponent { + private elementRef = inject(ElementRef); + readonly PathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); + @Input() public workPackage:WorkPackageResource; turboFrameSrc:string; - constructor( - private elementRef:ElementRef, - readonly PathHelper:PathHelperService, - readonly I18n:I18nService, - ) {} - ngOnInit():void { this.turboFrameSrc = `${this.PathHelper.projectWorkPackagePath(this.workPackage.project.id as string, this.workPackage.id as string)}/meetings/tab`; } diff --git a/modules/wikis/frontend/module/main.ts b/modules/wikis/frontend/module/main.ts index f4f5ebbd8e69..695c1ae67ea6 100644 --- a/modules/wikis/frontend/module/main.ts +++ b/modules/wikis/frontend/module/main.ts @@ -24,7 +24,7 @@ // // See COPYRIGHT and LICENSE files for more details. -import { CUSTOM_ELEMENTS_SCHEMA, Injector, NgModule } from '@angular/core'; +import { CUSTOM_ELEMENTS_SCHEMA, Injector, NgModule, inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OpSharedModule } from 'core-app/shared/shared.module'; @@ -65,7 +65,9 @@ export function initializeWikiPlugin(injector:Injector) { schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class PluginModule { - constructor(injector:Injector) { + constructor() { + const injector = inject(Injector); + initializeWikiPlugin(injector); } } diff --git a/modules/wikis/frontend/module/wikis-tab/wikis-tab.component.ts b/modules/wikis/frontend/module/wikis-tab/wikis-tab.component.ts index 6a40bfdc7cfc..3e7010a46378 100644 --- a/modules/wikis/frontend/module/wikis-tab/wikis-tab.component.ts +++ b/modules/wikis/frontend/module/wikis-tab/wikis-tab.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectionStrategy, Component, ElementRef, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, inject } from '@angular/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { TabComponent } from 'core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab'; @@ -40,15 +40,13 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service standalone: false, }) export class WikisTabComponent implements OnInit, TabComponent { + private elementRef = inject(ElementRef); + readonly PathHelper = inject(PathHelperService); + readonly I18n = inject(I18nService); + @Input() public workPackage:WorkPackageResource; turboFrameSrc:string; - constructor( - private elementRef:ElementRef, - readonly PathHelper:PathHelperService, - readonly I18n:I18nService, - ) {} - ngOnInit():void { this.turboFrameSrc = `${this.PathHelper.projectWorkPackagePath(this.workPackage.project.id as string, this.workPackage.id as string)}/wikis/tab`; }