diff --git a/.eslintrc.js b/.eslintrc.js index 8903b3f357..b7b4baaf5a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -68,6 +68,7 @@ module.exports = { ], '@angular-eslint/no-host-metadata-property': 'off', '@angular-eslint/no-input-prefix': 'error', + '@angular-eslint/prefer-inject': 'error', '@typescript-eslint/consistent-type-definitions': 'error', '@typescript-eslint/dot-notation': 'off', '@typescript-eslint/explicit-member-accessibility': [ @@ -78,6 +79,7 @@ module.exports = { ], '@typescript-eslint/await-thenable': 'error', '@typescript-eslint/prefer-optional-chain': 'error', + '@typescript-eslint/prefer-readonly': 'error', '@typescript-eslint/no-inferrable-types': 'off', '@typescript-eslint/no-require-imports': 'off', '@typescript-eslint/no-var-requires': 'error', diff --git a/.storybook/main.ts b/.storybook/main.ts index 3593f6f525..c9edccec51 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -11,6 +11,9 @@ const config: StorybookConfig = { stories: ['lib/**/*.stories.ts'], features: { backgrounds: false + }, + core: { + disableTelemetry: true } }; diff --git a/lib/content-services/.storybook/main.ts b/lib/content-services/.storybook/main.ts index 39b51f7720..e765d06a55 100644 --- a/lib/content-services/.storybook/main.ts +++ b/lib/content-services/.storybook/main.ts @@ -13,6 +13,9 @@ const config: StorybookConfig = { framework: { name: getAbsolutePath('@storybook/angular'), options: {} + }, + core: { + disableTelemetry: true } }; diff --git a/lib/content-services/src/lib/agent/services/agent.service.ts b/lib/content-services/src/lib/agent/services/agent.service.ts index c39d8ad532..ef34e26b61 100644 --- a/lib/content-services/src/lib/agent/services/agent.service.ts +++ b/lib/content-services/src/lib/agent/services/agent.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Agent, AgentsApi } from '@alfresco/js-api'; import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; @@ -25,8 +25,10 @@ import { AlfrescoApiService } from '../../services'; providedIn: 'root' }) export class AgentService { + private readonly apiService = inject(AlfrescoApiService); + private _agentsApi: AgentsApi; - private agents = new BehaviorSubject([]); + private readonly agents = new BehaviorSubject([]); get agentsApi(): AgentsApi { this._agentsApi = this._agentsApi ?? new AgentsApi(this.apiService.getInstance()); @@ -35,8 +37,6 @@ export class AgentService { agents$ = this.agents.asObservable(); - constructor(private apiService: AlfrescoApiService) {} - /** * Gets all agents from cache. If cache is empty, fetches agents from backend. * diff --git a/lib/content-services/src/lib/api-factories/alfresco-api-no-auth.service.ts b/lib/content-services/src/lib/api-factories/alfresco-api-no-auth.service.ts index 455aba181b..ff1234302f 100644 --- a/lib/content-services/src/lib/api-factories/alfresco-api-no-auth.service.ts +++ b/lib/content-services/src/lib/api-factories/alfresco-api-no-auth.service.ts @@ -16,16 +16,13 @@ */ import { AdfHttpClient } from '@alfresco/adf-core/api'; -import { StorageService, AppConfigService } from '@alfresco/adf-core'; import { AlfrescoApi, AlfrescoApiConfig } from '@alfresco/js-api'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AlfrescoApiService } from '../services/alfresco-api.service'; @Injectable() export class AlfrescoApiNoAuthService extends AlfrescoApiService { - constructor(storage: StorageService, appConfig: AppConfigService, private readonly adfHttpClient: AdfHttpClient) { - super(appConfig, storage); - } + private readonly adfHttpClient = inject(AdfHttpClient); override createInstance(config: AlfrescoApiConfig) { return new AlfrescoApi( diff --git a/lib/content-services/src/lib/api-factories/alfresco-api-v2-loader.service.ts b/lib/content-services/src/lib/api-factories/alfresco-api-v2-loader.service.ts index 19840ae99f..93a59a8e45 100644 --- a/lib/content-services/src/lib/api-factories/alfresco-api-v2-loader.service.ts +++ b/lib/content-services/src/lib/api-factories/alfresco-api-v2-loader.service.ts @@ -16,7 +16,7 @@ */ import { AlfrescoApiConfig } from '@alfresco/js-api'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AppConfigService, AppConfigValues, StorageService } from '@alfresco/adf-core'; import { AlfrescoApiService } from '../services/alfresco-api.service'; import { SecurityOptionsLoaderService } from '../security-options-loader/security-options-loader.service'; @@ -35,12 +35,10 @@ export function createAlfrescoApiInstance(angularAlfrescoApiService: AlfrescoApi providedIn: 'root' }) export class AlfrescoApiLoaderService { - constructor( - private readonly appConfig: AppConfigService, - private readonly apiService: AlfrescoApiService, - private readonly securityOptionsLoaderService: SecurityOptionsLoaderService, - private storageService: StorageService - ) {} + private readonly appConfig = inject(AppConfigService); + private readonly apiService = inject(AlfrescoApiService); + private readonly securityOptionsLoaderService = inject(SecurityOptionsLoaderService); + private readonly storageService = inject(StorageService); async init(): Promise { await this.appConfig.load(this.securityOptionsLoaderService.load); diff --git a/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.ts b/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.ts index 53b90fc3e0..4b32e87520 100644 --- a/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.ts +++ b/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { AspectListDialogComponentData } from './aspect-list-dialog-data.interface'; import { TranslatePipe } from '@ngx-translate/core'; @@ -32,6 +32,9 @@ import { CommonModule } from '@angular/common'; encapsulation: ViewEncapsulation.None }) export class AspectListDialogComponent implements OnInit { + private readonly dialog = inject>(MatDialogRef); + data = inject(MAT_DIALOG_DATA); + title: string; description: string; currentNodeId: string; @@ -40,7 +43,9 @@ export class AspectListDialogComponent implements OnInit { currentAspectSelection: string[] = []; - constructor(private dialog: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: AspectListDialogComponentData) { + constructor() { + const data = this.data; + this.title = data.title; this.description = data.description; this.overTableMessage = data.overTableMessage; diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.component.ts b/lib/content-services/src/lib/aspect-list/aspect-list.component.ts index da7b56e2a0..32d45af951 100644 --- a/lib/content-services/src/lib/aspect-list/aspect-list.component.ts +++ b/lib/content-services/src/lib/aspect-list/aspect-list.component.ts @@ -37,6 +37,9 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; encapsulation: ViewEncapsulation.None }) export class AspectListComponent implements OnInit { + private readonly aspectListService = inject(AspectListService); + private readonly nodeApiService = inject(NodesApiService); + /** Node Id of the node that we want to update */ @Input({ required: true }) nodeId: string = ''; @@ -67,8 +70,6 @@ export class AspectListComponent implements OnInit { private standardAspectsLoaded = 0; private hasMoreAspects = false; - constructor(private aspectListService: AspectListService, private nodeApiService: NodesApiService) {} - ngOnInit(): void { let aspects$: Observable; if (this.nodeId) { diff --git a/lib/content-services/src/lib/aspect-list/services/aspect-list.service.ts b/lib/content-services/src/lib/aspect-list/services/aspect-list.service.ts index a64742cc31..0fba43bcbd 100644 --- a/lib/content-services/src/lib/aspect-list/services/aspect-list.service.ts +++ b/lib/content-services/src/lib/aspect-list/services/aspect-list.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { AppConfigService } from '@alfresco/adf-core'; import { from, Observable, of, zip } from 'rxjs'; @@ -30,14 +30,15 @@ export const CustomAspectsWhere = `(not namespaceUri matches('http://www.alfresc providedIn: 'root' }) export class AspectListService { + private readonly alfrescoApiService = inject(AlfrescoApiService); + private readonly appConfigService = inject(AppConfigService); + private _aspectsApi: AspectsApi; get aspectsApi(): AspectsApi { this._aspectsApi = this._aspectsApi ?? new AspectsApi(this.alfrescoApiService.getInstance()); return this._aspectsApi; } - constructor(private alfrescoApiService: AlfrescoApiService, private appConfigService: AppConfigService) {} - getAllAspects(standardOpts?: ListAspectsOpts, customOpts?: ListAspectsOpts): Observable { const visibleAspectList = this.getVisibleAspects(); const standardAspects$ = this.getAspects(visibleAspectList, standardOpts); diff --git a/lib/content-services/src/lib/aspect-list/services/dialog-aspect-list.service.ts b/lib/content-services/src/lib/aspect-list/services/dialog-aspect-list.service.ts index 933ce711d9..4d86fa6c40 100644 --- a/lib/content-services/src/lib/aspect-list/services/dialog-aspect-list.service.ts +++ b/lib/content-services/src/lib/aspect-list/services/dialog-aspect-list.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { Observable, Subject } from 'rxjs'; import { AspectListDialogComponentData } from '../aspect-list-dialog-data.interface'; @@ -28,12 +28,10 @@ import { CategoryService } from '../../category'; providedIn: 'root' }) export class DialogAspectListService { - constructor( - private dialog: MatDialog, - private overlayContainer: OverlayContainer, - private tagService: TagService, - private categoryService: CategoryService - ) {} + private readonly dialog = inject(MatDialog); + private readonly overlayContainer = inject(OverlayContainer); + private readonly tagService = inject(TagService); + private readonly categoryService = inject(CategoryService); openAspectListDialog(nodeId?: string, selectorAutoFocusedOnClose?: string): Observable { const select = new Subject(); diff --git a/lib/content-services/src/lib/aspect-list/services/node-aspect.service.ts b/lib/content-services/src/lib/aspect-list/services/node-aspect.service.ts index e9c401bd4c..f404283b7c 100644 --- a/lib/content-services/src/lib/aspect-list/services/node-aspect.service.ts +++ b/lib/content-services/src/lib/aspect-list/services/node-aspect.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { DialogAspectListService } from './dialog-aspect-list.service'; import { CardViewContentUpdateService } from '../../common/services/card-view-content-update.service'; import { NodesApiService } from '../../common/services/nodes-api.service'; @@ -25,12 +25,10 @@ import { TagService } from '../../tag/services/tag.service'; providedIn: 'root' }) export class NodeAspectService { - constructor( - private nodesApiService: NodesApiService, - private dialogAspectListService: DialogAspectListService, - private cardViewContentUpdateService: CardViewContentUpdateService, - private tagService: TagService - ) {} + private readonly nodesApiService = inject(NodesApiService); + private readonly dialogAspectListService = inject(DialogAspectListService); + private readonly cardViewContentUpdateService = inject(CardViewContentUpdateService); + private readonly tagService = inject(TagService); updateNodeAspects(nodeId: string, selectorAutoFocusedOnClose?: string) { this.dialogAspectListService.openAspectListDialog(nodeId, selectorAutoFocusedOnClose).subscribe((aspectList) => { diff --git a/lib/content-services/src/lib/auth-loader/content-auth-loader.service.ts b/lib/content-services/src/lib/auth-loader/content-auth-loader.service.ts index 50907012b7..2133d38ccb 100644 --- a/lib/content-services/src/lib/auth-loader/content-auth-loader.service.ts +++ b/lib/content-services/src/lib/auth-loader/content-auth-loader.service.ts @@ -15,13 +15,14 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AuthenticationService, BasicAlfrescoAuthService } from '@alfresco/adf-core'; import { take } from 'rxjs/operators'; @Injectable() export class ContentAuthLoaderService { - constructor(private readonly basicAlfrescoAuthService: BasicAlfrescoAuthService, private readonly authService: AuthenticationService) {} + private readonly basicAlfrescoAuthService = inject(BasicAlfrescoAuthService); + private readonly authService = inject(AuthenticationService); init(): void { this.authService.onLogin.pipe(take(1)).subscribe({ diff --git a/lib/content-services/src/lib/category/categories-management/categories-management.component.ts b/lib/content-services/src/lib/category/categories-management/categories-management.component.ts index ce24883c2d..520d97dae4 100644 --- a/lib/content-services/src/lib/category/categories-management/categories-management.component.ts +++ b/lib/content-services/src/lib/category/categories-management/categories-management.component.ts @@ -73,6 +73,8 @@ interface CategoryNameControlErrors { encapsulation: ViewEncapsulation.None }) export class CategoriesManagementComponent implements OnInit, OnDestroy { + private readonly categoryService = inject(CategoryService); + readonly nameErrorMessagesByErrors = new Map([ ['duplicatedExistingCategory', 'ALREADY_EXISTS'], ['duplicatedCategory', 'DUPLICATED_CATEGORY'], @@ -82,9 +84,9 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy { ['endsWithDot', 'ENDS_WITH_DOT'] ]); - private existingCategoryLoaded$ = new Subject(); - private cancelExistingCategoriesLoading$ = new Subject(); - private _categoryNameControl = new FormControl( + private readonly existingCategoryLoaded$ = new Subject(); + private readonly cancelExistingCategoriesLoading$ = new Subject(); + private readonly _categoryNameControl = new FormControl( '', [ this.validateIfNotAlreadyAdded.bind(this), @@ -168,12 +170,10 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy { categoryNameControlVisibleChange = new EventEmitter(); @ViewChild('categoryNameInput') - private categoryNameInputElement: ElementRef; + private readonly categoryNameInputElement: ElementRef; private readonly destroyRef = inject(DestroyRef); - constructor(private categoryService: CategoryService) {} - ngOnInit() { this.categoryNameControl.valueChanges .pipe( diff --git a/lib/content-services/src/lib/category/services/category-tree-datasource.service.ts b/lib/content-services/src/lib/category/services/category-tree-datasource.service.ts index 89a08037a9..4a77c46ed8 100644 --- a/lib/content-services/src/lib/category/services/category-tree-datasource.service.ts +++ b/lib/content-services/src/lib/category/services/category-tree-datasource.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { TreeNodeType, TreeResponse, TreeService } from '../../tree'; import { CategoryNode } from '../models/category-node.interface'; import { CategoryService } from './category.service'; @@ -24,9 +24,7 @@ import { map, mergeMap, toArray } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class CategoryTreeDatasourceService extends TreeService { - constructor(private categoryService: CategoryService) { - super(); - } + private readonly categoryService = inject(CategoryService); public getSubNodes(parentNodeId: string, skipCount?: number, maxItems?: number, name?: string): Observable> { return !name diff --git a/lib/content-services/src/lib/category/services/category.service.ts b/lib/content-services/src/lib/category/services/category.service.ts index 8df816f0d6..81f3b4395f 100644 --- a/lib/content-services/src/lib/category/services/category.service.ts +++ b/lib/content-services/src/lib/category/services/category.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AppConfigService, UserPreferencesService } from '@alfresco/adf-core'; import { CategoriesApi, @@ -32,6 +32,10 @@ import { from, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CategoryService { + private readonly apiService = inject(AlfrescoApiService); + private readonly userPreferencesService = inject(UserPreferencesService); + private readonly appConfigService = inject(AppConfigService); + private _categoriesApi: CategoriesApi; private _searchApi: SearchApi; @@ -45,12 +49,6 @@ export class CategoryService { return this._searchApi; } - constructor( - private apiService: AlfrescoApiService, - private userPreferencesService: UserPreferencesService, - private appConfigService: AppConfigService - ) {} - /** * Get subcategories of a given parent category * diff --git a/lib/content-services/src/lib/common/services/card-view-content-update.service.ts b/lib/content-services/src/lib/common/services/card-view-content-update.service.ts index 9be6184a1d..75210a6ea6 100644 --- a/lib/content-services/src/lib/common/services/card-view-content-update.service.ts +++ b/lib/content-services/src/lib/common/services/card-view-content-update.service.ts @@ -17,17 +17,19 @@ import { UpdateNotification, CardViewBaseItemModel, CardViewUpdateService } from '@alfresco/adf-core'; import { Node } from '@alfresco/js-api'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Subject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CardViewContentUpdateService { + private readonly cardViewUpdateService = inject(CardViewUpdateService); + itemUpdated$ = new Subject(); updatedAspect$ = new Subject(); - constructor(private cardViewUpdateService: CardViewUpdateService) { + constructor() { this.linkVariables(); } diff --git a/lib/content-services/src/lib/common/services/content.service.ts b/lib/content-services/src/lib/common/services/content.service.ts index a6f4ae2065..5c62cb37c7 100644 --- a/lib/content-services/src/lib/common/services/content.service.ts +++ b/lib/content-services/src/lib/common/services/content.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ContentApi, Node, NodeEntry } from '@alfresco/js-api'; import { Subject } from 'rxjs'; import { AuthenticationService, ThumbnailService } from '@alfresco/adf-core'; @@ -34,6 +34,10 @@ export interface FolderCreatedEvent { providedIn: 'root' }) export class ContentService { + authService = inject(AuthenticationService); + apiService = inject(AlfrescoApiService); + private readonly thumbnailService = inject(ThumbnailService); + folderCreated = new Subject(); folderCreate = new Subject(); folderEdit = new Subject(); @@ -44,8 +48,6 @@ export class ContentService { return this._contentApi; } - constructor(public authService: AuthenticationService, public apiService: AlfrescoApiService, private thumbnailService?: ThumbnailService) {} - /** * Gets a content URL for the given node. * diff --git a/lib/content-services/src/lib/common/services/discovery-api.service.ts b/lib/content-services/src/lib/common/services/discovery-api.service.ts index 483ea21c86..ecb6f1b441 100644 --- a/lib/content-services/src/lib/common/services/discovery-api.service.ts +++ b/lib/content-services/src/lib/common/services/discovery-api.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { from, Observable, throwError, Subject } from 'rxjs'; import { catchError, map, switchMap, filter, take } from 'rxjs/operators'; import { RepositoryInfo, SystemPropertiesRepresentation, DiscoveryApi, AboutApi, SystemPropertiesApi } from '@alfresco/js-api'; @@ -27,6 +27,9 @@ import { BpmProductVersionModel, AuthenticationService } from '@alfresco/adf-cor providedIn: 'root' }) export class DiscoveryApiService { + private readonly authenticationService = inject(AuthenticationService); + private readonly alfrescoApiService = inject(AlfrescoApiService); + private _discoveryApi: DiscoveryApi; get discoveryApi(): DiscoveryApi { this._discoveryApi = this._discoveryApi ?? new DiscoveryApi(this.alfrescoApiService.getInstance()); @@ -38,10 +41,7 @@ export class DiscoveryApiService { */ ecmProductInfo$ = new Subject(); - constructor( - private readonly authenticationService: AuthenticationService, - private readonly alfrescoApiService: AlfrescoApiService - ) { + constructor() { this.authenticationService.onLogin.subscribe(() => { this.alfrescoApiService.alfrescoApiInitialized .pipe( diff --git a/lib/content-services/src/lib/common/services/favorites-api.service.ts b/lib/content-services/src/lib/common/services/favorites-api.service.ts index 63bf0b9977..ad26e36d9b 100644 --- a/lib/content-services/src/lib/common/services/favorites-api.service.ts +++ b/lib/content-services/src/lib/common/services/favorites-api.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { FavoritesApi, NodePaging, FavoritePaging } from '@alfresco/js-api'; import { Observable, from, of } from 'rxjs'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; @@ -26,6 +26,9 @@ import { catchError } from 'rxjs/operators'; providedIn: 'root' }) export class FavoritesApiService { + private readonly apiService = inject(AlfrescoApiService); + private readonly preferences = inject(UserPreferencesService); + private _favoritesApi: FavoritesApi; get favoritesApi(): FavoritesApi { this._favoritesApi = this._favoritesApi ?? new FavoritesApi(this.apiService.getInstance()); @@ -41,8 +44,6 @@ export class FavoritesApiService { return { entry }; } - constructor(private apiService: AlfrescoApiService, private preferences: UserPreferencesService) {} - remapFavoritesData(data: FavoritePaging = {}): NodePaging { const pagination = data?.list?.pagination || {}; const entries: any[] = this.remapFavoriteEntries(data?.list?.entries || []); diff --git a/lib/content-services/src/lib/common/services/nodes-api.service.ts b/lib/content-services/src/lib/common/services/nodes-api.service.ts index e02aebad37..eaaecd8b46 100644 --- a/lib/content-services/src/lib/common/services/nodes-api.service.ts +++ b/lib/content-services/src/lib/common/services/nodes-api.service.ts @@ -29,7 +29,7 @@ import { JobIdBodyEntry, NodeAssociationPaging } from '@alfresco/js-api'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { from, Observable, Subject, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { NodeMetadata } from '../models/node-metadata.model'; @@ -39,6 +39,9 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; providedIn: 'root' }) export class NodesApiService { + private readonly apiService = inject(AlfrescoApiService); + private readonly preferences = inject(UserPreferencesService); + /** * Publish/subscribe to events related to node updates. */ @@ -56,11 +59,6 @@ export class NodesApiService { return this._nodesApi; } - constructor( - private readonly apiService: AlfrescoApiService, - private readonly preferences: UserPreferencesService - ) {} - private getEntryFromEntity(entity: NodeEntry): Node { return entity.entry; } diff --git a/lib/content-services/src/lib/common/services/people-content.service.spec.ts b/lib/content-services/src/lib/common/services/people-content.service.spec.ts index 73eca98d52..fbe33c6bcc 100644 --- a/lib/content-services/src/lib/common/services/people-content.service.spec.ts +++ b/lib/content-services/src/lib/common/services/people-content.service.spec.ts @@ -20,7 +20,8 @@ import { RedirectAuthService } from '@alfresco/adf-core'; import { PeopleContentQueryRequestModel, PeopleContentService } from './people-content.service'; import { TestBed } from '@angular/core/testing'; import { PersonPaging } from '@alfresco/js-api'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { EMPTY, firstValueFrom, of } from 'rxjs'; import { AlfrescoApiService } from '../../services'; import { AlfrescoApiServiceMock } from '../../mock'; @@ -70,11 +71,12 @@ describe('PeopleContentService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], providers: [ PeopleContentService, { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }, - { provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of() } } + { provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of() } }, + provideHttpClient(), + provideHttpClientTesting() ] }); diff --git a/lib/content-services/src/lib/common/services/people-content.service.ts b/lib/content-services/src/lib/common/services/people-content.service.ts index aaeddee811..2d0f07e1c5 100644 --- a/lib/content-services/src/lib/common/services/people-content.service.ts +++ b/lib/content-services/src/lib/common/services/people-content.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { from, Observable, of } from 'rxjs'; import { AuthenticationService } from '@alfresco/adf-core'; import { map, tap } from 'rxjs/operators'; @@ -44,6 +44,9 @@ export interface PeopleContentQueryRequestModel { providedIn: 'root' }) export class PeopleContentService { + private readonly apiService = inject(AlfrescoApiService); + private readonly contentService = inject(ContentService); + private currentUser: EcmUserModel; private _peopleApi: PeopleApi; @@ -52,7 +55,9 @@ export class PeopleContentService { return this._peopleApi; } - constructor(private apiService: AlfrescoApiService, authenticationService: AuthenticationService, private contentService: ContentService) { + constructor() { + const authenticationService = inject(AuthenticationService); + authenticationService.onLogout.subscribe(() => { this.resetLocalCurrentUser(); }); diff --git a/lib/content-services/src/lib/common/services/rendition.service.ts b/lib/content-services/src/lib/common/services/rendition.service.ts index f4e6a615a2..dc3db4512c 100644 --- a/lib/content-services/src/lib/common/services/rendition.service.ts +++ b/lib/content-services/src/lib/common/services/rendition.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ContentApi, RenditionEntry, RenditionPaging, RenditionsApi, VersionsApi } from '@alfresco/js-api'; import { Track, TranslationService, ViewUtilService } from '@alfresco/adf-core'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; @@ -24,6 +24,10 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; providedIn: 'root' }) export class RenditionService { + private readonly apiService = inject(AlfrescoApiService); + private readonly translateService = inject(TranslationService); + private readonly viewUtilsService = inject(ViewUtilService); + static TARGET = '_new'; /** @@ -51,7 +55,7 @@ export class RenditionService { /** * Timeout used for setInterval. */ - private TRY_TIMEOUT: number = 10000; + private readonly TRY_TIMEOUT: number = 10000; _renditionsApi: RenditionsApi; get renditionsApi(): RenditionsApi { @@ -66,19 +70,13 @@ export class RenditionService { } _versionsApi: VersionsApi; - private DEFAULT_RENDITION: string = 'imgpreview'; + private readonly DEFAULT_RENDITION: string = 'imgpreview'; get versionsApi(): VersionsApi { this._versionsApi = this._versionsApi ?? new VersionsApi(this.apiService.getInstance()); return this._versionsApi; } - constructor( - private readonly apiService: AlfrescoApiService, - private readonly translateService: TranslationService, - private readonly viewUtilsService: ViewUtilService - ) {} - getRenditionUrl(nodeId: string, type: string, renditionExists: boolean): string { return renditionExists && type !== RenditionService.ContentGroup.IMAGE ? this.contentApi.getRenditionUrl(nodeId, RenditionService.ContentGroup.PDF) diff --git a/lib/content-services/src/lib/common/services/saved-searches-base.service.ts b/lib/content-services/src/lib/common/services/saved-searches-base.service.ts index 6c32ec47aa..65edf7a5af 100644 --- a/lib/content-services/src/lib/common/services/saved-searches-base.service.ts +++ b/lib/content-services/src/lib/common/services/saved-searches-base.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { SavedSearchStrategy } from '../interfaces/saved-searches-strategy.interface'; import { AuthenticationService } from '@alfresco/adf-core'; import { ReplaySubject, Observable, catchError, switchMap, take, tap, throwError, map } from 'rxjs'; @@ -32,16 +32,14 @@ export abstract class SavedSearchesBaseService implements SavedSearchStrategy { protected readonly _savedSearches$ = new ReplaySubject(1); readonly savedSearches$: Observable = this._savedSearches$.asObservable(); + protected readonly apiService = inject(AlfrescoApiService); + protected readonly authService = inject(AuthenticationService); + get nodesApi(): NodesApi { this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance()); return this._nodesApi; } - protected constructor( - protected readonly apiService: AlfrescoApiService, - protected readonly authService: AuthenticationService - ) {} - protected abstract fetchAllSavedSearches(): Observable; protected abstract updateSavedSearches(searches: SavedSearch[]): Observable; diff --git a/lib/content-services/src/lib/common/services/saved-searches-legacy.service.ts b/lib/content-services/src/lib/common/services/saved-searches-legacy.service.ts index 1cfb8f4657..69b635988e 100644 --- a/lib/content-services/src/lib/common/services/saved-searches-legacy.service.ts +++ b/lib/content-services/src/lib/common/services/saved-searches-legacy.service.ts @@ -19,9 +19,7 @@ import { NodeEntry } from '@alfresco/js-api'; import { Injectable } from '@angular/core'; import { Observable, of, from, throwError } from 'rxjs'; import { catchError, concatMap, first, map } from 'rxjs/operators'; -import { AlfrescoApiService } from '../../services'; import { SavedSearch } from '../interfaces/saved-search.interface'; -import { AuthenticationService } from '@alfresco/adf-core'; import { SavedSearchesBaseService } from './saved-searches-base.service'; @Injectable({ @@ -32,10 +30,6 @@ export class SavedSearchesLegacyService extends SavedSearchesBaseService { private currentUserLocalStorageKey: string; private createFileAttempt = false; - constructor(apiService: AlfrescoApiService, authService: AuthenticationService) { - super(apiService, authService); - } - protected fetchAllSavedSearches(): Observable { return this.getSavedSearchesNodeId().pipe( concatMap(() => diff --git a/lib/content-services/src/lib/common/services/saved-searches.service.ts b/lib/content-services/src/lib/common/services/saved-searches.service.ts index 32fc945e6e..bf8aa54bc1 100644 --- a/lib/content-services/src/lib/common/services/saved-searches.service.ts +++ b/lib/content-services/src/lib/common/services/saved-searches.service.ts @@ -16,12 +16,10 @@ */ import { NodeEntry, PreferencesApi, ContentFieldsQuery, PreferenceEntry } from '@alfresco/js-api'; -import { inject, Injectable, InjectionToken } from '@angular/core'; +import { Injectable, InjectionToken, inject } from '@angular/core'; import { Observable, of, from, throwError } from 'rxjs'; import { catchError, concatMap, first, map, switchMap, take, tap } from 'rxjs/operators'; -import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { SavedSearch } from '../interfaces/saved-search.interface'; -import { AuthenticationService } from '@alfresco/adf-core'; import { SavedSearchesBaseService } from './saved-searches-base.service'; export interface SavedSearchesPreferencesApiService { @@ -37,7 +35,7 @@ export const SAVED_SEARCHES_SERVICE_PREFERENCES = new InjectionToken { const savedSearchesMigrated = localStorage.getItem(this.getLocalStorageKey()) ?? ''; if (savedSearchesMigrated === 'true') { diff --git a/lib/content-services/src/lib/common/services/sites.service.ts b/lib/content-services/src/lib/common/services/sites.service.ts index 5399ef4127..31f6e81d4b 100644 --- a/lib/content-services/src/lib/common/services/sites.service.ts +++ b/lib/content-services/src/lib/common/services/sites.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { from, Observable } from 'rxjs'; import { Node, @@ -37,14 +37,14 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; providedIn: 'root' }) export class SitesService { + private readonly apiService = inject(AlfrescoApiService); + private _sitesApi: SitesApi; get sitesApi(): SitesApi { this._sitesApi = this._sitesApi ?? new SitesApi(this.apiService.getInstance()); return this._sitesApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Create a site * diff --git a/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.ts b/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.ts index aebfdfa231..cef0f8f04d 100644 --- a/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.ts +++ b/lib/content-services/src/lib/content-metadata/components/content-metadata-card/content-metadata-card.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation, inject } from '@angular/core'; import { Node } from '@alfresco/js-api'; import { NodeAspectService } from '../../../aspect-list/services/node-aspect.service'; import { ContentMetadataCustomPanel, PresetConfig } from '../../interfaces/content-metadata.interfaces'; @@ -38,6 +38,10 @@ import { IconModule } from '@alfresco/adf-core'; host: { class: 'adf-content-metadata-card' } }) export class ContentMetadataCardComponent implements OnChanges { + private readonly contentService = inject(ContentService); + private readonly nodeAspectService = inject(NodeAspectService); + private readonly versionCompatibilityService = inject(VersionCompatibilityService); + /** (required) The node entity to fetch metadata about */ @Input({ required: true }) node: Node; @@ -110,11 +114,7 @@ export class ContentMetadataCardComponent implements OnChanges { editAspectSupported = false; - constructor( - private contentService: ContentService, - private nodeAspectService: NodeAspectService, - private versionCompatibilityService: VersionCompatibilityService - ) { + constructor() { this.editAspectSupported = this.versionCompatibilityService.isVersionSupported('7'); } diff --git a/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.ts b/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.ts index 6c05ef3548..dbf7c6b8ec 100644 --- a/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.ts +++ b/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.ts @@ -86,6 +86,16 @@ export type DefaultPanels = (typeof DefaultPanels)[keyof typeof DefaultPanels]; encapsulation: ViewEncapsulation.None }) export class ContentMetadataComponent implements OnChanges, OnInit { + private readonly contentMetadataService = inject(ContentMetadataService); + private readonly cardViewContentUpdateService = inject(CardViewContentUpdateService); + private readonly nodesApiService = inject(NodesApiService); + private readonly translationService = inject(TranslationService); + private readonly appConfig = inject(AppConfigService); + private readonly tagService = inject(TagService); + private readonly categoryService = inject(CategoryService); + private readonly contentService = inject(ContentService); + private readonly notificationService = inject(NotificationService); + /** (required) The node entity to fetch metadata about */ @Input({ required: true }) node: Node; @@ -146,10 +156,10 @@ export class ContentMetadataComponent implements OnChanges, OnInit { private _assignedTags: string[] = []; private assignedTagsEntries: TagEntry[] = []; - private _tagsCreatorMode = TagsCreatorMode.CREATE_AND_ASSIGN; + private readonly _tagsCreatorMode = TagsCreatorMode.CREATE_AND_ASSIGN; private _tags: string[] = []; private targetProperty: CardViewBaseItemModel; - private classifiableChangedSubject = new Subject(); + private readonly classifiableChangedSubject = new Subject(); private _saving = false; DefaultPanels = DefaultPanels; @@ -174,17 +184,7 @@ export class ContentMetadataComponent implements OnChanges, OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private contentMetadataService: ContentMetadataService, - private cardViewContentUpdateService: CardViewContentUpdateService, - private nodesApiService: NodesApiService, - private translationService: TranslationService, - private appConfig: AppConfigService, - private tagService: TagService, - private categoryService: CategoryService, - private contentService: ContentService, - private notificationService: NotificationService - ) { + constructor() { this.copyToClipboardAction = this.appConfig.get('content-metadata.copy-to-clipboard-action'); this.multiValueSeparator = this.appConfig.get('content-metadata.multi-value-pipe-separator') || DEFAULT_SEPARATOR; this.useChipsForMultiValueProperty = this.appConfig.get('content-metadata.multi-value-chips'); diff --git a/lib/content-services/src/lib/content-metadata/services/basic-properties.service.ts b/lib/content-services/src/lib/content-metadata/services/basic-properties.service.ts index 97c8136b4b..347aea98bf 100644 --- a/lib/content-services/src/lib/content-metadata/services/basic-properties.service.ts +++ b/lib/content-services/src/lib/content-metadata/services/basic-properties.service.ts @@ -15,15 +15,15 @@ * limitations under the License. */ -import { inject, Injectable } from '@angular/core'; +import { inject, Injectable, Injector, runInInjectionContext } from '@angular/core'; import { Node } from '@alfresco/js-api'; -import { CardViewDateItemModel, CardViewItemMatchValidator, CardViewTextItemModel, FileSizePipe, TranslationService } from '@alfresco/adf-core'; +import { CardViewDateItemModel, CardViewItemMatchValidator, CardViewTextItemModel, FileSizePipe } from '@alfresco/adf-core'; @Injectable({ providedIn: 'root' }) export class BasicPropertiesService { - private translationService = inject(TranslationService); + private readonly injector = inject(Injector); getProperties(node: Node) { const sizeInBytes = node.content ? node.content.sizeInBytes : ''; @@ -63,7 +63,7 @@ export class BasicPropertiesService { label: 'CORE.METADATA.BASIC.SIZE', value: sizeInBytes, key: 'content.sizeInBytes', - pipes: [{ pipe: new FileSizePipe(this.translationService) }], + pipes: [{ pipe: runInInjectionContext(this.injector, () => new FileSizePipe()) }], editable: false }), new CardViewTextItemModel({ diff --git a/lib/content-services/src/lib/content-metadata/services/config/aspect-oriented-config.service.ts b/lib/content-services/src/lib/content-metadata/services/config/aspect-oriented-config.service.ts index 1d2ee25876..1137984b71 100644 --- a/lib/content-services/src/lib/content-metadata/services/config/aspect-oriented-config.service.ts +++ b/lib/content-services/src/lib/content-metadata/services/config/aspect-oriented-config.service.ts @@ -19,7 +19,7 @@ import { ContentMetadataConfig, OrganisedPropertyGroup, PropertyGroupContainer, import { getGroup, getProperty } from './property-group-reader'; export class AspectOrientedConfigService implements ContentMetadataConfig { - constructor(private config: any) {} + constructor(private readonly config: any) {} public isGroupAllowed(groupName: string): boolean { if (this.isIncludeAllEnabled()) { diff --git a/lib/content-services/src/lib/content-metadata/services/config/content-metadata-config.factory.ts b/lib/content-services/src/lib/content-metadata/services/config/content-metadata-config.factory.ts index 32f2ad8b4e..de8df0b5df 100644 --- a/lib/content-services/src/lib/content-metadata/services/config/content-metadata-config.factory.ts +++ b/lib/content-services/src/lib/content-metadata/services/config/content-metadata-config.factory.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AppConfigService, LogService } from '@alfresco/adf-core'; import { AspectOrientedConfigService } from './aspect-oriented-config.service'; import { IndifferentConfigService } from './indifferent-config.service'; @@ -29,7 +29,8 @@ const DEFAULT_PRESET_NAME = 'default'; providedIn: 'root' }) export class ContentMetadataConfigFactory { - constructor(private appConfigService: AppConfigService, private logService: LogService) {} + private readonly appConfigService = inject(AppConfigService); + private readonly logService = inject(LogService); public get(presetName: string = 'default'): ContentMetadataConfig { let presetConfig: PresetConfig; diff --git a/lib/content-services/src/lib/content-metadata/services/config/layout-oriented-config.service.ts b/lib/content-services/src/lib/content-metadata/services/config/layout-oriented-config.service.ts index 32e8f486ca..acfbd081a8 100644 --- a/lib/content-services/src/lib/content-metadata/services/config/layout-oriented-config.service.ts +++ b/lib/content-services/src/lib/content-metadata/services/config/layout-oriented-config.service.ts @@ -25,7 +25,7 @@ import { import { getProperty } from './property-group-reader'; export class LayoutOrientedConfigService implements ContentMetadataConfig { - constructor(private config: any) {} + constructor(private readonly config: any) {} public isGroupAllowed(groupName: string): boolean { if (this.isIncludeAllEnabled()) { diff --git a/lib/content-services/src/lib/content-metadata/services/content-metadata.service.ts b/lib/content-services/src/lib/content-metadata/services/content-metadata.service.ts index 25ab837a48..f521d10071 100644 --- a/lib/content-services/src/lib/content-metadata/services/content-metadata.service.ts +++ b/lib/content-services/src/lib/content-metadata/services/content-metadata.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Node } from '@alfresco/js-api'; import { BasicPropertiesService } from './basic-properties.service'; import { Observable, of, iif, Subject } from 'rxjs'; @@ -30,15 +30,13 @@ import { ContentTypePropertiesService } from './content-type-property.service'; providedIn: 'root' }) export class ContentMetadataService { - error = new Subject<{ statusCode: number; message: string }>(); + private readonly basicPropertiesService = inject(BasicPropertiesService); + private readonly contentMetadataConfigFactory = inject(ContentMetadataConfigFactory); + private readonly propertyGroupTranslatorService = inject(PropertyGroupTranslatorService); + private readonly propertyDescriptorsService = inject(PropertyDescriptorsService); + private readonly contentTypePropertyService = inject(ContentTypePropertiesService); - constructor( - private basicPropertiesService: BasicPropertiesService, - private contentMetadataConfigFactory: ContentMetadataConfigFactory, - private propertyGroupTranslatorService: PropertyGroupTranslatorService, - private propertyDescriptorsService: PropertyDescriptorsService, - private contentTypePropertyService: ContentTypePropertiesService - ) {} + error = new Subject<{ statusCode: number; message: string }>(); getBasicProperties(node: Node): Observable { return of(this.basicPropertiesService.getProperties(node)); diff --git a/lib/content-services/src/lib/content-metadata/services/content-type-property.service.ts b/lib/content-services/src/lib/content-metadata/services/content-type-property.service.ts index f110f6cb4c..89dd4ad532 100644 --- a/lib/content-services/src/lib/content-metadata/services/content-type-property.service.ts +++ b/lib/content-services/src/lib/content-metadata/services/content-type-property.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { CardViewItem, CardViewSelectItemModel, CardViewSelectItemOption, CardViewTextItemModel } from '@alfresco/adf-core'; import { Observable, of, Subject, zip } from 'rxjs'; @@ -31,12 +31,10 @@ import { VersionCompatibilityService } from '../../version-compatibility/version providedIn: 'root' }) export class ContentTypePropertiesService { - constructor( - private contentTypeService: ContentTypeService, - private dialog: MatDialog, - private versionCompatibilityService: VersionCompatibilityService, - private propertyGroupTranslatorService: PropertyGroupTranslatorService - ) {} + private readonly contentTypeService = inject(ContentTypeService); + private readonly dialog = inject(MatDialog); + private readonly versionCompatibilityService = inject(VersionCompatibilityService); + private readonly propertyGroupTranslatorService = inject(PropertyGroupTranslatorService); getContentTypeCardItem(node: Node): Observable { if (this.versionCompatibilityService.isVersionSupported('7')) { diff --git a/lib/content-services/src/lib/content-metadata/services/property-descriptors.service.ts b/lib/content-services/src/lib/content-metadata/services/property-descriptors.service.ts index f8753c24da..fb23d68004 100644 --- a/lib/content-services/src/lib/content-metadata/services/property-descriptors.service.ts +++ b/lib/content-services/src/lib/content-metadata/services/property-descriptors.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { Observable, defer, forkJoin } from 'rxjs'; import { PropertyGroup, PropertyGroupContainer } from '../interfaces/content-metadata.interfaces'; @@ -26,14 +26,14 @@ import { ClassesApi } from '@alfresco/js-api'; providedIn: 'root' }) export class PropertyDescriptorsService { + private readonly alfrescoApiService = inject(AlfrescoApiService); + private _classesApi: ClassesApi; get classesApi(): ClassesApi { this._classesApi = this._classesApi ?? new ClassesApi(this.alfrescoApiService.getInstance()); return this._classesApi; } - constructor(private alfrescoApiService: AlfrescoApiService) {} - load(groupNames: string[]): Observable { const groupFetchStreams = groupNames .map((groupName) => groupName.replace(':', '_')) diff --git a/lib/content-services/src/lib/content-metadata/services/property-groups-translator.service.ts b/lib/content-services/src/lib/content-metadata/services/property-groups-translator.service.ts index f0c1726f67..52651254c7 100644 --- a/lib/content-services/src/lib/content-metadata/services/property-groups-translator.service.ts +++ b/lib/content-services/src/lib/content-metadata/services/property-groups-translator.service.ts @@ -29,8 +29,7 @@ import { CardViewSelectItemModel, CardViewTextItemModel, DecimalNumberPipe, - LogService, - UserPreferencesService + LogService } from '@alfresco/adf-core'; import { CardViewGroup, OrganisedPropertyGroup, Property } from '../interfaces/content-metadata.interfaces'; import { of } from 'rxjs'; @@ -53,9 +52,8 @@ export const RECOGNISED_ECM_TYPES = [D_TEXT, D_MLTEXT, D_DATE, D_DATETIME, D_INT providedIn: 'root' }) export class PropertyGroupTranslatorService { - private userPreferenceService = inject(UserPreferencesService); - private appConfig = inject(AppConfigService); - private logService = inject(LogService); + private readonly appConfig = inject(AppConfigService); + private readonly logService = inject(LogService); valueSeparator: string; @@ -221,7 +219,7 @@ export class PropertyGroupTranslatorService { private getDecimalNumberPipe(): DecimalNumberPipe { let decimalNumberPipe: DecimalNumberPipe; runInInjectionContext(this.injector, () => { - decimalNumberPipe = new DecimalNumberPipe(this.userPreferenceService, this.appConfig); + decimalNumberPipe = new DecimalNumberPipe(); }); return decimalNumberPipe; } diff --git a/lib/content-services/src/lib/content-node-selector/content-node-dialog.service.ts b/lib/content-services/src/lib/content-node-selector/content-node-dialog.service.ts index 1b68704745..3adf82bfee 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-dialog.service.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-dialog.service.ts @@ -16,7 +16,7 @@ */ import { MatDialog, MatDialogRef } from '@angular/material/dialog'; -import { EventEmitter, Injectable, Output } from '@angular/core'; +import { EventEmitter, Injectable, Output, inject } from '@angular/core'; import { ThumbnailService, TranslationService } from '@alfresco/adf-core'; import { Subject, Observable, throwError } from 'rxjs'; import { ShareDataRow } from '../document-list/data/share-data-row.model'; @@ -36,21 +36,19 @@ import { SitesService } from '../common/services/sites.service'; }) // eslint-disable-next-line @angular-eslint/directive-class-suffix export class ContentNodeDialogService { + private readonly dialog = inject(MatDialog); + private readonly contentService = inject(ContentService); + private readonly documentListService = inject(DocumentListService); + private readonly siteService = inject(SitesService); + private readonly translation = inject(TranslationService); + private readonly thumbnailService = inject(ThumbnailService); + static nonDocumentSiteContent = ['blog', 'calendar', 'dataLists', 'discussions', 'links', 'wiki']; /** Emitted when an error occurs. */ @Output() error: EventEmitter = new EventEmitter(); - constructor( - private dialog: MatDialog, - private contentService: ContentService, - private documentListService: DocumentListService, - private siteService: SitesService, - private translation: TranslationService, - private thumbnailService: ThumbnailService - ) {} - /** * Opens a file browser at a chosen folder location. * shows files and folders in the dialog search result. diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel/content-node-selector-panel.component.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel/content-node-selector-panel.component.ts index 663d84885b..b9e27e3015 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel/content-node-selector-panel.component.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel/content-node-selector-panel.component.ts @@ -90,6 +90,14 @@ export const defaultValidation = () => true; providers: [SearchQueryBuilderService] }) export class ContentNodeSelectorPanelComponent implements OnInit { + private readonly customResourcesService = inject(CustomResourcesService); + private readonly queryBuilderService = inject(SearchQueryBuilderService); + private readonly userPreferencesService = inject(UserPreferencesService); + private readonly nodesApiService = inject(NodesApiService); + private readonly uploadService = inject(UploadService); + private readonly sitesService = inject(SitesService); + private readonly contentNodeSelectorPanelService = inject(ContentNodeSelectorPanelService); + // eslint-disable-next-line @typescript-eslint/naming-convention DEFAULT_PAGINATION: Pagination = new Pagination({ maxItems: 25, @@ -304,16 +312,6 @@ export class ContentNodeSelectorPanelComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private customResourcesService: CustomResourcesService, - private queryBuilderService: SearchQueryBuilderService, - private userPreferencesService: UserPreferencesService, - private nodesApiService: NodesApiService, - private uploadService: UploadService, - private sitesService: SitesService, - private contentNodeSelectorPanelService: ContentNodeSelectorPanelService - ) {} - set chosenNode(value: Node[]) { this._chosenNode = value; this.select.next(value); diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.ts index 702d2349fe..72d7086632 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, DestroyRef, inject, Inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { EmptyListComponent, IconModule, NotificationService, ToolbarComponent, ToolbarTitleComponent, TranslationService } from '@alfresco/adf-core'; import { Node } from '@alfresco/js-api'; @@ -62,6 +62,14 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; encapsulation: ViewEncapsulation.None }) export class ContentNodeSelectorComponent implements OnInit { + private readonly translation = inject(TranslationService); + private readonly contentService = inject(ContentService); + private readonly notificationService = inject(NotificationService); + private readonly uploadService = inject(UploadService); + private readonly dialog = inject>(MatDialogRef); + private readonly overlayContainer = inject(OverlayContainer); + data = inject(MAT_DIALOG_DATA); + title: string; action: NodeAction; buttonActionName: string; @@ -78,15 +86,9 @@ export class ContentNodeSelectorComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private translation: TranslationService, - private contentService: ContentService, - private notificationService: NotificationService, - private uploadService: UploadService, - private dialog: MatDialogRef, - private overlayContainer: OverlayContainer, - @Inject(MAT_DIALOG_DATA) public data: ContentNodeSelectorComponentData - ) { + constructor() { + const data = this.data; + this.action = data.actionName ?? NodeAction.CHOOSE; this.buttonActionName = `NODE_SELECTOR.${this.action}`; this.title = data.title; diff --git a/lib/content-services/src/lib/content-node-selector/site-dropdown/sites-dropdown.component.ts b/lib/content-services/src/lib/content-node-selector/site-dropdown/sites-dropdown.component.ts index 34672293ce..0c1a9b7ed8 100644 --- a/lib/content-services/src/lib/content-node-selector/site-dropdown/sites-dropdown.component.ts +++ b/lib/content-services/src/lib/content-node-selector/site-dropdown/sites-dropdown.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, inject } from '@angular/core'; import { InfiniteSelectScrollDirective, AuthenticationService } from '@alfresco/adf-core'; import { SitePaging, SiteEntry, Site } from '@alfresco/js-api'; import { MatSelectChange, MatSelectModule } from '@angular/material/select'; @@ -43,6 +43,11 @@ export type Relations = (typeof Relations)[keyof typeof Relations]; host: { class: 'adf-sites-dropdown' } }) export class DropdownSitesComponent implements OnInit { + private readonly authService = inject(AuthenticationService); + private readonly sitesService = inject(SitesService); + private readonly liveAnnouncer = inject(LiveAnnouncer); + private readonly translateService = inject(TranslateService); + /** Hide the "My Files" option. */ @Input() hideMyFiles: boolean = false; @@ -95,13 +100,6 @@ export class DropdownSitesComponent implements OnInit { return this.loading; } - constructor( - private authService: AuthenticationService, - private sitesService: SitesService, - private liveAnnouncer: LiveAnnouncer, - private translateService: TranslateService - ) {} - ngOnInit() { if (!this.siteList) { this.loadSiteList(); diff --git a/lib/content-services/src/lib/content-node-share/content-node-share.dialog.ts b/lib/content-services/src/lib/content-node-share/content-node-share.dialog.ts index 22f9188016..27976cc76a 100644 --- a/lib/content-services/src/lib/content-node-share/content-node-share.dialog.ts +++ b/lib/content-services/src/lib/content-node-share/content-node-share.dialog.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatSlideToggleChange, MatSlideToggleModule } from '@angular/material/slide-toggle'; import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; @@ -59,7 +59,14 @@ interface SharedDialogFormProps { encapsulation: ViewEncapsulation.None }) export class ShareDialogComponent implements OnInit { - private minDateValidator = (control: FormControl): any => + private readonly sharedLinksApiService = inject(SharedLinksApiService); + private readonly dialogRef = inject>(MatDialogRef); + private readonly dialog = inject(MatDialog); + private readonly contentService = inject(ContentService); + private readonly renditionService = inject(RenditionService); + data = inject(MAT_DIALOG_DATA); + + private readonly minDateValidator = (control: FormControl): any => isBefore(endOfDay(new Date(control.value)), this.minDate) ? { invalidDate: true } : null; minDate = add(new Date(), { days: 1 }); @@ -77,14 +84,6 @@ export class ShareDialogComponent implements OnInit { @ViewChild('slideToggleExpirationDate', { static: true }) slideToggleExpirationDate; - constructor( - private sharedLinksApiService: SharedLinksApiService, - private dialogRef: MatDialogRef, - private dialog: MatDialog, - private contentService: ContentService, - private renditionService: RenditionService, - @Inject(MAT_DIALOG_DATA) public data: ContentNodeShareSettings - ) {} ngOnInit() { if (this.data.node?.entry) { diff --git a/lib/content-services/src/lib/content-node-share/content-node-share.directive.ts b/lib/content-services/src/lib/content-node-share/content-node-share.directive.ts index 0bec072026..9a20f7decf 100644 --- a/lib/content-services/src/lib/content-node-share/content-node-share.directive.ts +++ b/lib/content-services/src/lib/content-node-share/content-node-share.directive.ts @@ -29,6 +29,10 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; exportAs: 'adfShare' }) export class NodeSharedDirective implements OnChanges { + private readonly dialog = inject(MatDialog); + private readonly zone = inject(NgZone); + private readonly alfrescoApiService = inject(AlfrescoApiService); + isFile: boolean = false; isShared: boolean = false; @@ -48,8 +52,6 @@ export class NodeSharedDirective implements OnChanges { } private readonly destroyRef = inject(DestroyRef); - - constructor(private dialog: MatDialog, private zone: NgZone, private alfrescoApiService: AlfrescoApiService) {} shareNode(nodeEntry: NodeEntry) { if (nodeEntry?.entry?.isFile) { // shared and favorite diff --git a/lib/content-services/src/lib/content-node-share/services/shared-links-api.service.ts b/lib/content-services/src/lib/content-node-share/services/shared-links-api.service.ts index 538fa517ab..c909eb3801 100644 --- a/lib/content-services/src/lib/content-node-share/services/shared-links-api.service.ts +++ b/lib/content-services/src/lib/content-node-share/services/shared-links-api.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { NodePaging, SharedLinkBodyCreate, SharedLinkEntry, SharedlinksApi } from '@alfresco/js-api'; import { Observable, from, of, Subject } from 'rxjs'; import { UserPreferencesService } from '@alfresco/adf-core'; @@ -26,6 +26,9 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; providedIn: 'root' }) export class SharedLinksApiService { + private readonly apiService = inject(AlfrescoApiService); + private readonly preferences = inject(UserPreferencesService); + error = new Subject<{ statusCode: number; message: string }>(); private _sharedLinksApi: SharedlinksApi; @@ -34,8 +37,6 @@ export class SharedLinksApiService { return this._sharedLinksApi; } - constructor(private apiService: AlfrescoApiService, private preferences: UserPreferencesService) {} - /** * Gets shared links available to the current user. * diff --git a/lib/content-services/src/lib/content-type/content-type-dialog.component.ts b/lib/content-services/src/lib/content-type/content-type-dialog.component.ts index 36a81ad9de..1dee48327e 100644 --- a/lib/content-services/src/lib/content-type/content-type-dialog.component.ts +++ b/lib/content-services/src/lib/content-type/content-type-dialog.component.ts @@ -16,7 +16,7 @@ */ import { TypeEntry } from '@alfresco/js-api'; -import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { ContentTypeDialogComponentData } from './content-type-metadata.interface'; import { ContentTypeService } from './content-type.service'; @@ -34,6 +34,10 @@ import { MatButtonModule } from '@angular/material/button'; encapsulation: ViewEncapsulation.None }) export class ContentTypeDialogComponent implements OnInit { + private readonly dialog = inject>(MatDialogRef); + data = inject(MAT_DIALOG_DATA); + private readonly contentTypeService = inject(ContentTypeService); + title: string; description: string; nodeType: string; @@ -44,11 +48,9 @@ export class ContentTypeDialogComponent implements OnInit { propertyColumns: string[] = ['name', 'title', 'dataType']; - constructor( - private dialog: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: ContentTypeDialogComponentData, - private contentTypeService: ContentTypeService - ) { + constructor() { + const data = this.data; + this.title = data.title; this.description = data.description; this.confirmMessage = data.confirmMessage; diff --git a/lib/content-services/src/lib/content-type/content-type.service.ts b/lib/content-services/src/lib/content-type/content-type.service.ts index bced8a9998..61bb4feb5c 100644 --- a/lib/content-services/src/lib/content-type/content-type.service.ts +++ b/lib/content-services/src/lib/content-type/content-type.service.ts @@ -16,7 +16,7 @@ */ import { TypeEntry, TypePaging, TypesApi } from '@alfresco/js-api'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AlfrescoApiService } from '../services/alfresco-api.service'; import { from, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -25,14 +25,14 @@ import { map } from 'rxjs/operators'; providedIn: 'root' }) export class ContentTypeService { + private readonly alfrescoApiService = inject(AlfrescoApiService); + private _typesApi: TypesApi; get typesApi(): TypesApi { this._typesApi = this._typesApi ?? new TypesApi(this.alfrescoApiService.getInstance()); return this._typesApi; } - constructor(private alfrescoApiService: AlfrescoApiService) {} - getContentTypeByPrefix(prefixedType: string): Observable { return from(this.typesApi.getType(prefixedType)); } diff --git a/lib/content-services/src/lib/dialogs/category-selector/category-selector.dialog.ts b/lib/content-services/src/lib/dialogs/category-selector/category-selector.dialog.ts index 58ea881c6c..428919befb 100644 --- a/lib/content-services/src/lib/dialogs/category-selector/category-selector.dialog.ts +++ b/lib/content-services/src/lib/dialogs/category-selector/category-selector.dialog.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { Subject } from 'rxjs'; import { Category } from '@alfresco/js-api'; @@ -37,15 +37,13 @@ export interface CategorySelectorDialogOptions { encapsulation: ViewEncapsulation.None }) export class CategorySelectorDialogComponent implements OnInit { + private readonly dialog = inject>(MatDialogRef); + private readonly options = inject(MAT_DIALOG_DATA); + categories: Category[] = []; categoriesManagementMode = CategoriesManagementMode.ASSIGN; multiSelect = true; - constructor( - private dialog: MatDialogRef, - @Inject(MAT_DIALOG_DATA) private options: CategorySelectorDialogOptions - ) {} - ngOnInit() { this.multiSelect = this.options.multiSelect ?? true; } diff --git a/lib/content-services/src/lib/dialogs/download-zip/download-zip.dialog.ts b/lib/content-services/src/lib/dialogs/download-zip/download-zip.dialog.ts index 432f88fffc..88193ae8d9 100755 --- a/lib/content-services/src/lib/dialogs/download-zip/download-zip.dialog.ts +++ b/lib/content-services/src/lib/dialogs/download-zip/download-zip.dialog.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog'; import { NodesApiService } from '../../common/services/nodes-api.service'; import { DownloadZipService } from './services/download-zip.service'; @@ -35,20 +35,17 @@ import { MatButtonModule } from '@angular/material/button'; encapsulation: ViewEncapsulation.None }) export class DownloadZipDialogComponent implements OnInit { + private readonly dialogRef = inject>(MatDialogRef); + data = inject(MAT_DIALOG_DATA); + private readonly downloadZipService = inject(DownloadZipService); + private readonly nodeService = inject(NodesApiService); + private readonly contentService = inject(ContentService); + // flag for async threads cancelled = false; downloadId: string; percentageDone = 0; - constructor( - private dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) - public data: any, - private downloadZipService: DownloadZipService, - private nodeService: NodesApiService, - private contentService: ContentService - ) {} - ngOnInit() { if (this.data?.nodeIds?.length > 0) { if (!this.cancelled) { diff --git a/lib/content-services/src/lib/dialogs/download-zip/services/download-zip.service.ts b/lib/content-services/src/lib/dialogs/download-zip/services/download-zip.service.ts index deceec79dc..59a585524e 100755 --- a/lib/content-services/src/lib/dialogs/download-zip/services/download-zip.service.ts +++ b/lib/content-services/src/lib/dialogs/download-zip/services/download-zip.service.ts @@ -16,7 +16,7 @@ */ import { DownloadEntry, DownloadBodyCreate, DownloadsApi } from '@alfresco/js-api'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from } from 'rxjs'; import { AlfrescoApiService } from '../../../services/alfresco-api.service'; @@ -24,14 +24,14 @@ import { AlfrescoApiService } from '../../../services/alfresco-api.service'; providedIn: 'root' }) export class DownloadZipService { + private readonly apiService = inject(AlfrescoApiService); + private _downloadsApi: DownloadsApi; get downloadsApi(): DownloadsApi { this._downloadsApi = this._downloadsApi ?? new DownloadsApi(this.apiService.getInstance()); return this._downloadsApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Creates a new download. * diff --git a/lib/content-services/src/lib/dialogs/folder/folder.dialog.ts b/lib/content-services/src/lib/dialogs/folder/folder.dialog.ts index 90ed573002..30730af1b1 100644 --- a/lib/content-services/src/lib/dialogs/folder/folder.dialog.ts +++ b/lib/content-services/src/lib/dialogs/folder/folder.dialog.ts @@ -16,7 +16,7 @@ */ import { Observable } from 'rxjs'; -import { Component, DestroyRef, EventEmitter, inject, Inject, OnInit, Optional, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, inject, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { Node } from '@alfresco/js-api'; @@ -49,6 +49,12 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; host: { class: 'adf-folder-dialog' } }) export class FolderDialogComponent implements OnInit { + private readonly formBuilder = inject(UntypedFormBuilder); + private readonly dialog = inject>(MatDialogRef); + private readonly nodesApi = inject(NodesApiService); + private readonly translation = inject(TranslationService); + data = inject(MAT_DIALOG_DATA, { optional: true }); + /** * Emitted when the edit/create folder give error for example a folder with same name already exist */ @@ -94,17 +100,11 @@ export class FolderDialogComponent implements OnInit { } private readonly destroyRef = inject(DestroyRef); - private readonly notificationService = inject(NotificationService) + private readonly notificationService = inject(NotificationService); + + constructor() { + const data = this.data; - constructor( - private formBuilder: UntypedFormBuilder, - private dialog: MatDialogRef, - private nodesApi: NodesApiService, - private translation: TranslationService, - @Optional() - @Inject(MAT_DIALOG_DATA) - public data: any - ) { if (data) { this.editTitle = data.editTitle || this.editTitle; this.createTitle = data.createTitle || this.createTitle; diff --git a/lib/content-services/src/lib/dialogs/library/library.dialog.ts b/lib/content-services/src/lib/dialogs/library/library.dialog.ts index c7de2d700d..4c26b47150 100644 --- a/lib/content-services/src/lib/dialogs/library/library.dialog.ts +++ b/lib/content-services/src/lib/dialogs/library/library.dialog.ts @@ -68,6 +68,12 @@ interface VisibilityOption { host: { class: 'adf-library-dialog' } }) export class LibraryDialogComponent implements OnInit { + private readonly alfrescoApiService = inject(AlfrescoApiService); + private readonly sitesService = inject(SitesService); + private readonly formBuilder = inject(UntypedFormBuilder); + private readonly dialog = inject>(MatDialogRef); + private readonly notificationService = inject(NotificationService); + /** Emitted when an error occurs. */ @Output() error = new EventEmitter(); @@ -103,14 +109,6 @@ export class LibraryDialogComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private alfrescoApiService: AlfrescoApiService, - private sitesService: SitesService, - private formBuilder: UntypedFormBuilder, - private dialog: MatDialogRef, - private notificationService: NotificationService - ) {} - ngOnInit() { const validators = { id: [Validators.required, Validators.maxLength(72), this.forbidSpecialCharacters], diff --git a/lib/content-services/src/lib/dialogs/node-lock/node-lock.dialog.ts b/lib/content-services/src/lib/dialogs/node-lock/node-lock.dialog.ts index dd2ae6dd11..a095fe0cb4 100644 --- a/lib/content-services/src/lib/dialogs/node-lock/node-lock.dialog.ts +++ b/lib/content-services/src/lib/dialogs/node-lock/node-lock.dialog.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject, OnInit, Optional, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { differenceInSeconds } from 'date-fns'; @@ -46,6 +46,11 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; encapsulation: ViewEncapsulation.None }) export class NodeLockDialogComponent implements OnInit { + private readonly formBuilder = inject(UntypedFormBuilder); + dialog = inject>(MatDialogRef); + private readonly alfrescoApi = inject(AlfrescoApiService); + data = inject(MAT_DIALOG_DATA, { optional: true }); + form: UntypedFormGroup; node: Node = null; nodeName: string; @@ -56,15 +61,6 @@ export class NodeLockDialogComponent implements OnInit { return this._nodesApi; } - constructor( - private formBuilder: UntypedFormBuilder, - public dialog: MatDialogRef, - private alfrescoApi: AlfrescoApiService, - @Optional() - @Inject(MAT_DIALOG_DATA) - public data: any - ) {} - ngOnInit() { const { node } = this.data; this.nodeName = node.name; diff --git a/lib/content-services/src/lib/directives/auto-focus.directive.ts b/lib/content-services/src/lib/directives/auto-focus.directive.ts index 0eb0908863..b7fd30a220 100644 --- a/lib/content-services/src/lib/directives/auto-focus.directive.ts +++ b/lib/content-services/src/lib/directives/auto-focus.directive.ts @@ -15,14 +15,14 @@ * limitations under the License. */ -import { AfterContentInit, Directive, ElementRef } from '@angular/core'; +import { AfterContentInit, Directive, ElementRef, inject } from '@angular/core'; @Directive({ standalone: true, selector: '[adf-auto-focus]' }) export class AutoFocusDirective implements AfterContentInit { - public constructor(private el: ElementRef) {} + private readonly el = inject(ElementRef); public ngAfterContentInit() { setTimeout(() => { diff --git a/lib/content-services/src/lib/directives/library-favorite.directive.ts b/lib/content-services/src/lib/directives/library-favorite.directive.ts index dc546f2b77..21fef08bdf 100644 --- a/lib/content-services/src/lib/directives/library-favorite.directive.ts +++ b/lib/content-services/src/lib/directives/library-favorite.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, HostListener, Input, OnChanges, Output, EventEmitter, SimpleChanges } from '@angular/core'; +import { Directive, HostListener, Input, OnChanges, Output, EventEmitter, SimpleChanges, inject } from '@angular/core'; import { FavoriteBodyCreate, FavoritesApi } from '@alfresco/js-api'; import { AlfrescoApiService } from '../services/alfresco-api.service'; import { LibraryEntity } from '../interfaces/library-entity.interface'; @@ -27,6 +27,9 @@ import { NotificationService } from '@alfresco/adf-core'; exportAs: 'favoriteLibrary' }) export class LibraryFavoriteDirective implements OnChanges { + private readonly alfrescoApiService = inject(AlfrescoApiService); + private readonly notificationService = inject(NotificationService); + @Input('adf-favorite-library') library: LibraryEntity = null; @@ -59,8 +62,6 @@ export class LibraryFavoriteDirective implements OnChanges { } } - constructor(private readonly alfrescoApiService: AlfrescoApiService, private readonly notificationService: NotificationService) {} - ngOnChanges(changes: SimpleChanges) { if (!changes.library.currentValue) { this.targetLibrary = null; diff --git a/lib/content-services/src/lib/directives/library-membership.directive.spec.ts b/lib/content-services/src/lib/directives/library-membership.directive.spec.ts index 6a6db4f292..be8eeda087 100644 --- a/lib/content-services/src/lib/directives/library-membership.directive.spec.ts +++ b/lib/content-services/src/lib/directives/library-membership.directive.spec.ts @@ -18,28 +18,39 @@ import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { LibraryMembershipDirective } from './library-membership.directive'; import { SimpleChange } from '@angular/core'; -import { of, throwError, Subject } from 'rxjs'; +import { of, throwError } from 'rxjs'; import { SitesService } from '../common/services/sites.service'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { AlfrescoApiService } from '../services/alfresco-api.service'; import { AlfrescoApiServiceMock } from '../mock/alfresco-api.service.mock'; +import { VersionCompatibilityService } from '../version-compatibility/version-compatibility.service'; describe('LibraryMembershipDirective', () => { - let alfrescoApiService: AlfrescoApiService; let directive: LibraryMembershipDirective; let sitesService: SitesService; + let versionCompatibilityService: jasmine.SpyObj; let addMembershipSpy: jasmine.Spy; let getMembershipSpy: jasmine.Spy; let deleteMembershipSpy: jasmine.Spy; let mockSupportedVersion = false; - let testSiteEntry: any; - let requestedMembershipResponse: any; + let testSiteEntry: Partial<{ id: string; guid: string; title: string; visibility: string }>; + let requestedMembershipResponse: Partial<{ id: string; createdAt: string; site: typeof testSiteEntry }>; beforeEach(() => { + versionCompatibilityService = jasmine.createSpyObj('VersionCompatibilityService', ['isVersionSupported']); + versionCompatibilityService.isVersionSupported.and.callFake(() => mockSupportedVersion); + TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [SitesService, { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }] + providers: [ + LibraryMembershipDirective, + SitesService, + { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }, + { provide: VersionCompatibilityService, useValue: versionCompatibilityService }, + provideHttpClient(), + provideHttpClientTesting() + ] }); testSiteEntry = { @@ -55,18 +66,14 @@ describe('LibraryMembershipDirective', () => { site: testSiteEntry }; - alfrescoApiService = TestBed.inject(AlfrescoApiService); sitesService = TestBed.inject(SitesService); - directive = new LibraryMembershipDirective(alfrescoApiService, sitesService, { - ecmProductInfo$: new Subject(), - isVersionSupported: () => mockSupportedVersion - } as any); + directive = TestBed.inject(LibraryMembershipDirective); }); describe('markMembershipRequest', () => { beforeEach(() => { getMembershipSpy = spyOn(directive.sitesApi, 'getSiteMembershipRequestForPerson').and.returnValue( - Promise.resolve({ entry: requestedMembershipResponse }) + Promise.resolve({ entry: requestedMembershipResponse } as never) ); }); @@ -109,10 +116,10 @@ describe('LibraryMembershipDirective', () => { beforeEach(() => { mockSupportedVersion = false; getMembershipSpy = spyOn(directive.sitesApi, 'getSiteMembershipRequestForPerson').and.returnValue( - Promise.resolve({ entry: requestedMembershipResponse }) + Promise.resolve({ entry: requestedMembershipResponse } as never) ); addMembershipSpy = spyOn(directive.sitesApi, 'createSiteMembershipRequestForPerson').and.returnValue( - Promise.resolve({ entry: requestedMembershipResponse }) + Promise.resolve({ entry: requestedMembershipResponse } as never) ); deleteMembershipSpy = spyOn(directive.sitesApi, 'deleteSiteMembershipRequestForPerson').and.returnValue(Promise.resolve()); }); @@ -162,7 +169,7 @@ describe('LibraryMembershipDirective', () => { })); it('should call API to add user to library if admin user', fakeAsync(() => { - const createSiteMembershipSpy = spyOn(sitesService, 'createSiteMembership').and.returnValue(of({} as any)); + const createSiteMembershipSpy = spyOn(sitesService, 'createSiteMembership').and.returnValue(of({} as never)); const selection = { entry: { id: 'no-membership-requested' } }; const selectionChange = new SimpleChange(null, selection, true); directive.isAdmin = true; @@ -176,7 +183,7 @@ describe('LibraryMembershipDirective', () => { it('should emit error when the request to join a library fails', fakeAsync(() => { spyOn(directive.error, 'emit'); - addMembershipSpy.and.returnValue(throwError('err')); + addMembershipSpy.and.returnValue(throwError(() => 'err')); const selection = { entry: { id: 'no-membership-requested' } }; const change = new SimpleChange(null, selection, true); @@ -206,7 +213,7 @@ describe('LibraryMembershipDirective', () => { ]; testData.forEach((data) => { - addMembershipSpy.and.returnValue(throwError({ message: data.fixture })); + addMembershipSpy.and.returnValue(throwError(() => ({ message: data.fixture }))); emitErrorSpy.calls.reset(); directive.toggleMembershipRequest(); tick(); diff --git a/lib/content-services/src/lib/directives/library-membership.directive.ts b/lib/content-services/src/lib/directives/library-membership.directive.ts index ea547b4eb1..c3eeb36db6 100644 --- a/lib/content-services/src/lib/directives/library-membership.directive.ts +++ b/lib/content-services/src/lib/directives/library-membership.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, EventEmitter, HostListener, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; +import { Directive, EventEmitter, HostListener, Input, OnChanges, Output, SimpleChanges, inject } from '@angular/core'; import { SiteEntry, SiteMembershipRequestBodyCreate, SiteMembershipRequestEntry, SitesApi } from '@alfresco/js-api'; import { BehaviorSubject, from, Observable } from 'rxjs'; import { AlfrescoApiService } from '../services/alfresco-api.service'; @@ -30,6 +30,10 @@ import { SitesService } from '../common/services/sites.service'; exportAs: 'libraryMembership' }) export class LibraryMembershipDirective implements OnChanges { + private readonly alfrescoApiService = inject(AlfrescoApiService); + private readonly sitesService = inject(SitesService); + private readonly versionCompatibilityService = inject(VersionCompatibilityService); + targetSite: any = null; isJoinRequested: BehaviorSubject = new BehaviorSubject(false); @@ -60,12 +64,6 @@ export class LibraryMembershipDirective implements OnChanges { this.toggleMembershipRequest(); } - constructor( - private alfrescoApiService: AlfrescoApiService, - private sitesService: SitesService, - private versionCompatibilityService: VersionCompatibilityService - ) {} - ngOnChanges(changes: SimpleChanges) { if (!changes.selection.currentValue?.entry) { this.targetSite = null; diff --git a/lib/content-services/src/lib/directives/node-counter.directive.ts b/lib/content-services/src/lib/directives/node-counter.directive.ts index 6766afb603..7ab078adb2 100644 --- a/lib/content-services/src/lib/directives/node-counter.directive.ts +++ b/lib/content-services/src/lib/directives/node-counter.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, Input, Component, OnInit, OnChanges, ViewContainerRef } from '@angular/core'; +import { Directive, Input, Component, OnInit, OnChanges, ViewContainerRef, inject } from '@angular/core'; import { TranslatePipe } from '@ngx-translate/core'; @Directive({ @@ -23,14 +23,14 @@ import { TranslatePipe } from '@ngx-translate/core'; selector: '[adf-node-counter]' }) export class NodeCounterDirective implements OnInit, OnChanges { + private readonly viewContainerRef = inject(ViewContainerRef); + /** Number to display in the counter badge */ @Input('adf-node-counter') counter: number; componentRef: NodeCounterComponent; - constructor(private viewContainerRef: ViewContainerRef) {} - ngOnInit() { this.componentRef = this.viewContainerRef.createComponent(NodeCounterComponent).instance; this.componentRef.counter = this.counter; @@ -47,7 +47,7 @@ export class NodeCounterDirective implements OnInit, OnChanges { standalone: true, imports: [TranslatePipe], selector: 'adf-node-counter', - template: `
{{ 'NODE_COUNTER.SELECTED_COUNT' | translate : { count: counter } }}
` + template: `
{{ 'NODE_COUNTER.SELECTED_COUNT' | translate: { count: counter } }}
` }) export class NodeCounterComponent { counter: number; diff --git a/lib/content-services/src/lib/directives/node-delete.directive.ts b/lib/content-services/src/lib/directives/node-delete.directive.ts index 7a97639bf6..78cec33c39 100644 --- a/lib/content-services/src/lib/directives/node-delete.directive.ts +++ b/lib/content-services/src/lib/directives/node-delete.directive.ts @@ -17,7 +17,7 @@ /* eslint-disable @angular-eslint/no-input-rename */ -import { Directive, ElementRef, EventEmitter, HostListener, Input, OnChanges, Output } from '@angular/core'; +import { Directive, ElementRef, EventEmitter, HostListener, Input, OnChanges, Output, inject } from '@angular/core'; import { NodeEntry, Node, DeletedNodeEntry, DeletedNode, TrashcanApi, NodesApi } from '@alfresco/js-api'; import { Observable, forkJoin, from, of } from 'rxjs'; import { TranslationService } from '@alfresco/adf-core'; @@ -51,6 +51,10 @@ interface ProcessStatus { selector: '[adf-delete]' }) export class NodeDeleteDirective implements OnChanges { + private readonly alfrescoApiService = inject(AlfrescoApiService); + private readonly translation = inject(TranslationService); + private readonly elementRef = inject(ElementRef); + /** Array of nodes to delete. */ @Input('adf-delete') selection: NodeEntry[] | DeletedNodeEntry[]; @@ -80,8 +84,6 @@ export class NodeDeleteDirective implements OnChanges { this.process(this.selection); } - constructor(private alfrescoApiService: AlfrescoApiService, private translation: TranslationService, private elementRef: ElementRef) {} - ngOnChanges() { if (!this.selection || (this.selection && this.selection.length === 0)) { this.setDisableAttribute(true); diff --git a/lib/content-services/src/lib/directives/node-download.directive.spec.ts b/lib/content-services/src/lib/directives/node-download.directive.spec.ts index 165686b5cd..ee6d24f079 100755 --- a/lib/content-services/src/lib/directives/node-download.directive.spec.ts +++ b/lib/content-services/src/lib/directives/node-download.directive.spec.ts @@ -20,7 +20,8 @@ import { By } from '@angular/platform-browser'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { Component, DebugElement, ViewChild } from '@angular/core'; import { NodeDownloadDirective } from './node-download.directive'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { ContentApi } from '@alfresco/js-api'; import { AlfrescoApiService } from '../services/alfresco-api.service'; import { AlfrescoApiServiceMock } from '../mock/alfresco-api.service.mock'; @@ -56,8 +57,8 @@ describe('NodeDownloadDirective', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, MatDialogModule, TestComponent], - providers: [{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }] + imports: [MatDialogModule, TestComponent], + providers: [{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }, provideHttpClient(), provideHttpClientTesting()] }); fixture = TestBed.createComponent(TestComponent); component = fixture.componentInstance; diff --git a/lib/content-services/src/lib/directives/node-download.directive.ts b/lib/content-services/src/lib/directives/node-download.directive.ts index 629854655b..31a857462f 100755 --- a/lib/content-services/src/lib/directives/node-download.directive.ts +++ b/lib/content-services/src/lib/directives/node-download.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, Input, HostListener } from '@angular/core'; +import { Directive, Input, HostListener, inject } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { DownloadService } from '@alfresco/adf-core'; import { DownloadZipDialogComponent } from '../dialogs/download-zip/download-zip.dialog'; @@ -31,6 +31,10 @@ import { AlfrescoApiService } from '../services/alfresco-api.service'; selector: '[adfNodeDownload]' }) export class NodeDownloadDirective { + private readonly apiService = inject(AlfrescoApiService); + private readonly downloadService = inject(DownloadService); + private readonly dialog = inject(MatDialog); + private _contentApi: ContentApi; get contentApi(): ContentApi { this._contentApi = this._contentApi ?? new ContentApi(this.apiService.getInstance()); @@ -50,8 +54,6 @@ export class NodeDownloadDirective { this.downloadNodes(this.nodes); } - constructor(private apiService: AlfrescoApiService, private downloadService: DownloadService, private dialog: MatDialog) {} - /** * Downloads multiple selected nodes. * Packs result into a .ZIP archive if there is more than one node selected. diff --git a/lib/content-services/src/lib/directives/node-favorite.directive.spec.ts b/lib/content-services/src/lib/directives/node-favorite.directive.spec.ts index cc661c2877..4847352577 100644 --- a/lib/content-services/src/lib/directives/node-favorite.directive.spec.ts +++ b/lib/content-services/src/lib/directives/node-favorite.directive.spec.ts @@ -19,22 +19,19 @@ import { SimpleChange } from '@angular/core'; import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { NodeFavoriteDirective } from './node-favorite.directive'; import { AppConfigService, AppConfigServiceMock, NotificationService } from '@alfresco/adf-core'; -import { AlfrescoApiService } from '../services'; import { provideApiTesting } from '../testing/providers'; describe('NodeFavoriteDirective', () => { let directive: NodeFavoriteDirective; - let alfrescoApiService: AlfrescoApiService; let notificationService: NotificationService; beforeEach(() => { TestBed.configureTestingModule({ imports: [], - providers: [provideApiTesting(), { provide: AppConfigService, useClass: AppConfigServiceMock }] + providers: [NodeFavoriteDirective, provideApiTesting(), { provide: AppConfigService, useClass: AppConfigServiceMock }] }); - alfrescoApiService = TestBed.inject(AlfrescoApiService); notificationService = TestBed.inject(NotificationService); - directive = new NodeFavoriteDirective(alfrescoApiService, notificationService); + directive = TestBed.inject(NodeFavoriteDirective); }); describe('selection input change event', () => { diff --git a/lib/content-services/src/lib/directives/node-favorite.directive.ts b/lib/content-services/src/lib/directives/node-favorite.directive.ts index 548030045b..11e0e40a4f 100644 --- a/lib/content-services/src/lib/directives/node-favorite.directive.ts +++ b/lib/content-services/src/lib/directives/node-favorite.directive.ts @@ -17,7 +17,7 @@ /* eslint-disable @angular-eslint/no-input-rename */ -import { Directive, EventEmitter, HostListener, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; +import { Directive, EventEmitter, HostListener, Input, OnChanges, Output, SimpleChanges, inject } from '@angular/core'; import { FavoriteBodyCreate, NodeEntry, SharedLinkEntry, Node, SharedLink, FavoritesApi } from '@alfresco/js-api'; import { Observable, from, forkJoin, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; @@ -30,6 +30,9 @@ import { NotificationService } from '@alfresco/adf-core'; exportAs: 'adfFavorite' }) export class NodeFavoriteDirective implements OnChanges { + private readonly alfrescoApiService = inject(AlfrescoApiService); + private readonly notificationService = inject(NotificationService); + favorites: any[] = []; private _favoritesApi: FavoritesApi; @@ -53,8 +56,6 @@ export class NodeFavoriteDirective implements OnChanges { this.toggleFavorite(); } - constructor(private readonly alfrescoApiService: AlfrescoApiService, private readonly notificationService: NotificationService) {} - ngOnChanges(changes: SimpleChanges) { if (!changes.selection.currentValue.length) { this.favorites = []; diff --git a/lib/content-services/src/lib/directives/node-lock.directive.ts b/lib/content-services/src/lib/directives/node-lock.directive.ts index 50e1676643..e419e200ef 100644 --- a/lib/content-services/src/lib/directives/node-lock.directive.ts +++ b/lib/content-services/src/lib/directives/node-lock.directive.ts @@ -17,7 +17,7 @@ /* eslint-disable @angular-eslint/no-input-rename */ -import { Directive, ElementRef, Renderer2, HostListener, Input, AfterViewInit } from '@angular/core'; +import { Directive, ElementRef, Renderer2, HostListener, Input, AfterViewInit, inject } from '@angular/core'; import { Node } from '@alfresco/js-api'; import { ContentService } from '../common/services/content.service'; import { AllowableOperationsEnum } from '../common/models/allowable-operations.enum'; @@ -28,6 +28,11 @@ import { ContentNodeDialogService } from '../content-node-selector/content-node- selector: '[adf-node-lock]' }) export class NodeLockDirective implements AfterViewInit { + element = inject(ElementRef); + private readonly renderer = inject(Renderer2); + private readonly contentService = inject(ContentService); + private readonly contentNodeDialogService = inject(ContentNodeDialogService); + /** Node to lock/unlock. */ @Input('adf-node-lock') node: Node; @@ -38,13 +43,6 @@ export class NodeLockDirective implements AfterViewInit { this.contentNodeDialogService.openLockNodeDialog(this.node); } - constructor( - public element: ElementRef, - private renderer: Renderer2, - private contentService: ContentService, - private contentNodeDialogService: ContentNodeDialogService - ) {} - ngAfterViewInit() { const hasAllowableOperations = this.contentService.hasAllowableOperations(this.node, AllowableOperationsEnum.LOCK); this.renderer.setProperty(this.element.nativeElement, 'disabled', !hasAllowableOperations); diff --git a/lib/content-services/src/lib/directives/node-restore.directive.ts b/lib/content-services/src/lib/directives/node-restore.directive.ts index 1b1f97e954..bdbb077a61 100644 --- a/lib/content-services/src/lib/directives/node-restore.directive.ts +++ b/lib/content-services/src/lib/directives/node-restore.directive.ts @@ -17,7 +17,7 @@ /* eslint-disable @angular-eslint/component-selector, @angular-eslint/no-input-rename */ -import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core'; +import { Directive, EventEmitter, HostListener, Input, Output, inject } from '@angular/core'; import { TrashcanApi, DeletedNodeEntry, DeletedNodesPaging } from '@alfresco/js-api'; import { Observable, forkJoin, from, of } from 'rxjs'; import { tap, mergeMap, map, catchError } from 'rxjs/operators'; @@ -30,6 +30,9 @@ import { AlfrescoApiService } from '../services/alfresco-api.service'; selector: '[adf-restore]' }) export class NodeRestoreDirective { + private readonly alfrescoApiService = inject(AlfrescoApiService); + private readonly translation = inject(TranslationService); + private readonly restoreProcessStatus; private _trashcanApi: TrashcanApi; @@ -51,7 +54,7 @@ export class NodeRestoreDirective { this.recover(this.selection); } - constructor(private alfrescoApiService: AlfrescoApiService, private translation: TranslationService) { + constructor() { this.restoreProcessStatus = this.processStatus(); } diff --git a/lib/content-services/src/lib/document-list/components/content-action/content-action-list.component.spec.ts b/lib/content-services/src/lib/document-list/components/content-action/content-action-list.component.spec.ts index f066af5b5c..48c1f99977 100644 --- a/lib/content-services/src/lib/document-list/components/content-action/content-action-list.component.spec.ts +++ b/lib/content-services/src/lib/document-list/components/content-action/content-action-list.component.spec.ts @@ -29,11 +29,19 @@ describe('ContentColumnList', () => { TestBed.configureTestingModule({ imports: [NoopAuthModule] }); - documentList = TestBed.createComponent(DocumentListComponent).componentInstance as DocumentListComponent; - actionList = new ContentActionListComponent(documentList); + + const docListFixture = TestBed.createComponent(DocumentListComponent); + documentList = docListFixture.componentInstance; }); it('should register action', () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [NoopAuthModule], + providers: [{ provide: DocumentListComponent, useValue: documentList }, ContentActionListComponent] + }); + actionList = TestBed.inject(ContentActionListComponent); + spyOn(documentList.actions, 'push').and.callThrough(); const action = new ContentActionModel(); @@ -44,12 +52,24 @@ describe('ContentColumnList', () => { }); it('should require document list instance to register action', () => { - actionList = new ContentActionListComponent(null); + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [NoopAuthModule], + providers: [{ provide: DocumentListComponent, useValue: null }, ContentActionListComponent] + }); + actionList = TestBed.inject(ContentActionListComponent); const action = new ContentActionModel(); expect(actionList.registerAction(action)).toBeFalsy(); }); it('should require action instance to register', () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [NoopAuthModule], + providers: [{ provide: DocumentListComponent, useValue: documentList }, ContentActionListComponent] + }); + actionList = TestBed.inject(ContentActionListComponent); + spyOn(documentList.actions, 'push').and.callThrough(); const result = actionList.registerAction(null); diff --git a/lib/content-services/src/lib/document-list/components/content-action/content-action-list.component.ts b/lib/content-services/src/lib/document-list/components/content-action/content-action-list.component.ts index 0488fff8fc..c1353f732f 100644 --- a/lib/content-services/src/lib/document-list/components/content-action/content-action-list.component.ts +++ b/lib/content-services/src/lib/document-list/components/content-action/content-action-list.component.ts @@ -17,7 +17,7 @@ /* eslint-disable @angular-eslint/component-selector */ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { ContentActionModel } from './../../models/content-action.model'; import { DocumentListComponent } from './../document-list.component'; @@ -26,7 +26,7 @@ import { DocumentListComponent } from './../document-list.component'; template: '' }) export class ContentActionListComponent { - constructor(private documentList: DocumentListComponent) {} + private readonly documentList = inject(DocumentListComponent); /** * Registers action handler within the parent document list component. diff --git a/lib/content-services/src/lib/document-list/components/content-action/content-action.component.spec.ts b/lib/content-services/src/lib/document-list/components/content-action/content-action.component.spec.ts index c64efa80d6..d0a270d98f 100644 --- a/lib/content-services/src/lib/document-list/components/content-action/content-action.component.spec.ts +++ b/lib/content-services/src/lib/document-list/components/content-action/content-action.component.spec.ts @@ -21,46 +21,55 @@ import { FileNode } from '../../../mock'; import { ContentActionModel } from './../../models/content-action.model'; import { DocumentActionsService } from './../../services/document-actions.service'; import { FolderActionsService } from './../../services/folder-actions.service'; -import { NodeActionsService } from './../../services/node-actions.service'; import { DocumentListComponent } from './../document-list.component'; import { ContentActionListComponent } from './content-action-list.component'; import { ContentActionComponent } from './content-action.component'; -import { ContentService } from '../../../common/services/content.service'; import { NoopAuthModule } from '@alfresco/adf-core'; describe('ContentAction', () => { let documentList: DocumentListComponent; let actionList: ContentActionListComponent; - let documentActions: DocumentActionsService; - let folderActions: FolderActionsService; - - let contentService: ContentService; - let nodeActionsService: NodeActionsService; + let documentActionsService: DocumentActionsService; + let folderActionsService: FolderActionsService; beforeEach(() => { TestBed.configureTestingModule({ imports: [NoopAuthModule] }); - contentService = TestBed.inject(ContentService); - nodeActionsService = new NodeActionsService(null, null, null); - documentActions = new DocumentActionsService(nodeActionsService, null, null, null); - folderActions = new FolderActionsService(nodeActionsService, null, contentService, null); - documentList = TestBed.createComponent(DocumentListComponent).componentInstance as DocumentListComponent; - actionList = new ContentActionListComponent(documentList); + const docListFixture = TestBed.createComponent(DocumentListComponent); + documentList = docListFixture.componentInstance; + + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [NoopAuthModule], + providers: [ + { provide: DocumentListComponent, useValue: documentList }, + ContentActionListComponent, + DocumentActionsService, + FolderActionsService + ] + }); + actionList = TestBed.inject(ContentActionListComponent); + documentActionsService = TestBed.inject(DocumentActionsService); + folderActionsService = TestBed.inject(FolderActionsService); + }); + + afterEach(() => { + documentList.actions = []; }); it('should register within parent actions list', () => { spyOn(actionList, 'registerAction').and.stub(); - const action = new ContentActionComponent(actionList, null, null); + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; action.ngOnInit(); expect(actionList.registerAction).toHaveBeenCalled(); }); it('should setup and register model', () => { - const action = new ContentActionComponent(actionList, null, null); + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; action.target = 'document'; action.title = ''; action.icon = '<icon>'; @@ -77,7 +86,7 @@ describe('ContentAction', () => { }); it('should update visibility binding', () => { - const action = new ContentActionComponent(actionList, null, null); + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; action.target = 'document'; action.title = '<title>'; action.icon = '<icon>'; @@ -96,14 +105,15 @@ describe('ContentAction', () => { it('should get action handler from document actions service', () => { const handler = () => {}; - spyOn(documentActions, 'getHandler').and.returnValue(handler); + spyOn(documentActionsService, 'getHandler').and.returnValue(handler); + + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; - const action = new ContentActionComponent(actionList, documentActions, null); action.target = 'document'; action.handler = '<handler>'; action.ngOnInit(); - expect(documentActions.getHandler).toHaveBeenCalledWith(action.handler); + expect(documentActionsService.getHandler).toHaveBeenCalledWith(action.handler); expect(documentList.actions.length).toBe(1); const model = documentList.actions[0]; @@ -112,14 +122,15 @@ describe('ContentAction', () => { it('should get action handler from folder actions service', () => { const handler = () => {}; - spyOn(folderActions, 'getHandler').and.returnValue(handler); + spyOn(folderActionsService, 'getHandler').and.returnValue(handler); + + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; - const action = new ContentActionComponent(actionList, null, folderActions); action.target = 'folder'; action.handler = '<handler>'; action.ngOnInit(); - expect(folderActions.getHandler).toHaveBeenCalledWith(action.handler); + expect(folderActionsService.getHandler).toHaveBeenCalledWith(action.handler); expect(documentList.actions.length).toBe(1); const model = documentList.actions[0]; @@ -127,69 +138,75 @@ describe('ContentAction', () => { }); it('should create document and folder action when there is no target', () => { - spyOn(folderActions, 'getHandler').and.stub(); - spyOn(documentActions, 'getHandler').and.stub(); + spyOn(folderActionsService, 'getHandler').and.stub(); + spyOn(documentActionsService, 'getHandler').and.stub(); + + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; - const action = new ContentActionComponent(actionList, documentActions, folderActions); action.handler = '<handler>'; action.ngOnInit(); expect(documentList.actions.length).toBe(2); - expect(folderActions.getHandler).toHaveBeenCalled(); - expect(documentActions.getHandler).toHaveBeenCalled(); + expect(folderActionsService.getHandler).toHaveBeenCalled(); + expect(documentActionsService.getHandler).toHaveBeenCalled(); }); it('should create document action when target is document', () => { - spyOn(folderActions, 'getHandler').and.stub(); - spyOn(documentActions, 'getHandler').and.stub(); + spyOn(folderActionsService, 'getHandler').and.stub(); + spyOn(documentActionsService, 'getHandler').and.stub(); + + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; - const action = new ContentActionComponent(actionList, documentActions, folderActions); action.handler = '<handler>'; action.target = 'document'; action.ngOnInit(); expect(documentList.actions.length).toBe(1); - expect(folderActions.getHandler).not.toHaveBeenCalled(); - expect(documentActions.getHandler).toHaveBeenCalled(); + expect(folderActionsService.getHandler).not.toHaveBeenCalled(); + expect(documentActionsService.getHandler).toHaveBeenCalled(); }); it('should create folder action when target is folder', () => { - spyOn(folderActions, 'getHandler').and.stub(); - spyOn(documentActions, 'getHandler').and.stub(); + spyOn(folderActionsService, 'getHandler').and.stub(); + spyOn(documentActionsService, 'getHandler').and.stub(); + + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; - const action = new ContentActionComponent(actionList, documentActions, folderActions); action.handler = '<handler>'; action.target = 'folder'; action.ngOnInit(); expect(documentList.actions.length).toBe(1); - expect(folderActions.getHandler).toHaveBeenCalled(); - expect(documentActions.getHandler).not.toHaveBeenCalled(); + expect(folderActionsService.getHandler).toHaveBeenCalled(); + expect(documentActionsService.getHandler).not.toHaveBeenCalled(); }); it('should be case insensitive for document target', () => { - spyOn(documentActions, 'getHandler').and.stub(); + spyOn(documentActionsService, 'getHandler').and.stub(); + + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; - const action = new ContentActionComponent(actionList, documentActions, null); action.target = 'DoCuMeNt'; action.handler = '<handler>'; action.ngOnInit(); - expect(documentActions.getHandler).toHaveBeenCalledWith(action.handler); + expect(documentActionsService.getHandler).toHaveBeenCalledWith(action.handler); }); it('should be case insensitive for folder target', () => { - spyOn(folderActions, 'getHandler').and.stub(); + spyOn(folderActionsService, 'getHandler').and.stub(); + + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; - const action = new ContentActionComponent(actionList, null, folderActions); action.target = 'FoLdEr'; action.handler = '<handler>'; action.ngOnInit(); - expect(folderActions.getHandler).toHaveBeenCalledWith(action.handler); + expect(folderActionsService.getHandler).toHaveBeenCalledWith(action.handler); }); it('should use custom "execute" emitter', (done) => { + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; const emitter = new EventEmitter(); emitter.subscribe((e) => { @@ -197,7 +214,6 @@ describe('ContentAction', () => { done(); }); - const action = new ContentActionComponent(actionList, null, null); action.target = 'document'; action.execute = emitter; @@ -209,42 +225,44 @@ describe('ContentAction', () => { }); it('should not find document action handler with missing service', () => { - const action = new ContentActionComponent(actionList, null, null); + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; expect(action.getSystemHandler('document', 'name')).toBeNull(); }); it('should not find folder action handler with missing service', () => { - const action = new ContentActionComponent(actionList, null, null); + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; expect(action.getSystemHandler('folder', 'name')).toBeNull(); }); it('should find document action handler via service', () => { const handler = () => {}; - const action = new ContentActionComponent(actionList, documentActions, null); - spyOn(documentActions, 'getHandler').and.returnValue(handler); + spyOn(documentActionsService, 'getHandler').and.returnValue(handler); + + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; expect(action.getSystemHandler('document', 'name')).toBe(handler); }); it('should find folder action handler via service', () => { const handler = () => {}; - const action = new ContentActionComponent(actionList, null, folderActions); - spyOn(folderActions, 'getHandler').and.returnValue(handler); + spyOn(folderActionsService, 'getHandler').and.returnValue(handler); + + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; expect(action.getSystemHandler('folder', 'name')).toBe(handler); }); it('should not find actions for unknown target type', () => { - spyOn(folderActions, 'getHandler').and.stub(); - spyOn(documentActions, 'getHandler').and.stub(); + spyOn(folderActionsService, 'getHandler').and.stub(); + spyOn(documentActionsService, 'getHandler').and.stub(); - const action = new ContentActionComponent(actionList, documentActions, folderActions); + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; expect(action.getSystemHandler('unknown', 'name')).toBeNull(); - expect(folderActions.getHandler).not.toHaveBeenCalled(); - expect(documentActions.getHandler).not.toHaveBeenCalled(); + expect(folderActionsService.getHandler).not.toHaveBeenCalled(); + expect(documentActionsService.getHandler).not.toHaveBeenCalled(); }); it('should wire model with custom event handler', (done) => { - const action = new ContentActionComponent(actionList, documentActions, folderActions); + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; const file = new FileNode(); const handler = new EventEmitter(); @@ -260,9 +278,10 @@ describe('ContentAction', () => { }); it('should allow registering model without handler', () => { - const action = new ContentActionComponent(actionList, documentActions, folderActions); - spyOn(actionList, 'registerAction').and.callThrough(); + + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; + action.execute = null; action.handler = null; action.target = 'document'; @@ -272,7 +291,7 @@ describe('ContentAction', () => { }); it('should register on init', () => { - const action = new ContentActionComponent(actionList, null, null); + const action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; spyOn(action, 'register').and.callThrough(); action.ngOnInit(); @@ -281,10 +300,15 @@ describe('ContentAction', () => { it('should require action list to register action with', () => { const fakeModel = new ContentActionModel(); - let action = new ContentActionComponent(actionList, null, null); + let action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; expect(action.register(fakeModel)).toBeTruthy(); - action = new ContentActionComponent(null, null, null); + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [NoopAuthModule], + providers: [{ provide: ContentActionListComponent, useValue: null }] + }); + action = TestBed.createComponent(ContentActionComponent).componentInstance as ContentActionComponent; expect(action.register(fakeModel)).toBeFalsy(); }); }); diff --git a/lib/content-services/src/lib/document-list/components/content-action/content-action.component.ts b/lib/content-services/src/lib/document-list/components/content-action/content-action.component.ts index 5a57582d8b..4af8f53bf0 100644 --- a/lib/content-services/src/lib/document-list/components/content-action/content-action.component.ts +++ b/lib/content-services/src/lib/document-list/components/content-action/content-action.component.ts @@ -17,7 +17,7 @@ /* eslint-disable @angular-eslint/component-selector */ -import { Component, EventEmitter, Input, OnInit, Output, OnChanges, SimpleChanges, OnDestroy } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, OnChanges, SimpleChanges, OnDestroy, inject } from '@angular/core'; import { ContentActionHandler } from '../../models/content-action.model'; import { DocumentActionsService } from '../../services/document-actions.service'; @@ -28,10 +28,13 @@ import { Subscription } from 'rxjs'; @Component({ selector: 'content-action', - template: '', - providers: [DocumentActionsService, FolderActionsService] + template: '' }) export class ContentActionComponent implements OnInit, OnChanges, OnDestroy { + private readonly list = inject(ContentActionListComponent); + private readonly documentActions = inject(DocumentActionsService); + private readonly folderActions = inject(FolderActionsService); + /** The title of the action as shown in the menu. */ @Input() title: string = 'Action'; @@ -91,12 +94,6 @@ export class ContentActionComponent implements OnInit, OnChanges, OnDestroy { private subscriptions: Subscription[] = []; - constructor( - private list: ContentActionListComponent, - private documentActions: DocumentActionsService, - private folderActions: FolderActionsService - ) {} - ngOnInit() { if (this.target === ContentActionTarget.All) { this.folderActionModel = this.generateAction(ContentActionTarget.Folder); diff --git a/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts b/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts index 471f17b178..6dea15dd38 100644 --- a/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts +++ b/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts @@ -51,7 +51,6 @@ import { mockNodePagingWithPreselectedNodes, mockPreselectedNodes } from '../../mock'; -import { domSanitizerMock } from '../../testing/dom-sanitizer-mock'; import { ImageResolver } from '../data/image-resolver.model'; import { RowFilter } from '../data/row-filter.model'; import { ShareDataRow } from '../data/share-data-row.model'; @@ -1179,7 +1178,7 @@ describe('DocumentList', () => { it('should display [empty folder] template ', () => { fixture.detectChanges(); runInInjectionContext(injector, () => { - documentList.dataTable = new DataTableComponent(null, null, matIconRegistryMock, domSanitizerMock, null); + documentList.dataTable = TestBed.createComponent(DataTableComponent).componentInstance as DataTableComponent; }); expect(documentList.dataTable).toBeDefined(); expect(fixture.debugElement.query(By.css('adf-empty-list'))).not.toBeNull(); @@ -1200,7 +1199,7 @@ describe('DocumentList', () => { it('should empty folder NOT show the pagination', () => { runInInjectionContext(injector, () => { - documentList.dataTable = new DataTableComponent(null, null, matIconRegistryMock, domSanitizerMock, null); + documentList.dataTable = TestBed.createComponent(DataTableComponent).componentInstance as DataTableComponent; }); expect(documentList.isEmpty()).toBeTruthy(); diff --git a/lib/content-services/src/lib/document-list/components/document-list.component.ts b/lib/content-services/src/lib/document-list/components/document-list.component.ts index 735aefe250..eca5d999c8 100644 --- a/lib/content-services/src/lib/document-list/components/document-list.component.ts +++ b/lib/content-services/src/lib/document-list/components/document-list.component.ts @@ -112,6 +112,18 @@ const BYTES_TO_MB_CONVERSION_VALUE = 1048576; host: { class: 'adf-document-list' } }) export class DocumentListComponent extends DataTableSchema implements OnInit, OnChanges, AfterContentInit, PaginatedComponent { + private readonly documentListService = inject(DocumentListService); + private readonly elementRef = inject(ElementRef); + private readonly appConfig: AppConfigService; + private readonly userPreferencesService = inject(UserPreferencesService); + private readonly contentService = inject(ContentService); + private readonly thumbnailService = inject(ThumbnailService); + private readonly alfrescoApiService = inject(AlfrescoApiService); + private readonly nodeService = inject(NodesApiService); + private readonly dataTableService = inject(DataTableService); + private readonly lockService = inject(LockService); + private readonly dialog = inject(MatDialog); + static SINGLE_CLICK_NAVIGATION: string = 'click'; static DOUBLE_CLICK_NAVIGATION: string = 'dblclick'; @@ -448,7 +460,7 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On // @deprecated 3.0.0 folderNode: Node; - private _pagination: PaginationModel = this.DEFAULT_PAGINATION; + private readonly _pagination: PaginationModel = this.DEFAULT_PAGINATION; pagination: BehaviorSubject<PaginationModel> = new BehaviorSubject<PaginationModel>(this.DEFAULT_PAGINATION); sortingSubject: BehaviorSubject<DataSorting[]> = new BehaviorSubject<DataSorting[]>(this.DEFAULT_SORTING); @@ -463,20 +475,12 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On return this._nodesApi; } - constructor( - private documentListService: DocumentListService, - private elementRef: ElementRef, - private appConfig: AppConfigService, - private userPreferencesService: UserPreferencesService, - private contentService: ContentService, - private thumbnailService: ThumbnailService, - private alfrescoApiService: AlfrescoApiService, - private nodeService: NodesApiService, - private dataTableService: DataTableService, - private lockService: LockService, - private dialog: MatDialog - ) { - super(appConfig, 'default', presetsDefaultModel); + constructor() { + const appConfig = inject(AppConfigService); + + super('default', presetsDefaultModel); + this.appConfig = appConfig; + this.nodeService.nodeUpdated.pipe(takeUntilDestroyed()).subscribe((node) => { this.dataTableService.rowUpdate.next({ id: node.id, obj: { entry: node } }); }); diff --git a/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.ts b/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.ts index c3ee7d4fce..054d531d6f 100644 --- a/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.ts +++ b/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { NodeEntry } from '@alfresco/js-api'; import { CommonModule } from '@angular/common'; @@ -29,5 +29,5 @@ import { NodeDownloadDirective } from '../../../directives/node-download.directi templateUrl: './file-auto-download.component.html' }) export class FileAutoDownloadComponent { - constructor(@Inject(MAT_DIALOG_DATA) public node: NodeEntry) {} + node = inject<NodeEntry>(MAT_DIALOG_DATA); } diff --git a/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.spec.ts b/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.spec.ts index 60d9816a13..6e13882fad 100644 --- a/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.spec.ts +++ b/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.spec.ts @@ -17,7 +17,7 @@ import { Subject } from 'rxjs'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { DataTableComponent, DataSorting, PaginationModel } from '@alfresco/adf-core'; +import { DataSorting, PaginationModel } from '@alfresco/adf-core'; import { SearchService } from '../../../search/services/search.service'; import { SimpleChange } from '@angular/core'; import { SearchHeaderQueryBuilderService } from './../../../search/services/search-header-query-builder.service'; @@ -38,7 +38,7 @@ describe('FilterHeaderComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [FilterHeaderComponent], - providers: [provideRouter([]), { provide: SearchService, useValue: searchMock }, DataTableComponent] + providers: [provideRouter([]), { provide: SearchService, useValue: searchMock }] }); fixture = TestBed.createComponent(FilterHeaderComponent); component = fixture.componentInstance; diff --git a/lib/content-services/src/lib/document-list/components/library-name-column/library-name-column.component.ts b/lib/content-services/src/lib/document-list/components/library-name-column/library-name-column.component.ts index 74c7382ac6..9e7986324f 100644 --- a/lib/content-services/src/lib/document-list/components/library-name-column/library-name-column.component.ts +++ b/lib/content-services/src/lib/document-list/components/library-name-column/library-name-column.component.ts @@ -55,6 +55,9 @@ import { NodeTooltipUtils } from '../../utils/node-tooltip.utils'; } }) export class LibraryNameColumnComponent implements OnInit { + private readonly element = inject(ElementRef); + private readonly nodesApiService = inject(NodesApiService); + @Input({ required: true }) context: any; @@ -64,11 +67,6 @@ export class LibraryNameColumnComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private element: ElementRef, - private nodesApiService: NodesApiService - ) {} - ngOnInit() { this.updateValue(); diff --git a/lib/content-services/src/lib/document-list/components/library-role-column/library-role-column.component.ts b/lib/content-services/src/lib/document-list/components/library-role-column/library-role-column.component.ts index b2e44f5590..c5c6d6d19b 100644 --- a/lib/content-services/src/lib/document-list/components/library-role-column/library-role-column.component.ts +++ b/lib/content-services/src/lib/document-list/components/library-role-column/library-role-column.component.ts @@ -35,6 +35,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; host: { class: 'adf-library-role-column adf-datatable-content-cell' } }) export class LibraryRoleColumnComponent implements OnInit { + private readonly nodesApiService = inject(NodesApiService); + @Input({ required: true }) context: any; @@ -58,8 +60,6 @@ export class LibraryRoleColumnComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor(private nodesApiService: NodesApiService) {} - ngOnInit() { this.updateValue(); diff --git a/lib/content-services/src/lib/document-list/components/library-status-column/library-status-column.component.ts b/lib/content-services/src/lib/document-list/components/library-status-column/library-status-column.component.ts index 99453f4333..a1ed6f7377 100644 --- a/lib/content-services/src/lib/document-list/components/library-status-column/library-status-column.component.ts +++ b/lib/content-services/src/lib/document-list/components/library-status-column/library-status-column.component.ts @@ -35,6 +35,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; host: { class: 'adf-library-status-column adf-datatable-content-cell' } }) export class LibraryStatusColumnComponent implements OnInit { + private readonly nodesApiService = inject(NodesApiService); + @Input({ required: true }) context: any; @@ -42,8 +44,6 @@ export class LibraryStatusColumnComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor(private nodesApiService: NodesApiService) {} - ngOnInit() { this.updateValue(); diff --git a/lib/content-services/src/lib/document-list/components/name-column/name-column.component.ts b/lib/content-services/src/lib/document-list/components/name-column/name-column.component.ts index e0fc759824..62dc3a6575 100644 --- a/lib/content-services/src/lib/document-list/components/name-column/name-column.component.ts +++ b/lib/content-services/src/lib/document-list/components/name-column/name-column.component.ts @@ -52,6 +52,9 @@ import { NodeTooltipUtils } from '../../utils/node-tooltip.utils'; host: { class: 'adf-datatable-content-cell adf-datatable-link adf-name-column' } }) export class NameColumnComponent implements OnInit { + private readonly element = inject(ElementRef); + private readonly nodesApiService = inject(NodesApiService); + @Input({ required: true }) // eslint-disable-next-line @typescript-eslint/no-explicit-any context: any; @@ -66,11 +69,6 @@ export class NameColumnComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private element: ElementRef, - private nodesApiService: NodesApiService - ) {} - ngOnInit() { this.updateValue(); diff --git a/lib/content-services/src/lib/document-list/data/share-data-row.model.ts b/lib/content-services/src/lib/document-list/data/share-data-row.model.ts index 385c959cd2..9e03aad5fb 100644 --- a/lib/content-services/src/lib/document-list/data/share-data-row.model.ts +++ b/lib/content-services/src/lib/document-list/data/share-data-row.model.ts @@ -41,10 +41,10 @@ export class ShareDataRow implements DataRow { constructor( private obj: NodeEntry, - private contentService: ContentService, - private permissionsStyle: PermissionStyleModel[], - private thumbnailService?: ThumbnailService, - private allowDropFiles?: boolean + private readonly contentService: ContentService, + private readonly permissionsStyle: PermissionStyleModel[], + private readonly thumbnailService?: ThumbnailService, + private readonly allowDropFiles?: boolean ) { if (!obj) { throw new Error(ERR_OBJECT_NOT_FOUND); diff --git a/lib/content-services/src/lib/document-list/data/share-datatable-adapter.ts b/lib/content-services/src/lib/document-list/data/share-datatable-adapter.ts index 78911da92a..b4605fe60d 100644 --- a/lib/content-services/src/lib/document-list/data/share-datatable-adapter.ts +++ b/lib/content-services/src/lib/document-list/data/share-datatable-adapter.ts @@ -53,8 +53,8 @@ export class ShareDataTableAdapter implements DataTableAdapter { } constructor( - private thumbnailService: ThumbnailService, - private contentService: ContentService, + private readonly thumbnailService: ThumbnailService, + private readonly contentService: ContentService, schema: DataColumn[] = [], sorting?: DataSorting, sortingMode: string = 'client', diff --git a/lib/content-services/src/lib/document-list/services/custom-resources.service.ts b/lib/content-services/src/lib/document-list/services/custom-resources.service.ts index 2869f3cece..61f1a3db83 100644 --- a/lib/content-services/src/lib/document-list/services/custom-resources.service.ts +++ b/lib/content-services/src/lib/document-list/services/custom-resources.service.ts @@ -35,7 +35,7 @@ import { ResultSetPaging, SEARCH_LANGUAGE } from '@alfresco/js-api'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from, of } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -43,6 +43,8 @@ const CREATE_PERMISSION: string = 'create'; @Injectable({ providedIn: 'root' }) export class CustomResourcesService { + private readonly apiService = inject(AlfrescoApiService); + private _peopleApi: PeopleApi; get peopleApi(): PeopleApi { this._peopleApi = this._peopleApi ?? new PeopleApi(this.apiService.getInstance()); @@ -85,8 +87,6 @@ export class CustomResourcesService { return this._nodesApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Gets files recently accessed by a user. * diff --git a/lib/content-services/src/lib/document-list/services/document-actions.service.spec.ts b/lib/content-services/src/lib/document-list/services/document-actions.service.spec.ts index cd51d69e26..e1643332eb 100644 --- a/lib/content-services/src/lib/document-list/services/document-actions.service.spec.ts +++ b/lib/content-services/src/lib/document-list/services/document-actions.service.spec.ts @@ -69,7 +69,12 @@ describe('DocumentActionsService', () => { const file = new FileNode(); expect(service.canExecuteAction(file)).toBeTruthy(); - service = new DocumentActionsService(null, null, null); + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [NoopAuthModule], + providers: [{ provide: DocumentListService, useValue: null }] + }); + service = TestBed.inject(DocumentActionsService); expect(service.canExecuteAction(file)).toBeFalsy(); }); diff --git a/lib/content-services/src/lib/document-list/services/document-actions.service.ts b/lib/content-services/src/lib/document-list/services/document-actions.service.ts index f8d7c94135..20f4601197 100644 --- a/lib/content-services/src/lib/document-list/services/document-actions.service.ts +++ b/lib/content-services/src/lib/document-list/services/document-actions.service.ts @@ -17,7 +17,7 @@ import { TranslationService } from '@alfresco/adf-core'; import { ContentService } from '../../common/services/content.service'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { NodeEntry } from '@alfresco/js-api'; import { Observable, Subject, throwError, of } from 'rxjs'; import { ContentActionHandler } from '../models/content-action.model'; @@ -30,19 +30,19 @@ import { ContentNodeDialogService } from '../../content-node-selector/content-no providedIn: 'root' }) export class DocumentActionsService { + private readonly nodeActionsService = inject(NodeActionsService); + private readonly contentNodeDialogService = inject(ContentNodeDialogService); + private readonly translation = inject(TranslationService); + private readonly documentListService = inject(DocumentListService); + private readonly contentService = inject(ContentService); + permissionEvent = new Subject<PermissionModel>(); error = new Subject<Error>(); success = new Subject<string>(); private handlers: { [id: string]: ContentActionHandler } = {}; - constructor( - private nodeActionsService: NodeActionsService, - private contentNodeDialogService: ContentNodeDialogService, - private translation: TranslationService, - private documentListService?: DocumentListService, - private contentService?: ContentService - ) { + constructor() { this.setupActionHandlers(); } diff --git a/lib/content-services/src/lib/document-list/services/document-list.service.ts b/lib/content-services/src/lib/document-list/services/document-list.service.ts index 8f6c98e314..8f09dab726 100644 --- a/lib/content-services/src/lib/document-list/services/document-list.service.ts +++ b/lib/content-services/src/lib/document-list/services/document-list.service.ts @@ -32,9 +32,9 @@ const ROOT_ID = '-root-'; providedIn: 'root' }) export class DocumentListService implements DocumentListLoader { - private nodesApiService = inject(NodesApiService); - private apiService = inject(AlfrescoApiService); - private customResourcesService = inject(CustomResourcesService); + private readonly nodesApiService = inject(NodesApiService); + private readonly apiService = inject(AlfrescoApiService); + private readonly customResourcesService = inject(CustomResourcesService); private _nodesApi: NodesApi; get nodes(): NodesApi { @@ -42,8 +42,8 @@ export class DocumentListService implements DocumentListLoader { return this._nodesApi; } - private _reload = new Subject<void>(); - private _resetSelection = new Subject<void>(); + private readonly _reload = new Subject<void>(); + private readonly _resetSelection = new Subject<void>(); /** Gets an observable that emits when the document list should be reloaded. */ reload$ = this._reload.asObservable(); diff --git a/lib/content-services/src/lib/document-list/services/folder-actions.service.ts b/lib/content-services/src/lib/document-list/services/folder-actions.service.ts index a59fc56a5e..e29e2b4da6 100644 --- a/lib/content-services/src/lib/document-list/services/folder-actions.service.ts +++ b/lib/content-services/src/lib/document-list/services/folder-actions.service.ts @@ -17,7 +17,7 @@ import { TranslationService } from '@alfresco/adf-core'; import { ContentService } from '../../common/services/content.service'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { NodeEntry } from '@alfresco/js-api'; import { Observable, Subject, throwError, of } from 'rxjs'; import { ContentActionHandler } from '../models/content-action.model'; @@ -29,18 +29,18 @@ import { NodeActionsService } from './node-actions.service'; providedIn: 'root' }) export class FolderActionsService { + private readonly nodeActionsService = inject(NodeActionsService); + private readonly documentListService = inject(DocumentListService); + private readonly contentService = inject(ContentService); + private readonly translation = inject(TranslationService); + permissionEvent = new Subject<PermissionModel>(); error = new Subject<Error>(); success = new Subject<string>(); private handlers: { [id: string]: ContentActionHandler } = {}; - constructor( - private nodeActionsService: NodeActionsService, - private documentListService: DocumentListService, - private contentService: ContentService, - private translation: TranslationService - ) { + constructor() { this.setupActionHandlers(); } diff --git a/lib/content-services/src/lib/document-list/services/lock.service.spec.ts b/lib/content-services/src/lib/document-list/services/lock.service.spec.ts index d915f877a4..3567bd2bb9 100644 --- a/lib/content-services/src/lib/document-list/services/lock.service.spec.ts +++ b/lib/content-services/src/lib/document-list/services/lock.service.spec.ts @@ -20,7 +20,8 @@ import { LockService } from './lock.service'; import { AuthenticationService, RedirectAuthService } from '@alfresco/adf-core'; import { Node } from '@alfresco/js-api'; import { addDays, subDays } from 'date-fns'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { EMPTY, of } from 'rxjs'; describe('LockService', () => { @@ -33,8 +34,11 @@ describe('LockService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [{ provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of() } }] + providers: [ + { provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of() } }, + provideHttpClient(), + provideHttpClientTesting() + ] }); service = TestBed.inject(LockService); authenticationService = TestBed.inject(AuthenticationService); diff --git a/lib/content-services/src/lib/document-list/services/lock.service.ts b/lib/content-services/src/lib/document-list/services/lock.service.ts index 8e2bfec303..6f0f1db5ef 100644 --- a/lib/content-services/src/lib/document-list/services/lock.service.ts +++ b/lib/content-services/src/lib/document-list/services/lock.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Node } from '@alfresco/js-api'; import { AuthenticationService } from '@alfresco/adf-core'; import { isAfter } from 'date-fns'; @@ -24,7 +24,7 @@ import { isAfter } from 'date-fns'; providedIn: 'root' }) export class LockService { - constructor(private authService: AuthenticationService) {} + private readonly authService = inject(AuthenticationService); isLocked(node: Node): boolean { let isLocked = false; diff --git a/lib/content-services/src/lib/document-list/services/node-actions.service.ts b/lib/content-services/src/lib/document-list/services/node-actions.service.ts index 558e274198..c0bd3db576 100644 --- a/lib/content-services/src/lib/document-list/services/node-actions.service.ts +++ b/lib/content-services/src/lib/document-list/services/node-actions.service.ts @@ -15,15 +15,13 @@ * limitations under the License. */ -import { Injectable, Output, EventEmitter } from '@angular/core'; +import { Injectable, Output, EventEmitter, inject, Injector, runInInjectionContext } from '@angular/core'; import { Node, NodeEntry } from '@alfresco/js-api'; import { Observable } from 'rxjs'; import { switchMap, map } from 'rxjs/operators'; -import { DownloadService } from '@alfresco/adf-core'; import { MatDialog } from '@angular/material/dialog'; import { ContentService } from '../../common/services/content.service'; import { NodeDownloadDirective } from '../../directives/node-download.directive'; -import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { DocumentListService } from './document-list.service'; import { ContentNodeDialogService } from '../../content-node-selector/content-node-dialog.service'; @@ -34,21 +32,19 @@ import { NodeAction } from '../models/node-action.enum'; }) // eslint-disable-next-line @angular-eslint/directive-class-suffix export class NodeActionsService { + private readonly contentDialogService = inject(ContentNodeDialogService); + dialogRef = inject(MatDialog); + content = inject(ContentService); + private readonly documentListService = inject(DocumentListService); + private readonly injector = inject(Injector); + @Output() error = new EventEmitter<any>(); - constructor( - private contentDialogService: ContentNodeDialogService, - public dialogRef: MatDialog, - public content: ContentService, - private documentListService?: DocumentListService, - private apiService?: AlfrescoApiService, - private dialog?: MatDialog, - private downloadService?: DownloadService - ) {} - downloadNode(node: NodeEntry) { - new NodeDownloadDirective(this.apiService, this.downloadService, this.dialog).downloadNode(node); + runInInjectionContext(this.injector, () => { + new NodeDownloadDirective().downloadNode(node); + }); } /** diff --git a/lib/content-services/src/lib/folder-directive/folder-edit.directive.ts b/lib/content-services/src/lib/folder-directive/folder-edit.directive.ts index 89d2890d6b..fadaafa523 100644 --- a/lib/content-services/src/lib/folder-directive/folder-edit.directive.ts +++ b/lib/content-services/src/lib/folder-directive/folder-edit.directive.ts @@ -17,7 +17,7 @@ /* eslint-disable @angular-eslint/no-input-rename */ -import { Directive, ElementRef, HostListener, Input, Output, EventEmitter } from '@angular/core'; +import { Directive, ElementRef, HostListener, Input, Output, EventEmitter, inject } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { Node } from '@alfresco/js-api'; import { FolderDialogComponent } from '../dialogs/folder/folder.dialog'; @@ -29,6 +29,10 @@ const DIALOG_WIDTH: number = 400; selector: '[adf-edit-folder]' }) export class FolderEditDirective { + dialogRef = inject(MatDialog); + elementRef = inject(ElementRef); + content = inject(ContentService); + /** Folder node to edit. */ @Input('adf-edit-folder') folder: Node; @@ -53,8 +57,6 @@ export class FolderEditDirective { } } - constructor(public dialogRef: MatDialog, public elementRef: ElementRef, public content: ContentService) {} - private get dialogConfig() { const { folder } = this; diff --git a/lib/content-services/src/lib/group/services/group.service.ts b/lib/content-services/src/lib/group/services/group.service.ts index c439c27a33..62f4f7518d 100644 --- a/lib/content-services/src/lib/group/services/group.service.ts +++ b/lib/content-services/src/lib/group/services/group.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ContentIncludeQuery, Group, GroupEntry, GroupsApi } from '@alfresco/js-api'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { from, Observable } from 'rxjs'; @@ -25,14 +25,14 @@ import { map } from 'rxjs/operators'; providedIn: 'root' }) export class GroupService { + private readonly alfrescoApiService = inject(AlfrescoApiService); + private _groupsApi: GroupsApi; get groupsApi(): GroupsApi { this._groupsApi = this._groupsApi ?? new GroupsApi(this.alfrescoApiService.getInstance()); return this._groupsApi; } - constructor(private alfrescoApiService: AlfrescoApiService) {} - async listAllGroupMembershipsForPerson(personId: string, opts?: any, accumulator = []): Promise<GroupEntry[]> { const groupsPaginated = await this.groupsApi.listGroupMembershipsForPerson(personId, opts); accumulator = [...accumulator, ...groupsPaginated.list.entries]; diff --git a/lib/content-services/src/lib/infinite-scroll-datasource/infinite-scroll-datasource.ts b/lib/content-services/src/lib/infinite-scroll-datasource/infinite-scroll-datasource.ts index fce1b0a07d..d1fddda8cb 100644 --- a/lib/content-services/src/lib/infinite-scroll-datasource/infinite-scroll-datasource.ts +++ b/lib/content-services/src/lib/infinite-scroll-datasource/infinite-scroll-datasource.ts @@ -22,8 +22,8 @@ import { take, tap } from 'rxjs/operators'; export abstract class InfiniteScrollDatasource<T> extends DataSource<T> { protected readonly dataStream = new BehaviorSubject<T[]>([]); - private isLoading$ = new Subject<boolean>(); - private subscription = new Subscription(); + private readonly isLoading$ = new Subject<boolean>(); + private readonly subscription = new Subscription(); private batchesFetched = 0; private _itemsCount = 0; private _firstItem: T; diff --git a/lib/content-services/src/lib/legal-hold/services/legal-hold.service.ts b/lib/content-services/src/lib/legal-hold/services/legal-hold.service.ts index b63e4d95e1..51305a69be 100644 --- a/lib/content-services/src/lib/legal-hold/services/legal-hold.service.ts +++ b/lib/content-services/src/lib/legal-hold/services/legal-hold.service.ts @@ -26,7 +26,7 @@ import { LegalHoldApi, RequestQuery } from '@alfresco/js-api'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from } from 'rxjs'; import { map } from 'rxjs/operators'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; @@ -35,14 +35,14 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; providedIn: 'root' }) export class LegalHoldService { + private readonly apiService = inject(AlfrescoApiService); + private _legalHoldApi: LegalHoldApi; get legalHoldApi(): LegalHoldApi { this._legalHoldApi = this._legalHoldApi ?? new LegalHoldApi(this.apiService.getInstance()); return this._legalHoldApi; } - constructor(private readonly apiService: AlfrescoApiService) {} - /** * Gets the list of holds available in the file plan. * diff --git a/lib/content-services/src/lib/mock/alfresco-api.service.mock.ts b/lib/content-services/src/lib/mock/alfresco-api.service.mock.ts index 95f2bbc885..5e87378d9c 100644 --- a/lib/content-services/src/lib/mock/alfresco-api.service.mock.ts +++ b/lib/content-services/src/lib/mock/alfresco-api.service.mock.ts @@ -17,12 +17,12 @@ import { Injectable } from '@angular/core'; import { AlfrescoApiService } from '../services/alfresco-api.service'; -import { AppConfigService, StorageService } from '@alfresco/adf-core'; @Injectable() export class AlfrescoApiServiceMock extends AlfrescoApiService { - constructor(protected appConfig: AppConfigService, protected storageService: StorageService) { - super(appConfig, storageService); + constructor() { + super(); + if (!this.alfrescoApi) { this.initAlfrescoApi(); } diff --git a/lib/content-services/src/lib/mock/saved-searches-derived.mock.ts b/lib/content-services/src/lib/mock/saved-searches-derived.mock.ts index a2da535b65..e31612039e 100644 --- a/lib/content-services/src/lib/mock/saved-searches-derived.mock.ts +++ b/lib/content-services/src/lib/mock/saved-searches-derived.mock.ts @@ -17,8 +17,6 @@ import { NodeEntry } from '@alfresco/js-api'; import { of, Observable, ReplaySubject } from 'rxjs'; -import { AlfrescoApiService } from '../services'; -import { AuthenticationService } from '@alfresco/adf-core'; import { SavedSearch, SavedSearchesBaseService } from '../common'; import { Injectable } from '@angular/core'; @@ -28,10 +26,6 @@ export class MockSavedSearchesService extends SavedSearchesBaseService { public updateSpy = jasmine.createSpy('updateSavedSearches').and.returnValue(of({} as NodeEntry)); - constructor(apiService: AlfrescoApiService, authService: AuthenticationService) { - super(apiService, authService); - } - protected fetchAllSavedSearches(): Observable<SavedSearch[]> { return this.fetchSubject.asObservable(); } diff --git a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.ts b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.ts index 9ef49ffc6b..0030ce5f8c 100644 --- a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.ts +++ b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.ts @@ -16,7 +16,7 @@ */ import { Node } from '@alfresco/js-api'; -import { Component, EventEmitter, Inject, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, OnInit, Output, ViewEncapsulation, inject } from '@angular/core'; import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { NewVersionUploaderDialogData, NewVersionUploaderData, NewVersionUploaderDataAction } from './models'; import { CommonModule } from '@angular/common'; @@ -45,6 +45,9 @@ import { VersionListComponent } from '../version-manager/version-list.component' } }) export class NewVersionUploaderDialogComponent implements OnInit { + data = inject<NewVersionUploaderDialogData>(MAT_DIALOG_DATA); + private readonly dialogRef = inject<MatDialogRef<NewVersionUploaderDialogComponent>>(MatDialogRef); + /** * Dialog title to show into the header. * If data.title is not provided, a default title is set @@ -59,11 +62,6 @@ export class NewVersionUploaderDialogComponent implements OnInit { @Output() uploadError = new EventEmitter<any>(); - constructor( - @Inject(MAT_DIALOG_DATA) public data: NewVersionUploaderDialogData, - private dialogRef: MatDialogRef<NewVersionUploaderDialogComponent> - ) {} - ngOnInit(): void { this.setDialogTitle(); } diff --git a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.ts b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.ts index 740c9755b9..86d01c8f04 100644 --- a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.ts +++ b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { AlfrescoApiService } from '../services/alfresco-api.service'; @@ -30,14 +30,16 @@ import { take } from 'rxjs/operators'; providedIn: 'root' }) export class NewVersionUploaderService { + private readonly apiService = inject(AlfrescoApiService); + private readonly dialog = inject(MatDialog); + private readonly overlayContainer = inject(OverlayContainer); + private _versionsApi: VersionsApi; get versionsApi(): VersionsApi { this._versionsApi = this._versionsApi ?? new VersionsApi(this.apiService.getInstance()); return this._versionsApi; } - constructor(private apiService: AlfrescoApiService, private dialog: MatDialog, private overlayContainer: OverlayContainer) {} - /** * Open a dialog NewVersionUploaderDialogComponent to display: * - a side by side comparison between the current target node (type, name, icon) and the new file that should update it's version diff --git a/lib/content-services/src/lib/node-comments/services/node-comments.service.spec.ts b/lib/content-services/src/lib/node-comments/services/node-comments.service.spec.ts index a03eedfec6..0d8f67484a 100644 --- a/lib/content-services/src/lib/node-comments/services/node-comments.service.spec.ts +++ b/lib/content-services/src/lib/node-comments/services/node-comments.service.spec.ts @@ -19,7 +19,8 @@ import { TestBed } from '@angular/core/testing'; import { CommentModel, RedirectAuthService } from '@alfresco/adf-core'; import { fakeContentComment, fakeContentComments } from '../mocks/node-comments.mock'; import { NodeCommentsService } from './node-comments.service'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { EMPTY, of } from 'rxjs'; import { AlfrescoApiService } from '../../services'; import { AlfrescoApiServiceMock } from '../../mock'; @@ -31,10 +32,11 @@ describe('NodeCommentsService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], providers: [ { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }, - { provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of() } } + { provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of() } }, + provideHttpClient(), + provideHttpClientTesting() ] }); service = TestBed.inject(NodeCommentsService); diff --git a/lib/content-services/src/lib/node-comments/services/node-comments.service.ts b/lib/content-services/src/lib/node-comments/services/node-comments.service.ts index 1a24590db2..9ef7a42321 100644 --- a/lib/content-services/src/lib/node-comments/services/node-comments.service.ts +++ b/lib/content-services/src/lib/node-comments/services/node-comments.service.ts @@ -17,7 +17,7 @@ import { CommentModel, CommentsService, User } from '@alfresco/adf-core'; import { CommentEntry, CommentsApi, Comment, PeopleApi } from '@alfresco/js-api'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from } from 'rxjs'; import { map } from 'rxjs/operators'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; @@ -26,6 +26,8 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; providedIn: 'root' }) export class NodeCommentsService implements CommentsService { + private readonly apiService = inject(AlfrescoApiService); + private _commentsApi: CommentsApi; get commentsApi(): CommentsApi { this._commentsApi = this._commentsApi ?? new CommentsApi(this.apiService.getInstance()); @@ -38,8 +40,6 @@ export class NodeCommentsService implements CommentsService { return this._peopleApi; } - constructor(private readonly apiService: AlfrescoApiService) {} - /** * Gets all comments that have been added to a task. * diff --git a/lib/content-services/src/lib/permission-manager/components/add-permission/add-permission-dialog.component.ts b/lib/content-services/src/lib/permission-manager/components/add-permission/add-permission-dialog.component.ts index 35b20e91a2..ee544eed63 100644 --- a/lib/content-services/src/lib/permission-manager/components/add-permission/add-permission-dialog.component.ts +++ b/lib/content-services/src/lib/permission-manager/components/add-permission/add-permission-dialog.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject, ViewEncapsulation } from '@angular/core'; +import { Component, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { NodeEntry, PermissionElement } from '@alfresco/js-api'; import { AddPermissionDialogData } from './add-permission-dialog-data.interface'; @@ -51,16 +51,16 @@ import { UserRoleColumnComponent } from '../user-role-column/user-role-column.co encapsulation: ViewEncapsulation.None }) export class AddPermissionDialogComponent { + data = inject<AddPermissionDialogData>(MAT_DIALOG_DATA); + private readonly dialogRef = inject<MatDialogRef<AddPermissionDialogComponent>>(MatDialogRef); + isSearchActive = true; selectedMembers: MemberModel[] = []; - private existingMembers: PermissionElement[] = []; + private readonly existingMembers: PermissionElement[] = []; currentSelection: NodeEntry[] = []; - constructor( - @Inject(MAT_DIALOG_DATA) public data: AddPermissionDialogData, - private dialogRef: MatDialogRef<AddPermissionDialogComponent> - ) { + constructor() { this.existingMembers = this.data.node.permissions.locallySet || []; } diff --git a/lib/content-services/src/lib/permission-manager/components/add-permission/add-permission.component.ts b/lib/content-services/src/lib/permission-manager/components/add-permission/add-permission.component.ts index 0828fac757..018d31d874 100644 --- a/lib/content-services/src/lib/permission-manager/components/add-permission/add-permission.component.ts +++ b/lib/content-services/src/lib/permission-manager/components/add-permission/add-permission.component.ts @@ -16,7 +16,7 @@ */ import { Node, NodeEntry, PermissionElement } from '@alfresco/js-api'; -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, inject } from '@angular/core'; import { NodePermissionService } from '../../services/node-permission.service'; import { RoleModel } from '../../models/role.model'; import { ContentService } from '../../../common/services/content.service'; @@ -37,6 +37,9 @@ import { TranslatePipe } from '@ngx-translate/core'; * @deprecated in 4.4.0, use adf-add-permission-panel instead. */ export class AddPermissionComponent implements OnInit { + private readonly nodePermissionService = inject(NodePermissionService); + private readonly contentService = inject(ContentService); + /** ID of the target node. */ @Input({ required: true }) nodeId: string; @@ -53,8 +56,6 @@ export class AddPermissionComponent implements OnInit { currentNode: Node; currentNodeRoles: RoleModel[]; - constructor(private nodePermissionService: NodePermissionService, private contentService: ContentService) {} - ngOnInit(): void { this.nodePermissionService.getNodeWithRoles(this.nodeId).subscribe(({ node, roles }) => { this.currentNode = node; diff --git a/lib/content-services/src/lib/permission-manager/components/add-permission/search-config-permission.service.ts b/lib/content-services/src/lib/permission-manager/components/add-permission/search-config-permission.service.ts index 111566defd..93cf0c7e03 100644 --- a/lib/content-services/src/lib/permission-manager/components/add-permission/search-config-permission.service.ts +++ b/lib/content-services/src/lib/permission-manager/components/add-permission/search-config-permission.service.ts @@ -16,7 +16,7 @@ */ import { SearchRequest } from '@alfresco/js-api'; -import { Injectable, Optional, Inject, InjectionToken } from '@angular/core'; +import { Injectable, InjectionToken, inject } from '@angular/core'; import { SearchConfigurationInterface } from '../../../common/interfaces/search-configuration.interface'; export const SEARCH_QUERY_TOKEN = new InjectionToken<QueryProvider>('Alfresco Search Query Token'); @@ -26,11 +26,7 @@ export interface QueryProvider { @Injectable() export class SearchPermissionConfigurationService implements SearchConfigurationInterface { - constructor( - @Optional() - @Inject(SEARCH_QUERY_TOKEN) - private queryProvider: QueryProvider - ) {} + private readonly queryProvider = inject<QueryProvider>(SEARCH_QUERY_TOKEN, { optional: true }); public generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): SearchRequest { return { diff --git a/lib/content-services/src/lib/permission-manager/components/inherited-button.directive.ts b/lib/content-services/src/lib/permission-manager/components/inherited-button.directive.ts index 2835fdd727..294898f8f6 100644 --- a/lib/content-services/src/lib/permission-manager/components/inherited-button.directive.ts +++ b/lib/content-services/src/lib/permission-manager/components/inherited-button.directive.ts @@ -16,7 +16,7 @@ */ /* eslint-disable @angular-eslint/no-input-rename */ -import { Directive, Input, Output, EventEmitter } from '@angular/core'; +import { Directive, Input, Output, EventEmitter, inject } from '@angular/core'; import { Node } from '@alfresco/js-api'; import { ContentService } from '../../common/services/content.service'; import { NodesApiService } from '../../common/services/nodes-api.service'; @@ -30,6 +30,9 @@ import { AllowableOperationsEnum } from '../../common/models/allowable-operation } }) export class InheritPermissionDirective { + private readonly nodeService = inject(NodesApiService); + private readonly contentService = inject(ContentService); + /** ID of the node to add/remove inherited permissions. */ @Input() nodeId: string; @@ -42,8 +45,6 @@ export class InheritPermissionDirective { @Output() error: EventEmitter<any> = new EventEmitter<any>(); - constructor(private nodeService: NodesApiService, private contentService: ContentService) {} - onInheritPermissionClicked() { this.nodeService.getNode(this.nodeId).subscribe((node: Node) => { if (this.contentService.hasAllowableOperations(node, AllowableOperationsEnum.UPDATEPERMISSIONS)) { diff --git a/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.ts b/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.ts index c5195b6063..bec6ccfe91 100644 --- a/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.ts +++ b/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.ts @@ -49,6 +49,9 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; encapsulation: ViewEncapsulation.None }) export class PermissionListComponent implements OnInit { + readonly permissionList = inject(PermissionListService); + private readonly contentService = inject(ContentService); + /** ID of the node whose permissions you want to show. */ @Input({ required: true }) nodeId: string; @@ -71,10 +74,7 @@ export class PermissionListComponent implements OnInit { return this._updatePermissionsAllowed; } - constructor( - public readonly permissionList: PermissionListService, - private readonly contentService: ContentService - ) { + constructor() { this.error = this.permissionList.errored; this.update = this.permissionList.updated; } diff --git a/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.service.ts b/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.service.ts index 1d042770af..91d77e822d 100644 --- a/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.service.ts +++ b/lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.service.ts @@ -17,7 +17,7 @@ import { NotificationService } from '@alfresco/adf-core'; import { Node, PermissionElement } from '@alfresco/js-api'; -import { EventEmitter, Injectable } from '@angular/core'; +import { EventEmitter, Injectable, inject } from '@angular/core'; import { MatSlideToggleChange } from '@angular/material/slide-toggle'; import { BehaviorSubject, forkJoin, Observable, of, Subject } from 'rxjs'; import { finalize, map, switchMap } from 'rxjs/operators'; @@ -36,6 +36,12 @@ const SITE_MANAGER_ROLE = 'SiteManager'; providedIn: 'root' }) export class PermissionListService { + private readonly nodeService = inject(NodesApiService); + private readonly nodePermissionService = inject(NodePermissionService); + private readonly nodePermissionDialogService = inject(NodePermissionDialogService); + private readonly contentService = inject(ContentService); + private readonly notificationService = inject(NotificationService); + updated = new EventEmitter<PermissionDisplayModel>(); errored = new EventEmitter<PermissionDisplayModel>(); @@ -58,14 +64,6 @@ export class PermissionListService { private node: Node; private roles: RoleModel[]; - constructor( - private nodeService: NodesApiService, - private nodePermissionService: NodePermissionService, - private nodePermissionDialogService: NodePermissionDialogService, - private contentService: ContentService, - private notificationService: NotificationService - ) {} - fetchPermission(nodeId: string) { this.loading$.next(true); this.nodePermissionService diff --git a/lib/content-services/src/lib/permission-manager/components/pop-over.directive.ts b/lib/content-services/src/lib/permission-manager/components/pop-over.directive.ts index 897e25e138..22e078cbba 100644 --- a/lib/content-services/src/lib/permission-manager/components/pop-over.directive.ts +++ b/lib/content-services/src/lib/permission-manager/components/pop-over.directive.ts @@ -38,6 +38,11 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; exportAs: 'adfPopOver' }) export class PopOverDirective implements OnInit, OnDestroy, AfterViewInit { + private readonly element = inject(ElementRef); + private readonly overlay = inject(Overlay); + private readonly vcr = inject(ViewContainerRef); + private readonly focusTrapFactory = inject(ConfigurableFocusTrapFactory); + get open(): boolean { return this._open; } @@ -53,13 +58,6 @@ export class PopOverDirective implements OnInit, OnDestroy, AfterViewInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private element: ElementRef, - private overlay: Overlay, - private vcr: ViewContainerRef, - private focusTrapFactory: ConfigurableFocusTrapFactory - ) {} - ngOnInit(): void { this.createOverlay(); } diff --git a/lib/content-services/src/lib/permission-manager/components/user-icon-column/user-icon-column.component.ts b/lib/content-services/src/lib/permission-manager/components/user-icon-column/user-icon-column.component.ts index cfebe82eda..c48ccd3914 100644 --- a/lib/content-services/src/lib/permission-manager/components/user-icon-column/user-icon-column.component.ts +++ b/lib/content-services/src/lib/permission-manager/components/user-icon-column/user-icon-column.component.ts @@ -17,7 +17,7 @@ import { IconModule, InitialUsernamePipe, User } from '@alfresco/adf-core'; import { Group, NodeEntry } from '@alfresco/js-api'; -import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, Input, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { NodePermissionService } from '../../services/node-permission.service'; import { CommonModule } from '@angular/common'; @@ -50,6 +50,8 @@ import { TranslatePipe } from '@ngx-translate/core'; host: { class: 'adf-user-icon-column adf-datatable-content-cell' } }) export class UserIconColumnComponent implements OnInit { + private readonly nodePermissionService = inject(NodePermissionService); + @Input() context: any; @@ -66,8 +68,6 @@ export class UserIconColumnComponent implements OnInit { return this.context?.row?.isSelected || this.selected; } - constructor(private nodePermissionService: NodePermissionService) {} - ngOnInit() { if (this.context) { const { person, group, authorityId } = this.context.row.obj?.entry ?? this.context.row.obj; diff --git a/lib/content-services/src/lib/permission-manager/components/user-name-column/user-name-column.component.ts b/lib/content-services/src/lib/permission-manager/components/user-name-column/user-name-column.component.ts index 1a318a74ee..926e6203f1 100644 --- a/lib/content-services/src/lib/permission-manager/components/user-name-column/user-name-column.component.ts +++ b/lib/content-services/src/lib/permission-manager/components/user-name-column/user-name-column.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, Input, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { Group, NodeEntry } from '@alfresco/js-api'; import { NodePermissionService } from '../../services/node-permission.service'; @@ -39,6 +39,8 @@ import { CommonModule } from '@angular/common'; host: { class: 'adf-user-name-column adf-datatable-content-cell adf-expand-cell-5 adf-ellipsis-cell' } }) export class UserNameColumnComponent implements OnInit { + private readonly nodePermissionService = inject(NodePermissionService); + @Input() context: any; @@ -48,8 +50,6 @@ export class UserNameColumnComponent implements OnInit { displayText$ = new BehaviorSubject<string>(''); subTitleText$ = new BehaviorSubject<string>(''); - constructor(private nodePermissionService: NodePermissionService) {} - ngOnInit() { if (this.context != null) { const { person, group, authorityId } = this.context.row.obj?.entry ?? this.context.row.obj; diff --git a/lib/content-services/src/lib/permission-manager/services/node-permission-dialog.service.ts b/lib/content-services/src/lib/permission-manager/services/node-permission-dialog.service.ts index 4651d21e16..1362746ea4 100644 --- a/lib/content-services/src/lib/permission-manager/services/node-permission-dialog.service.ts +++ b/lib/content-services/src/lib/permission-manager/services/node-permission-dialog.service.ts @@ -17,7 +17,7 @@ import { Node, PermissionElement } from '@alfresco/js-api'; import { MatDialog } from '@angular/material/dialog'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, Subject, throwError } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { NodePermissionService } from './node-permission.service'; @@ -31,7 +31,9 @@ import { AllowableOperationsEnum } from '../../common/models/allowable-operation providedIn: 'root' }) export class NodePermissionDialogService { - constructor(private dialog: MatDialog, private nodePermissionService: NodePermissionService, private contentService: ContentService) {} + private readonly dialog = inject(MatDialog); + private readonly nodePermissionService = inject(NodePermissionService); + private readonly contentService = inject(ContentService); /** * Opens a dialog to add permissions to a node. diff --git a/lib/content-services/src/lib/permission-manager/services/node-permission.service.ts b/lib/content-services/src/lib/permission-manager/services/node-permission.service.ts index abbd06f00c..e29cac5b51 100644 --- a/lib/content-services/src/lib/permission-manager/services/node-permission.service.ts +++ b/lib/content-services/src/lib/permission-manager/services/node-permission.service.ts @@ -21,7 +21,7 @@ import { NodesApiService } from '../../common/services/nodes-api.service'; import { EcmUserModel } from '../../common/models/ecm-user.model'; import { Group, GroupMemberPaging, GroupsApi, Node, PathElement, PermissionElement, SearchRequest } from '@alfresco/js-api'; import { SearchService } from '../../search/services/search.service'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { forkJoin, from, Observable, of, throwError } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; import { PermissionDisplayModel } from '../models/permission.model'; @@ -31,19 +31,17 @@ import { RoleModel } from '../models/role.model'; providedIn: 'root' }) export class NodePermissionService { + private readonly apiService = inject(AlfrescoApiService); + private readonly searchApiService = inject(SearchService); + private readonly nodeService = inject(NodesApiService); + private readonly translation = inject(TranslationService); + private _groupsApi: GroupsApi; get groupsApi(): GroupsApi { this._groupsApi = this._groupsApi ?? new GroupsApi(this.apiService.getInstance()); return this._groupsApi; } - constructor( - private apiService: AlfrescoApiService, - private searchApiService: SearchService, - private nodeService: NodesApiService, - private translation: TranslationService - ) {} - /** * Gets a list of roles for the current node. * diff --git a/lib/content-services/src/lib/prediction/services/prediction.service.ts b/lib/content-services/src/lib/prediction/services/prediction.service.ts index b3a0108ee8..c27426caf0 100644 --- a/lib/content-services/src/lib/prediction/services/prediction.service.ts +++ b/lib/content-services/src/lib/prediction/services/prediction.service.ts @@ -15,13 +15,15 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { PredictionsApi, PredictionPaging, ReviewStatus } from '@alfresco/js-api'; import { from, Observable } from 'rxjs'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; @Injectable({ providedIn: 'root' }) export class PredictionService { + private readonly apiService = inject(AlfrescoApiService); + private _predictionsApi: PredictionsApi; get predictionsApi(): PredictionsApi { @@ -29,8 +31,6 @@ export class PredictionService { return this._predictionsApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Get predictions for a given node * diff --git a/lib/content-services/src/lib/search-ai/services/search-ai.service.ts b/lib/content-services/src/lib/search-ai/services/search-ai.service.ts index 316b674ec2..e5c2909470 100644 --- a/lib/content-services/src/lib/search-ai/services/search-ai.service.ts +++ b/lib/content-services/src/lib/search-ai/services/search-ai.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AiAnswerEntry, KnowledgeRetrievalConfigEntry, QuestionModel, QuestionRequest, SearchAiApi } from '@alfresco/js-api'; import { BehaviorSubject, from, Observable } from 'rxjs'; import { SelectionState } from '@alfresco/adf-extensions'; @@ -27,7 +27,10 @@ import { AlfrescoApiService } from '../../services'; providedIn: 'root' }) export class SearchAiService { - private toggleSearchAiInput = new BehaviorSubject<SearchAiInputState>({ + private readonly apiService = inject(AlfrescoApiService); + private readonly translateService = inject(TranslateService); + + private readonly toggleSearchAiInput = new BehaviorSubject<SearchAiInputState>({ active: false }); private _searchAiApi: SearchAiApi; @@ -39,8 +42,6 @@ export class SearchAiService { toggleSearchAiInput$ = this.toggleSearchAiInput.asObservable(); - constructor(private apiService: AlfrescoApiService, private translateService: TranslateService) {} - /** * Update the state of the search AI input. * diff --git a/lib/content-services/src/lib/search/components/reset-search.directive.ts b/lib/content-services/src/lib/search/components/reset-search.directive.ts index b181843713..a54679e782 100644 --- a/lib/content-services/src/lib/search/components/reset-search.directive.ts +++ b/lib/content-services/src/lib/search/components/reset-search.directive.ts @@ -15,17 +15,17 @@ * limitations under the License. */ -import { Directive, HostListener } from '@angular/core'; +import { Directive, HostListener, inject } from '@angular/core'; import { SearchFacetFiltersService } from '../services/search-facet-filters.service'; @Directive({ selector: '[adf-reset-search]' }) export class ResetSearchDirective { + private readonly filterService = inject(SearchFacetFiltersService); + @HostListener('click') onClick() { this.filterService.reset(); } - - constructor(private filterService: SearchFacetFiltersService) {} } diff --git a/lib/content-services/src/lib/search/components/search-check-list/search-check-list.component.ts b/lib/content-services/src/lib/search/components/search-check-list/search-check-list.component.ts index 1a6d1c867d..5551aa2078 100644 --- a/lib/content-services/src/lib/search/components/search-check-list/search-check-list.component.ts +++ b/lib/content-services/src/lib/search/components/search-check-list/search-check-list.component.ts @@ -44,6 +44,8 @@ export interface SearchListOption { host: { class: 'adf-search-check-list' } }) export class SearchCheckListComponent implements SearchWidget, OnInit { + private readonly translationService = inject(TranslationService); + id: string; settings?: SearchWidgetSettings; context?: SearchQueryBuilderService; @@ -57,7 +59,7 @@ export class SearchCheckListComponent implements SearchWidget, OnInit { private readonly destroyRef = inject(DestroyRef); - constructor(private translationService: TranslationService) { + constructor() { this.options = new SearchFilterList<SearchListOption>(); } diff --git a/lib/content-services/src/lib/search/components/search-chip-list/search-chip-list.component.ts b/lib/content-services/src/lib/search/components/search-chip-list/search-chip-list.component.ts index 3295d4cea7..428c556b42 100644 --- a/lib/content-services/src/lib/search/components/search-chip-list/search-chip-list.component.ts +++ b/lib/content-services/src/lib/search/components/search-chip-list/search-chip-list.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, ViewEncapsulation, Input } from '@angular/core'; +import { Component, ViewEncapsulation, Input, inject } from '@angular/core'; import { SearchFilterComponent } from '../../components/search-filter/search-filter.component'; import { SearchFacetFiltersService } from '../../services/search-facet-filters.service'; import { CommonModule } from '@angular/common'; @@ -32,6 +32,8 @@ import { IconModule } from '@alfresco/adf-core'; host: { class: 'adf-search-chip-list' } }) export class SearchChipListComponent { + facetFiltersService = inject(SearchFacetFiltersService); + /** * Search filter to supply the data for the chips. * Not required from 4.5.0 and later versions @deprecated @@ -42,6 +44,4 @@ export class SearchChipListComponent { /** Flag used to enable the display of a clear-all-filters button. */ @Input() clearAll: boolean = false; - - constructor(public facetFiltersService: SearchFacetFiltersService) {} } diff --git a/lib/content-services/src/lib/search/components/search-control.component.ts b/lib/content-services/src/lib/search/components/search-control.component.ts index 79deaaf780..cd2168791a 100644 --- a/lib/content-services/src/lib/search/components/search-control.component.ts +++ b/lib/content-services/src/lib/search/components/search-control.component.ts @@ -26,7 +26,8 @@ import { TemplateRef, ViewChild, ViewChildren, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { NodeEntry } from '@alfresco/js-api'; import { Subject } from 'rxjs'; @@ -46,6 +47,9 @@ import { TranslatePipe } from '@ngx-translate/core'; host: { class: 'adf-search-control' } }) export class SearchControlComponent { + authService = inject(AuthenticationService); + private readonly thumbnailService = inject(ThumbnailService); + /** Toggles highlighting of the search term in the results. */ @Input() highlight: boolean = false; @@ -100,7 +104,7 @@ export class SearchControlComponent { searchAutocomplete: SearchComponent; @ViewChildren(MatListItem) - private listResultElement: QueryList<MatListItem>; + private readonly listResultElement: QueryList<MatListItem>; @ContentChild(EmptySearchResultComponent) emptySearchTemplate: EmptySearchResultComponent; @@ -109,11 +113,6 @@ export class SearchControlComponent { noSearchResultTemplate: TemplateRef<any> = null; searchTerm: string = ''; - constructor( - public authService: AuthenticationService, - private readonly thumbnailService: ThumbnailService - ) {} - isNoSearchTemplatePresent(): boolean { return !!this.emptySearchTemplate; } diff --git a/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range-tabbed.component.ts b/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range-tabbed.component.ts index ee2cc79028..e4c6ab2200 100644 --- a/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range-tabbed.component.ts +++ b/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range-tabbed.component.ts @@ -42,6 +42,8 @@ const DEFAULT_DATE_DISPLAY_FORMAT = 'dd-MMM-yy'; encapsulation: ViewEncapsulation.None }) export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit { + private readonly translateService = inject(TranslationService); + private value: { [key: string]: Partial<SearchDateRange> } = {}; private readonly queryMapByField = new Map<string, string>(); @@ -67,8 +69,6 @@ export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit { combinedDisplayValue: string; reset$ = this.resetSubject$.asObservable(); - constructor(private translateService: TranslationService) {} - ngOnInit(): void { this.fields = this.settings?.field.split(',').map((field) => field.trim()); this.setDefaultDateFormatSettings(); diff --git a/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range/search-date-range.component.ts b/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range/search-date-range.component.ts index 3977c22295..64b1d1bafa 100644 --- a/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range/search-date-range.component.ts +++ b/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range/search-date-range.component.ts @@ -23,7 +23,6 @@ import { ElementRef, EventEmitter, inject, - Inject, Input, OnInit, Output, @@ -72,6 +71,10 @@ const DEFAULT_DATE_DISPLAY_FORMAT = 'dd-MMM-yy'; host: { class: 'adf-search-date-range' } }) export class SearchDateRangeComponent implements OnInit, AfterViewInit { + private readonly userPreferencesService = inject(UserPreferencesService); + private readonly dateAdapter = inject<DateAdapter<DateFnsAdapter>>(DateAdapter); + private readonly dateFormatConfig = inject<MatDateFormats>(MAT_DATE_FORMATS); + @Input() dateFormat = DEFAULT_DATE_DISPLAY_FORMAT; @Input() @@ -113,11 +116,7 @@ export class SearchDateRangeComponent implements OnInit, AfterViewInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private userPreferencesService: UserPreferencesService, - private dateAdapter: DateAdapter<DateFnsAdapter>, - @Inject(MAT_DATE_FORMATS) private dateFormatConfig: MatDateFormats - ) { + constructor() { // Use effect to react to locale signal changes (must be in injection context) effect(() => { const locale = this.userPreferencesService.localeSignal(); diff --git a/lib/content-services/src/lib/search/components/search-datetime-range/search-datetime-range.component.ts b/lib/content-services/src/lib/search/components/search-datetime-range/search-datetime-range.component.ts index 2b0471b513..d9110b2325 100644 --- a/lib/content-services/src/lib/search/components/search-datetime-range/search-datetime-range.component.ts +++ b/lib/content-services/src/lib/search/components/search-datetime-range/search-datetime-range.component.ts @@ -60,6 +60,9 @@ export const DEFAULT_DATETIME_FORMAT: string = 'dd/MM/yyyy HH:mm'; host: { class: 'adf-search-date-range' } }) export class SearchDatetimeRangeComponent implements SearchWidget, OnInit { + private readonly dateAdapter = inject<DateAdapter<Date>>(DateAdapter); + private readonly dateTimeAdapter = inject<DatetimeAdapter<Date>>(DatetimeAdapter); + from: FormControl<Date>; to: FormControl<Date>; @@ -79,11 +82,6 @@ export class SearchDatetimeRangeComponent implements SearchWidget, OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private readonly dateAdapter: DateAdapter<Date>, - private readonly dateTimeAdapter: DatetimeAdapter<Date> - ) {} - getFromValidationMessage(): string { return this.from.hasError('invalidOnChange') || this.hasParseError(this.from) ? 'SEARCH.FILTER.VALIDATION.INVALID-DATETIME' diff --git a/lib/content-services/src/lib/search/components/search-facet-field/search-facet-field.component.ts b/lib/content-services/src/lib/search/components/search-facet-field/search-facet-field.component.ts index 55cbc2d3be..20f1accdd3 100644 --- a/lib/content-services/src/lib/search/components/search-facet-field/search-facet-field.component.ts +++ b/lib/content-services/src/lib/search/components/search-facet-field/search-facet-field.component.ts @@ -39,9 +39,9 @@ import { FormsModule } from '@angular/forms'; encapsulation: ViewEncapsulation.None }) export class SearchFacetFieldComponent implements FacetWidget { - private queryBuilder = inject(SearchQueryBuilderService); - private searchFacetFiltersService = inject(SearchFacetFiltersService); - private translationService = inject(TranslationService); + private readonly queryBuilder = inject(SearchQueryBuilderService); + private readonly searchFacetFiltersService = inject(SearchFacetFiltersService); + private readonly translationService = inject(TranslationService); @Input({ required: true }) field: FacetField; diff --git a/lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.ts b/lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.ts index a114f9c8e3..cdf6f70f05 100644 --- a/lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.ts +++ b/lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.ts @@ -39,6 +39,10 @@ import { SitesService } from '../../../common/services/sites.service'; encapsulation: ViewEncapsulation.None }) export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnInit { + private readonly tagService = inject(TagService); + private readonly categoryService = inject(CategoryService); + private readonly sitesService = inject(SitesService); + id: string; settings?: SearchWidgetSettings; context?: SearchQueryBuilderService; @@ -48,18 +52,14 @@ export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnI selectedOptions: AutocompleteOption[] = []; enableChangeUpdate: boolean; - private resetSubject$ = new Subject<void>(); + private readonly resetSubject$ = new Subject<void>(); reset$: Observable<void> = this.resetSubject$.asObservable(); - private autocompleteOptionsSubject$ = new BehaviorSubject<AutocompleteOption[]>([]); + private readonly autocompleteOptionsSubject$ = new BehaviorSubject<AutocompleteOption[]>([]); autocompleteOptions$: Observable<AutocompleteOption[]> = this.autocompleteOptionsSubject$.asObservable(); private readonly destroyRef = inject(DestroyRef); - constructor( - private readonly tagService: TagService, - private readonly categoryService: CategoryService, - private readonly sitesService: SitesService - ) { + constructor() { this.options = new SearchFilterList<AutocompleteOption[]>(); } diff --git a/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-chip-tabbed.component.ts b/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-chip-tabbed.component.ts index 6b1cd10a88..b5fa2b2b10 100644 --- a/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-chip-tabbed.component.ts +++ b/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-chip-tabbed.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ChangeDetectorRef, Component, ElementRef, Input, ViewChild, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectorRef, Component, ElementRef, Input, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y'; import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu'; import { TabbedFacetField } from '../../../models/tabbed-facet-field.interface'; @@ -45,6 +45,9 @@ import { IconModule } from '@alfresco/adf-core'; encapsulation: ViewEncapsulation.None }) export class SearchFacetChipTabbedComponent { + private readonly focusTrapFactory = inject(ConfigurableFocusTrapFactory); + private readonly changeDetectorRef = inject(ChangeDetectorRef); + @Input() tabbedFacet: TabbedFacetField; @@ -54,8 +57,8 @@ export class SearchFacetChipTabbedComponent { @ViewChild('menuTrigger', { static: false }) menuTrigger: MatMenuTrigger; - private resetSubject$ = new Subject<void>(); - private applySubject$ = new Subject<void>(); + private readonly resetSubject$ = new Subject<void>(); + private readonly applySubject$ = new Subject<void>(); displayValue = ''; reset$ = this.resetSubject$.asObservable(); @@ -64,11 +67,6 @@ export class SearchFacetChipTabbedComponent { chipIcon = 'keyboard_arrow_down'; isPopulated = false; - constructor( - private readonly focusTrapFactory: ConfigurableFocusTrapFactory, - private readonly changeDetectorRef: ChangeDetectorRef - ) {} - onMenuOpen() { setTimeout(() => { if (this.menuContainer && !this.focusTrap) { diff --git a/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-tabbed-content.component.ts b/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-tabbed-content.component.ts index 277af6340b..6e92e01226 100644 --- a/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-tabbed-content.component.ts +++ b/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-tabbed-content.component.ts @@ -36,9 +36,9 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; encapsulation: ViewEncapsulation.None }) export class SearchFacetTabbedContentComponent implements OnInit, OnChanges, FacetWidget { - private queryBuilder = inject(SearchQueryBuilderService); - private translationService = inject(TranslationService); - private searchFacetFiltersService = inject(SearchFacetFiltersService); + private readonly queryBuilder = inject(SearchQueryBuilderService); + private readonly translationService = inject(TranslationService); + private readonly searchFacetFiltersService = inject(SearchFacetFiltersService); @Input({ required: true }) tabbedFacet: TabbedFacetField; @@ -55,7 +55,7 @@ export class SearchFacetTabbedContentComponent implements OnInit, OnChanges, Fac @Output() displayValue$ = new EventEmitter<string>(); - private resetSubject$ = new Subject<void>(); + private readonly resetSubject$ = new Subject<void>(); reset$ = this.resetSubject$.asObservable(); chipIcon = 'keyboard_arrow_down'; diff --git a/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip/search-facet-chip.component.ts b/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip/search-facet-chip.component.ts index 053166e566..0c5c123c5f 100644 --- a/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip/search-facet-chip.component.ts +++ b/lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip/search-facet-chip.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, ElementRef, Input, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, ElementRef, Input, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y'; import { FacetField } from '../../../models/facet-field.interface'; import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu'; @@ -44,6 +44,8 @@ import { IconModule } from '@alfresco/adf-core'; encapsulation: ViewEncapsulation.None }) export class SearchFacetChipComponent { + private readonly focusTrapFactory = inject(ConfigurableFocusTrapFactory); + @Input({ required: true }) field: FacetField; @@ -59,8 +61,6 @@ export class SearchFacetChipComponent { focusTrap: ConfigurableFocusTrap; chipIcon = 'keyboard_arrow_down'; - constructor(private focusTrapFactory: ConfigurableFocusTrapFactory) {} - onMenuOpen() { if (this.menuContainer && !this.focusTrap) { this.focusTrap = this.focusTrapFactory.create(this.menuContainer.nativeElement); diff --git a/lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.ts b/lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.ts index 8389f2e1c6..8b21a325ee 100644 --- a/lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.ts +++ b/lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.ts @@ -35,8 +35,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; encapsulation: ViewEncapsulation.None }) export class SearchFilterChipsComponent implements OnInit { - private queryBuilder = inject(SearchQueryBuilderService); - private facetFiltersService = inject(SearchFacetFiltersService); + private readonly queryBuilder = inject(SearchQueryBuilderService); + private readonly facetFiltersService = inject(SearchFacetFiltersService); private readonly destroyRef = inject(DestroyRef); diff --git a/lib/content-services/src/lib/search/components/search-filter-chips/search-widget-chip/search-widget-chip.component.ts b/lib/content-services/src/lib/search/components/search-filter-chips/search-widget-chip/search-widget-chip.component.ts index 6e8166e125..14a3fd7fd7 100644 --- a/lib/content-services/src/lib/search/components/search-filter-chips/search-widget-chip/search-widget-chip.component.ts +++ b/lib/content-services/src/lib/search/components/search-filter-chips/search-widget-chip/search-widget-chip.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, ViewChild, ViewEncapsulation } from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { SearchCategory } from '../../../models/search-category.interface'; import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y'; import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu'; @@ -51,6 +51,9 @@ import { IconModule } from '@alfresco/adf-core'; encapsulation: ViewEncapsulation.None }) export class SearchWidgetChipComponent implements AfterViewInit { + private readonly cd = inject(ChangeDetectorRef); + private readonly focusTrapFactory = inject(ConfigurableFocusTrapFactory); + @Input({ required: true }) category: SearchCategory; @@ -69,11 +72,6 @@ export class SearchWidgetChipComponent implements AfterViewInit { focusTrap: ConfigurableFocusTrap; chipIcon = 'keyboard_arrow_down'; - constructor( - private readonly cd: ChangeDetectorRef, - private readonly focusTrapFactory: ConfigurableFocusTrapFactory - ) {} - ngAfterViewInit(): void { this.widgetContainerComponent ?.getDisplayValue() diff --git a/lib/content-services/src/lib/search/components/search-filter-container/search-filter-container.component.ts b/lib/content-services/src/lib/search/components/search-filter-container/search-filter-container.component.ts index e26e5764ae..76ea8313cc 100644 --- a/lib/content-services/src/lib/search/components/search-filter-container/search-filter-container.component.ts +++ b/lib/content-services/src/lib/search/components/search-filter-container/search-filter-container.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y'; import { DataColumn, TranslationService } from '@alfresco/adf-core'; import { SearchWidgetContainerComponent } from '../search-widget-container/search-widget-container.component'; @@ -32,20 +32,18 @@ import { MatIconModule, MatIconRegistry } from '@angular/material/icon'; @Component({ selector: 'adf-search-filter-container', - imports: [ - CommonModule, - MatButtonModule, - MatMenuModule, - MatIconModule, - SearchWidgetContainerComponent, - TranslatePipe, - MatDialogModule - ], + imports: [CommonModule, MatButtonModule, MatMenuModule, MatIconModule, SearchWidgetContainerComponent, TranslatePipe, MatDialogModule], templateUrl: './search-filter-container.component.html', styleUrls: ['./search-filter-container.component.scss'], encapsulation: ViewEncapsulation.None }) export class SearchFilterContainerComponent implements OnInit { + private readonly searchFilterQueryBuilder = inject(SearchHeaderQueryBuilderService); + private readonly translationService = inject(TranslationService); + private readonly focusTrapFactory = inject(ConfigurableFocusTrapFactory); + private readonly matIconRegistry = inject(MatIconRegistry); + private readonly sanitizer = inject(DomSanitizer); + /** The column the filter will be applied on. */ @Input({ required: true }) col: DataColumn; @@ -68,14 +66,6 @@ export class SearchFilterContainerComponent implements OnInit { focusTrap: ConfigurableFocusTrap; initialValue: any; - constructor( - private readonly searchFilterQueryBuilder: SearchHeaderQueryBuilderService, - private readonly translationService: TranslationService, - private readonly focusTrapFactory: ConfigurableFocusTrapFactory, - private readonly matIconRegistry: MatIconRegistry, - private readonly sanitizer: DomSanitizer - ) {} - ngOnInit() { this.registerFilterIcon(); this.category = this.searchFilterQueryBuilder.getCategoryForColumn(this.col.key); diff --git a/lib/content-services/src/lib/search/components/search-filter-tabbed/search-filter-tab.directive.ts b/lib/content-services/src/lib/search/components/search-filter-tabbed/search-filter-tab.directive.ts index 1bd4446090..44e1d774dc 100644 --- a/lib/content-services/src/lib/search/components/search-filter-tabbed/search-filter-tab.directive.ts +++ b/lib/content-services/src/lib/search/components/search-filter-tabbed/search-filter-tab.directive.ts @@ -15,14 +15,14 @@ * limitations under the License. */ -import { Directive, Input, TemplateRef } from '@angular/core'; +import { Directive, Input, TemplateRef, inject } from '@angular/core'; @Directive({ selector: '[adf-search-filter-tab]' }) export class SearchFilterTabDirective { + readonly templateRef = inject<TemplateRef<any>>(TemplateRef); + @Input('adf-search-filter-tab') name: string; - - constructor(public readonly templateRef: TemplateRef<any>) {} } diff --git a/lib/content-services/src/lib/search/components/search-filter-tabbed/search-filter-tabbed.component.ts b/lib/content-services/src/lib/search/components/search-filter-tabbed/search-filter-tabbed.component.ts index 42c2d56002..51860d4d03 100644 --- a/lib/content-services/src/lib/search/components/search-filter-tabbed/search-filter-tabbed.component.ts +++ b/lib/content-services/src/lib/search/components/search-filter-tabbed/search-filter-tabbed.component.ts @@ -15,7 +15,18 @@ * limitations under the License. */ -import { ChangeDetectorRef, Component, ContentChildren, ElementRef, OnDestroy, OnInit, QueryList, ViewChild, ViewEncapsulation } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + ContentChildren, + ElementRef, + OnDestroy, + OnInit, + QueryList, + ViewChild, + ViewEncapsulation, + inject +} from '@angular/core'; import { SearchFilterTabDirective } from './search-filter-tab.directive'; import { CommonModule } from '@angular/common'; import { MatTabGroup, MatTabsModule } from '@angular/material/tabs'; @@ -29,6 +40,9 @@ import { TranslatePipe } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None }) export class SearchFilterTabbedComponent implements OnInit, OnDestroy { + private readonly element = inject(ElementRef); + private readonly changeDetector = inject(ChangeDetectorRef); + @ContentChildren(SearchFilterTabDirective) tabsContents: QueryList<SearchFilterTabDirective>; @@ -51,8 +65,6 @@ export class SearchFilterTabbedComponent implements OnInit, OnDestroy { } ); - constructor(private readonly element: ElementRef, private readonly changeDetector: ChangeDetectorRef) {} - ngOnInit(): void { this.intersectionObserver.observe(this.element.nativeElement); } diff --git a/lib/content-services/src/lib/search/components/search-filter/search-filter.component.ts b/lib/content-services/src/lib/search/components/search-filter/search-filter.component.ts index e950c8d159..6cfaa7ad42 100644 --- a/lib/content-services/src/lib/search/components/search-filter/search-filter.component.ts +++ b/lib/content-services/src/lib/search/components/search-filter/search-filter.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Input, ViewEncapsulation } from '@angular/core'; +import { Component, Input, ViewEncapsulation, inject } from '@angular/core'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; import { FacetFieldBucket } from '../../models/facet-field-bucket.interface'; import { FacetField } from '../../models/facet-field.interface'; @@ -47,6 +47,9 @@ import { SearchFacetTabbedContentComponent } from '../search-filter-chips'; host: { class: 'adf-search-filter' } }) export class SearchFilterComponent { + queryBuilder = inject(SearchQueryBuilderService); + facetFiltersService = inject(SearchFacetFiltersService); + /** Toggles whether to show or not the context facet filters. */ @Input() showContextFacets: boolean = true; @@ -57,7 +60,9 @@ export class SearchFilterComponent { }; displayResetButton: boolean; - constructor(public queryBuilder: SearchQueryBuilderService, public facetFiltersService: SearchFacetFiltersService) { + constructor() { + const queryBuilder = this.queryBuilder; + if (queryBuilder.config?.facetQueries) { this.facetQueriesLabel = queryBuilder.config.facetQueries.label || 'Facet Queries'; this.facetExpanded['query'] = queryBuilder.config.facetQueries.expanded; diff --git a/lib/content-services/src/lib/search/components/search-form/search-form.component.ts b/lib/content-services/src/lib/search/components/search-form/search-form.component.ts index 1313fe22bc..48827e3819 100644 --- a/lib/content-services/src/lib/search/components/search-form/search-form.component.ts +++ b/lib/content-services/src/lib/search/components/search-form/search-form.component.ts @@ -32,7 +32,7 @@ import { IconModule } from '@alfresco/adf-core'; encapsulation: ViewEncapsulation.None }) export class SearchFormComponent { - private queryBuilder = inject(SearchQueryBuilderService); + private readonly queryBuilder = inject(SearchQueryBuilderService); searchForms$ = this.queryBuilder.searchForms; diff --git a/lib/content-services/src/lib/search/components/search-input/search-input.component.ts b/lib/content-services/src/lib/search/components/search-input/search-input.component.ts index 9b4d39265b..8322700f4e 100644 --- a/lib/content-services/src/lib/search/components/search-input/search-input.component.ts +++ b/lib/content-services/src/lib/search/components/search-input/search-input.component.ts @@ -17,7 +17,7 @@ import { AppConfigService } from '@alfresco/adf-core'; import { CommonModule } from '@angular/common'; -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, inject } from '@angular/core'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { TranslatePipe } from '@ngx-translate/core'; @@ -31,6 +31,8 @@ import { SearchConfiguration } from '../../models'; encapsulation: ViewEncapsulation.None }) export class SearchInputComponent implements OnInit { + private readonly appConfig = inject(AppConfigService); + @Input() value = ''; @@ -46,8 +48,6 @@ export class SearchInputComponent implements OnInit { @Output() changed = new EventEmitter<string>(); - constructor(private appConfig: AppConfigService) {} - ngOnInit(): void { const searchConfig = this.appConfig.get<SearchConfiguration>('search') || {}; if (searchConfig['app:fields']) { diff --git a/lib/content-services/src/lib/search/components/search-logical-filter/search-logical-filter.component.ts b/lib/content-services/src/lib/search/components/search-logical-filter/search-logical-filter.component.ts index a79d49a0c5..2310f58638 100644 --- a/lib/content-services/src/lib/search/components/search-logical-filter/search-logical-filter.component.ts +++ b/lib/content-services/src/lib/search/components/search-logical-filter/search-logical-filter.component.ts @@ -49,6 +49,8 @@ export interface LogicalSearchCondition extends LogicalSearchConditionEnumValued encapsulation: ViewEncapsulation.None }) export class SearchLogicalFilterComponent implements SearchWidget, OnInit { + private readonly translationService = inject(TranslationService); + id: string; settings?: SearchWidgetSettings; context?: SearchQueryBuilderService; @@ -60,8 +62,6 @@ export class SearchLogicalFilterComponent implements SearchWidget, OnInit { private readonly destroyRef = inject(DestroyRef); - constructor(private translationService: TranslationService) {} - ngOnInit(): void { this.clearSearchInputs(); this.context.populateFilters diff --git a/lib/content-services/src/lib/search/components/search-panel/search-panel.component.ts b/lib/content-services/src/lib/search/components/search-panel/search-panel.component.ts index 412c952e4f..f8599ba979 100644 --- a/lib/content-services/src/lib/search/components/search-panel/search-panel.component.ts +++ b/lib/content-services/src/lib/search/components/search-panel/search-panel.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { ContentNodeSelectorPanelService } from '../../../content-node-selector/content-node-selector-panel/content-node-selector-panel.service'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; import { CommonModule } from '@angular/common'; @@ -30,7 +30,8 @@ import { SearchFilterComponent } from '../search-filter'; host: { class: 'adf-search-panel' } }) export class SearchPanelComponent implements OnInit { - constructor(private contentNodeSelectorPanelService: ContentNodeSelectorPanelService, private queryBuilderService: SearchQueryBuilderService) {} + private readonly contentNodeSelectorPanelService = inject(ContentNodeSelectorPanelService); + private readonly queryBuilderService = inject(SearchQueryBuilderService); ngOnInit(): void { this.queryBuilderService.categories = this.contentNodeSelectorPanelService.convertCustomModelPropertiesToSearchCategories(); diff --git a/lib/content-services/src/lib/search/components/search-properties/search-properties.component.ts b/lib/content-services/src/lib/search/components/search-properties/search-properties.component.ts index c541d1a02c..f76455b37c 100644 --- a/lib/content-services/src/lib/search/components/search-properties/search-properties.component.ts +++ b/lib/content-services/src/lib/search/components/search-properties/search-properties.component.ts @@ -51,6 +51,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; encapsulation: ViewEncapsulation.None }) export class SearchPropertiesComponent implements OnInit, AfterViewChecked, SearchWidget { + private readonly translateService = inject(TranslateService); + id: string; settings?: SearchWidgetSettings; context?: SearchQueryBuilderService; @@ -61,17 +63,17 @@ export class SearchPropertiesComponent implements OnInit, AfterViewChecked, Sear private readonly formBuilder = inject(FormBuilder); - private _form = this.formBuilder.nonNullable.group<FileSizeCondition>({ + private readonly _form = this.formBuilder.nonNullable.group<FileSizeCondition>({ fileSizeOperator: FileSizeOperator.AT_LEAST, fileSize: undefined, fileSizeUnit: FileSizeUnit.KB }); - private _fileSizeOperators = Object.keys(FileSizeOperator).map<string>((key) => FileSizeOperator[key]); - private _fileSizeUnits = [FileSizeUnit.KB, FileSizeUnit.MB, FileSizeUnit.GB]; - private canvas = document.createElement('canvas'); + private readonly _fileSizeOperators = Object.keys(FileSizeOperator).map<string>((key) => FileSizeOperator[key]); + private readonly _fileSizeUnits = [FileSizeUnit.KB, FileSizeUnit.MB, FileSizeUnit.GB]; + private readonly canvas = document.createElement('canvas'); private _fileSizeOperatorsMaxWidth: number; private _selectedExtensions: string[]; - private _reset$ = new Subject<void>(); + private readonly _reset$ = new Subject<void>(); private sizeField: string; private nameField: string; @@ -108,8 +110,6 @@ export class SearchPropertiesComponent implements OnInit, AfterViewChecked, Sear private readonly destroyRef = inject(DestroyRef); - constructor(private readonly translateService: TranslateService) {} - ngOnInit() { if (this.settings) { if (!this.settings.fileExtensions) { diff --git a/lib/content-services/src/lib/search/components/search-widget-container/search-widget-container.component.ts b/lib/content-services/src/lib/search/components/search-widget-container/search-widget-container.component.ts index 296e9ded17..c432dcf65f 100644 --- a/lib/content-services/src/lib/search/components/search-widget-container/search-widget-container.component.ts +++ b/lib/content-services/src/lib/search/components/search-widget-container/search-widget-container.component.ts @@ -15,7 +15,19 @@ * limitations under the License. */ -import { Component, Input, ViewChild, ViewContainerRef, OnInit, OnDestroy, ComponentRef, SimpleChanges, OnChanges, Injector } from '@angular/core'; +import { + Component, + Input, + ViewChild, + ViewContainerRef, + OnInit, + OnDestroy, + ComponentRef, + SimpleChanges, + OnChanges, + Injector, + inject +} from '@angular/core'; import { SearchFilterService } from '../../services/search-filter.service'; import { Observable } from 'rxjs'; import { SearchHeaderQueryBuilderService, SearchQueryBuilderService } from '../../services'; @@ -25,6 +37,9 @@ import { SearchHeaderQueryBuilderService, SearchQueryBuilderService } from '../. template: '<div #content></div>' }) export class SearchWidgetContainerComponent implements OnInit, OnDestroy, OnChanges { + private readonly searchFilterService = inject(SearchFilterService); + private readonly injector = inject(Injector); + @ViewChild('content', { read: ViewContainerRef, static: true }) content: ViewContainerRef; @@ -48,8 +63,6 @@ export class SearchWidgetContainerComponent implements OnInit, OnDestroy, OnChan componentRef: ComponentRef<any>; - constructor(private searchFilterService: SearchFilterService, private injector: Injector) {} - ngOnInit() { const componentType = this.searchFilterService.widgets[this.selector]; if (componentType) { diff --git a/lib/content-services/src/lib/search/components/search.component.ts b/lib/content-services/src/lib/search/components/search.component.ts index c1930008ae..b0d26b9006 100644 --- a/lib/content-services/src/lib/search/components/search.component.ts +++ b/lib/content-services/src/lib/search/components/search.component.ts @@ -28,7 +28,8 @@ import { SimpleChanges, TemplateRef, ViewChild, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { NodePaging, ResultSetPaging } from '@alfresco/js-api'; import { Subject } from 'rxjs'; @@ -49,6 +50,9 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; host: { class: 'adf-search' } }) export class SearchComponent implements SearchComponentInterface, AfterContentInit, OnChanges { + private readonly searchService = inject(SearchService); + private readonly _elementRef = inject(ElementRef); + @ViewChild('panel', { static: true }) panel: ElementRef; @@ -106,7 +110,9 @@ export class SearchComponent implements SearchComponentInterface, AfterContentIn _isOpen: boolean = false; keyPressedStream = new Subject<string>(); _classList: { [key: string]: boolean } = {}; - constructor(private searchService: SearchService, private _elementRef: ElementRef) { + constructor() { + const searchService = this.searchService; + this.keyPressedStream.pipe(debounceTime(200), takeUntilDestroyed()).subscribe((searchedWord) => { this.loadSearchResults(searchedWord); }); diff --git a/lib/content-services/src/lib/search/services/search-facet-filters.service.ts b/lib/content-services/src/lib/search/services/search-facet-filters.service.ts index 9e8a4741ae..04e3ada95a 100644 --- a/lib/content-services/src/lib/search/services/search-facet-filters.service.ts +++ b/lib/content-services/src/lib/search/services/search-facet-filters.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { FacetBucketSortBy, FacetBucketSortDirection, FacetField } from '../models/facet-field.interface'; import { throwError } from 'rxjs'; import { SearchQueryBuilderService } from './search-query-builder.service'; @@ -40,6 +40,11 @@ const DEFAULT_PAGE_SIZE: number = 5; providedIn: 'root' }) export class SearchFacetFiltersService { + private readonly queryBuilder = inject(SearchQueryBuilderService); + private readonly searchService = inject(SearchService); + private readonly translationService = inject(TranslationService); + private readonly categoryService = inject(CategoryService); + /** * All facet field items to be displayed in the component. These are updated according to the response. * When a new search is performed, the already existing items are updated with the new bucket count values and @@ -53,12 +58,9 @@ export class SearchFacetFiltersService { selectedBuckets: SelectedBucket[] = []; private readonly facetQueriesPageSize = DEFAULT_PAGE_SIZE; - constructor( - private queryBuilder: SearchQueryBuilderService, - private searchService: SearchService, - private translationService: TranslationService, - private categoryService: CategoryService - ) { + constructor() { + const queryBuilder = this.queryBuilder; + if (queryBuilder.config?.facetQueries) { this.facetQueriesPageSize = queryBuilder.config.facetQueries.pageSize || DEFAULT_PAGE_SIZE; } @@ -281,7 +283,7 @@ export class SearchFacetFiltersService { return bucket.count === null ? '' : `(${bucket.count})`; } - private getFilterByMinCount = + private readonly getFilterByMinCount = (minCountInput: number) => (bucket: FacetFieldBucket): boolean => { let minCount = minCountInput; @@ -363,7 +365,7 @@ export class SearchFacetFiltersService { }); } - private getBucketFilterFunction = + private readonly getBucketFilterFunction = (bucketList: SearchFilterList<FacetFieldBucket>) => (bucket: FacetFieldBucket): boolean => { if (bucket && bucketList.filterText) { diff --git a/lib/content-services/src/lib/search/services/search-header-query-builder.service.spec.ts b/lib/content-services/src/lib/search/services/search-header-query-builder.service.spec.ts index 85e0531822..8b2032679e 100644 --- a/lib/content-services/src/lib/search/services/search-header-query-builder.service.spec.ts +++ b/lib/content-services/src/lib/search/services/search-header-query-builder.service.spec.ts @@ -19,7 +19,6 @@ import { SearchConfiguration } from '../models/search-configuration.interface'; import { AppConfigService } from '@alfresco/adf-core'; import { SearchHeaderQueryBuilderService } from './search-header-query-builder.service'; import { TestBed } from '@angular/core/testing'; -import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { ActivatedRoute, provideRouter, Router } from '@angular/router'; import { SearchCategory } from '../models'; @@ -54,8 +53,8 @@ describe('SearchHeaderQueryBuilderService', () => { filterQueries: [{ query: 'query1' }, { query: 'query2' }] } as SearchConfiguration; - const alfrescoApiService = TestBed.inject(AlfrescoApiService); - const builder = new SearchHeaderQueryBuilderService(buildConfig(config), alfrescoApiService, null); + buildConfig(config); + const builder = TestBed.inject(SearchHeaderQueryBuilderService); builder.categories = []; builder.filterQueries = []; @@ -71,50 +70,72 @@ describe('SearchHeaderQueryBuilderService', () => { }); it('should return the category assigned to a column key', () => { - TestBed.runInInjectionContext(() => { - const config: SearchConfiguration = { - id: 'test-config', - categories: [ - { id: 'cat1', columnKey: 'fake-key-1', enabled: true }, - { id: 'cat2', columnKey: 'fake-key-2', enabled: true } - ], - filterQueries: [{ query: 'query1' }, { query: 'query2' }] - } as SearchConfiguration; + const config: SearchConfiguration = { + id: 'test-config', + categories: [ + { id: 'cat1', columnKey: 'fake-key-1', enabled: true }, + { id: 'cat2', columnKey: 'fake-key-2', enabled: true } + ], + filterQueries: [{ query: 'query1' }, { query: 'query2' }] + } as SearchConfiguration; - const alfrescoApiService = TestBed.inject(AlfrescoApiService); - const service = new SearchHeaderQueryBuilderService(buildConfig(config), alfrescoApiService, null); - - const category = service.getCategoryForColumn('fake-key-1'); - expect(category).not.toBeNull(); - expect(category).not.toBeUndefined(); - expect(category.columnKey).toBe('fake-key-1'); + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + providers: [ + provideRouter([]), + { + provide: AppConfigService, + useFactory: () => { + const appConfig = new AppConfigService(); + appConfig.config['search-headers'] = config; + return appConfig; + } + } + ] }); + + const service = TestBed.inject(SearchHeaderQueryBuilderService); + const category = service.getCategoryForColumn('fake-key-1'); + expect(category).not.toBeNull(); + expect(category).not.toBeUndefined(); + expect(category.columnKey).toBe('fake-key-1'); }); it('should return operator for a category by id', () => { - TestBed.runInInjectionContext(() => { - const config: SearchConfiguration = { - id: 'test-config', - categories: [ - { id: 'cat1', columnKey: 'fake-key-1', enabled: true, component: { settings: { operator: 'operator' } } }, - { id: 'cat2', columnKey: 'fake-key-2', enabled: true } - ] as SearchCategory[] - }; + const config: SearchConfiguration = { + id: 'test-config', + categories: [ + { id: 'cat1', columnKey: 'fake-key-1', enabled: true, component: { settings: { operator: 'operator' } } }, + { id: 'cat2', columnKey: 'fake-key-2', enabled: true } + ] as SearchCategory[] + }; - const alfrescoApiService = TestBed.inject(AlfrescoApiService); - const service = new SearchHeaderQueryBuilderService(buildConfig(config), alfrescoApiService, null); - - const operator = service.getOperatorForFilterId('cat1'); - expect(operator).toBe('operator'); - const operator1 = service.getOperatorForFilterId('cat2'); - expect(operator1).toBeUndefined(); + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + providers: [ + provideRouter([]), + { + provide: AppConfigService, + useFactory: () => { + const appConfig = new AppConfigService(); + appConfig.config['search-headers'] = config; + return appConfig; + } + } + ] }); + + const service = TestBed.inject(SearchHeaderQueryBuilderService); + const operator = service.getOperatorForFilterId('cat1'); + expect(operator).toBe('operator'); + const operator1 = service.getOperatorForFilterId('cat2'); + expect(operator1).toBeUndefined(); }); it('should have empty user query by default', () => { TestBed.runInInjectionContext(() => { - const alfrescoApiService = TestBed.inject(AlfrescoApiService); - const builder = new SearchHeaderQueryBuilderService(buildConfig({}), alfrescoApiService, null); + buildConfig({}); + const builder = TestBed.inject(SearchHeaderQueryBuilderService); expect(builder.userQuery).toBe(''); }); }); @@ -132,8 +153,8 @@ describe('SearchHeaderQueryBuilderService', () => { const expectedResult = [{ query: 'PARENT:"workspace://SpacesStore/fake-node-id"' }]; - const alfrescoApiService = TestBed.inject(AlfrescoApiService); - const searchHeaderService = new SearchHeaderQueryBuilderService(buildConfig(config), alfrescoApiService, null); + buildConfig(config); + const searchHeaderService = TestBed.inject(SearchHeaderQueryBuilderService); searchHeaderService.setCurrentRootFolderId('fake-node-id'); @@ -154,8 +175,8 @@ describe('SearchHeaderQueryBuilderService', () => { filterQueries: expectedResult } as SearchConfiguration; - const alfrescoApiService = TestBed.inject(AlfrescoApiService); - const searchHeaderService = new SearchHeaderQueryBuilderService(buildConfig(config), alfrescoApiService, null); + buildConfig(config); + const searchHeaderService = TestBed.inject(SearchHeaderQueryBuilderService); searchHeaderService.setCurrentRootFolderId('fake-node-id'); @@ -173,8 +194,8 @@ describe('SearchHeaderQueryBuilderService', () => { filterQueries: [{ query: 'PARENT:"workspace://SpacesStore/fake-node-id' }] } as SearchConfiguration; - const alfrescoApiService = TestBed.inject(AlfrescoApiService); - const searchHeaderService = new SearchHeaderQueryBuilderService(buildConfig(config), alfrescoApiService, null); + buildConfig(config); + const searchHeaderService = TestBed.inject(SearchHeaderQueryBuilderService); expect(searchHeaderService.activeFilters.length).toBe(0); diff --git a/lib/content-services/src/lib/search/services/search-header-query-builder.service.ts b/lib/content-services/src/lib/search/services/search-header-query-builder.service.ts index e8392f40da..7ba51c6a4a 100644 --- a/lib/content-services/src/lib/search/services/search-header-query-builder.service.ts +++ b/lib/content-services/src/lib/search/services/search-header-query-builder.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AppConfigService, DataSorting } from '@alfresco/adf-core'; import { SearchConfiguration } from '../models/search-configuration.interface'; import { BaseQueryBuilderService } from './base-query-builder.service'; @@ -32,15 +32,16 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; providedIn: 'root' }) export class SearchHeaderQueryBuilderService extends BaseQueryBuilderService { - private customSources = ['-trashcan-', '-sharedlinks-', '-sites-', '-mysites-', '-favorites-', '-recent-', '-my-']; + private readonly nodeApiService = inject(NodesApiService); + + private readonly customSources = ['-trashcan-', '-sharedlinks-', '-sites-', '-mysites-', '-favorites-', '-recent-', '-my-']; activeFilters: FilterSearch[] = []; - constructor( - appConfig: AppConfigService, - alfrescoApiService: AlfrescoApiService, - private readonly nodeApiService: NodesApiService - ) { + constructor() { + const appConfig = inject(AppConfigService); + const alfrescoApiService = inject(AlfrescoApiService); + super(appConfig, alfrescoApiService); this.updated.pipe(filter((query) => !!query)).subscribe(() => { diff --git a/lib/content-services/src/lib/search/services/search-query-builder.service.spec.ts b/lib/content-services/src/lib/search/services/search-query-builder.service.spec.ts index a1085a74b8..b0bb87d341 100644 --- a/lib/content-services/src/lib/search/services/search-query-builder.service.spec.ts +++ b/lib/content-services/src/lib/search/services/search-query-builder.service.spec.ts @@ -18,20 +18,15 @@ import { SearchQueryBuilderService } from './search-query-builder.service'; import { SearchConfiguration } from '../models/search-configuration.interface'; import { AppConfigService } from '@alfresco/adf-core'; -import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { FacetField } from '../models/facet-field.interface'; import { TestBed } from '@angular/core/testing'; import { ADF_SEARCH_CONFIGURATION } from '../search-configuration.token'; import { ActivatedRoute, provideRouter, Router } from '@angular/router'; import { skip } from 'rxjs/operators'; -const buildConfig = (searchSettings = {}): AppConfigService => { - let config: AppConfigService; - TestBed.runInInjectionContext(() => { - config = TestBed.inject(AppConfigService); - }); +const buildConfig = (searchSettings = {}): void => { + const config = TestBed.inject(AppConfigService); config.config.search = searchSettings; - return config; }; describe('SearchQueryBuilder (runtime config)', () => { @@ -60,11 +55,10 @@ describe('SearchQueryBuilder (runtime config)', () => { filterQueries: [{ query: 'query1' }, { query: 'query2' }] }; - let alfrescoApiService: AlfrescoApiService; let builder: SearchQueryBuilderService; TestBed.runInInjectionContext(() => { - alfrescoApiService = TestBed.inject(AlfrescoApiService); - builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService, runtimeConfig); + buildConfig(config); + builder = TestBed.inject(SearchQueryBuilderService); }); const currentConfig = builder.loadConfiguration(); @@ -87,12 +81,23 @@ describe('SearchQueryBuilder', () => { }); const createQueryBuilder = (config?: any) => { - let builder: SearchQueryBuilderService; - TestBed.runInInjectionContext(() => { - const alfrescoApiService = TestBed.inject(AlfrescoApiService); - builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService); + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + providers: [ + provideRouter([]), + { + provide: AppConfigService, + useFactory: () => { + const appConfig = new AppConfigService(); + if (config !== undefined) { + appConfig.config.search = config; + } + return appConfig; + } + } + ] }); - return builder; + return TestBed.inject(SearchQueryBuilderService); }; it('should reset to defaults', () => { diff --git a/lib/content-services/src/lib/search/services/search-query-builder.service.ts b/lib/content-services/src/lib/search/services/search-query-builder.service.ts index 42c6f46173..720281b6e0 100644 --- a/lib/content-services/src/lib/search/services/search-query-builder.service.ts +++ b/lib/content-services/src/lib/search/services/search-query-builder.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Inject, Injectable, Optional } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AppConfigService } from '@alfresco/adf-core'; import { SearchConfiguration } from '../models/search-configuration.interface'; import { BaseQueryBuilderService } from './base-query-builder.service'; @@ -24,15 +24,16 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; @Injectable({ providedIn: 'root' }) export class SearchQueryBuilderService extends BaseQueryBuilderService { + private readonly configuration = inject<SearchConfiguration>(ADF_SEARCH_CONFIGURATION, { optional: true }); + public isFilterServiceActive(): boolean { return false; } - constructor( - appConfig: AppConfigService, - alfrescoApiService: AlfrescoApiService, - @Optional() @Inject(ADF_SEARCH_CONFIGURATION) private configuration?: SearchConfiguration - ) { + constructor() { + const appConfig = inject(AppConfigService); + const alfrescoApiService = inject(AlfrescoApiService); + super(appConfig, alfrescoApiService); } diff --git a/lib/content-services/src/lib/search/services/search.service.spec.ts b/lib/content-services/src/lib/search/services/search.service.spec.ts index df0a99a9f3..9f9f27c8cd 100644 --- a/lib/content-services/src/lib/search/services/search.service.spec.ts +++ b/lib/content-services/src/lib/search/services/search.service.spec.ts @@ -19,15 +19,15 @@ import { TestBed } from '@angular/core/testing'; import { mockError, fakeSearch } from '../mocks/search.service.mock'; import { SearchService } from './search.service'; import { NodePaging } from '@alfresco/js-api'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; describe('SearchService', () => { let service: SearchService; beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [SearchService] + providers: [SearchService, provideHttpClient(), provideHttpClientTesting()] }); service = TestBed.inject(SearchService); }); diff --git a/lib/content-services/src/lib/search/services/search.service.ts b/lib/content-services/src/lib/search/services/search.service.ts index 5f2cbe2a77..78bb6e1801 100644 --- a/lib/content-services/src/lib/search/services/search.service.ts +++ b/lib/content-services/src/lib/search/services/search.service.ts @@ -25,8 +25,8 @@ import { SearchConfigurationService } from './search-configuration.service'; providedIn: 'root' }) export class SearchService { - private apiService = inject(AlfrescoApiService); - private searchConfigurationService = inject(SearchConfigurationService); + private readonly apiService = inject(AlfrescoApiService); + private readonly searchConfigurationService = inject(SearchConfigurationService); dataLoaded = new Subject<ResultSetPaging>(); diff --git a/lib/content-services/src/lib/security-options-loader/security-options-loader.service.spec.ts b/lib/content-services/src/lib/security-options-loader/security-options-loader.service.spec.ts index 48db0ac47c..a59761b720 100644 --- a/lib/content-services/src/lib/security-options-loader/security-options-loader.service.spec.ts +++ b/lib/content-services/src/lib/security-options-loader/security-options-loader.service.spec.ts @@ -18,6 +18,7 @@ import { SecurityOptionsLoaderService } from './security-options-loader.service'; import { AppConfigService, AppConfigValues } from '@alfresco/adf-core'; import { AdfHttpClient } from '@alfresco/adf-core/api'; +import { TestBed } from '@angular/core/testing'; describe('SecurityOptionsLoaderService', () => { let service: SecurityOptionsLoaderService; @@ -28,7 +29,13 @@ describe('SecurityOptionsLoaderService', () => { appConfigServiceSpy = jasmine.createSpyObj('AppConfigService', ['get']); adfHttpClientSpy = jasmine.createSpyObj('AdfHttpClient', ['setDefaultSecurityOption']); - service = new SecurityOptionsLoaderService(appConfigServiceSpy, adfHttpClientSpy); + TestBed.configureTestingModule({ + providers: [ + { provide: AppConfigService, useValue: appConfigServiceSpy }, + { provide: AdfHttpClient, useValue: adfHttpClientSpy } + ] + }); + service = TestBed.inject(SecurityOptionsLoaderService); }); it('should set withCredentials when value is true', () => { diff --git a/lib/content-services/src/lib/security-options-loader/security-options-loader.service.ts b/lib/content-services/src/lib/security-options-loader/security-options-loader.service.ts index e488ed1df0..ccce7fd5cd 100644 --- a/lib/content-services/src/lib/security-options-loader/security-options-loader.service.ts +++ b/lib/content-services/src/lib/security-options-loader/security-options-loader.service.ts @@ -17,13 +17,14 @@ import { AppConfigService, AppConfigValues } from '@alfresco/adf-core'; import { AdfHttpClient } from '@alfresco/adf-core/api'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class SecurityOptionsLoaderService { - constructor(private appConfigService: AppConfigService, private adfHttpClient: AdfHttpClient) {} + private readonly appConfigService = inject(AppConfigService); + private readonly adfHttpClient = inject(AdfHttpClient); load = () => { const withCredentials = this.appConfigService.get<boolean>(AppConfigValues.AUTH_WITH_CREDENTIALS); diff --git a/lib/content-services/src/lib/security/services/security-controls-groups-marks-security.service.ts b/lib/content-services/src/lib/security/services/security-controls-groups-marks-security.service.ts index c2f16e58f9..0d1e693289 100644 --- a/lib/content-services/src/lib/security/services/security-controls-groups-marks-security.service.ts +++ b/lib/content-services/src/lib/security/services/security-controls-groups-marks-security.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { BehaviorSubject, from, Observable, Subject } from 'rxjs'; import { SecurityControlsGroupResponse } from './models/security-controls-group-response.interface'; import { SecurityControlsMarkResponse } from './models/security-controls-mark-response.interface'; @@ -41,25 +41,27 @@ const DEFAULT_INCLUDE = 'inUse'; @Injectable({ providedIn: 'root' }) export class SecurityControlsService { - private groupsPaginatedSource = new Subject<SecurityControlsGroupResponse>(); + private readonly apiService = inject(AlfrescoApiService); + private readonly userPreferencesService = inject(UserPreferencesService); + + private readonly groupsPaginatedSource = new Subject<SecurityControlsGroupResponse>(); groupsPaginated$ = this.groupsPaginatedSource.asObservable(); - private reloadSecurityControls: Subject<void> = new Subject<void>(); + private readonly reloadSecurityControls: Subject<void> = new Subject<void>(); reloadSecurityControls$ = this.reloadSecurityControls.asObservable(); - private _reloadAuthorityClearance: Subject<void> = new Subject<void>(); + private readonly _reloadAuthorityClearance: Subject<void> = new Subject<void>(); reloadAuthorityClearance$ = this._reloadAuthorityClearance.asObservable(); - private marksPaginatedSource = new Subject<SecurityControlsMarkResponse>(); + private readonly marksPaginatedSource = new Subject<SecurityControlsMarkResponse>(); marksPaginated$ = this.marksPaginatedSource.asObservable(); - private loadingSource = new BehaviorSubject<boolean>(true); + private readonly loadingSource = new BehaviorSubject<boolean>(true); loading$ = this.loadingSource.asObservable(); private securityGroup: SecurityGroupsApi; private securityMark: SecurityMarksApi; private authorityClearance: AuthorityClearanceApi; - constructor(private apiService: AlfrescoApiService, private userPreferencesService: UserPreferencesService) {} get groupsApi(): SecurityGroupsApi { return this.securityGroup || (this.securityGroup = new SecurityGroupsApi(this.apiService.getInstance())); diff --git a/lib/content-services/src/lib/services/alfresco-api.service.ts b/lib/content-services/src/lib/services/alfresco-api.service.ts index 65f6d1cb7f..f9d37bd7b4 100644 --- a/lib/content-services/src/lib/services/alfresco-api.service.ts +++ b/lib/content-services/src/lib/services/alfresco-api.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Inject, Injectable, InjectionToken, Optional } from '@angular/core'; +import { Injectable, InjectionToken, inject } from '@angular/core'; import { AlfrescoApi, AlfrescoApiConfig } from '@alfresco/js-api'; import { ReplaySubject } from 'rxjs'; import { AlfrescoApiFactory } from './alfresco-api.interface'; @@ -27,6 +27,10 @@ export const ALFRESCO_API_FACTORY = new InjectionToken('ALFRESCO_API_FACTORY'); providedIn: 'root' }) export class AlfrescoApiService { + protected appConfig = inject(AppConfigService); + protected storageService = inject(StorageService); + private readonly alfrescoApiFactory = inject<AlfrescoApiFactory>(ALFRESCO_API_FACTORY, { optional: true }); + alfrescoApiInitialized = new ReplaySubject<boolean>(1); protected alfrescoApi: AlfrescoApi; @@ -36,20 +40,12 @@ export class AlfrescoApiService { idpConfig: OpenidConfiguration; - private excludedErrorUrl: string[] = ['api/enterprise/system/properties']; + private readonly excludedErrorUrl: string[] = ['api/enterprise/system/properties']; getInstance(): AlfrescoApi { return this.alfrescoApi; } - constructor( - protected appConfig: AppConfigService, - protected storageService: StorageService, - @Optional() - @Inject(ALFRESCO_API_FACTORY) - private alfrescoApiFactory?: AlfrescoApiFactory - ) {} - async load(config: AlfrescoApiConfig): Promise<void> { this.currentAppConfig = config; diff --git a/lib/content-services/src/lib/tag/services/tag.service.ts b/lib/content-services/src/lib/tag/services/tag.service.ts index 0679595fbb..a9c399a806 100644 --- a/lib/content-services/src/lib/tag/services/tag.service.ts +++ b/lib/content-services/src/lib/tag/services/tag.service.ts @@ -17,7 +17,7 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { AppConfigService, UserPreferencesService } from '@alfresco/adf-core'; -import { EventEmitter, Injectable, Output } from '@angular/core'; +import { EventEmitter, Injectable, Output, inject } from '@angular/core'; import { from, Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; import { TagBody, TagEntry, TagPaging, TagsApi } from '@alfresco/js-api'; @@ -26,6 +26,10 @@ import { TagBody, TagEntry, TagPaging, TagsApi } from '@alfresco/js-api'; providedIn: 'root' }) export class TagService { + private readonly apiService = inject(AlfrescoApiService); + private readonly userPreferencesService = inject(UserPreferencesService); + private readonly appConfigService = inject(AppConfigService); + private _tagsApi: TagsApi; get tagsApi(): TagsApi { this._tagsApi = this._tagsApi ?? new TagsApi(this.apiService.getInstance()); @@ -36,12 +40,6 @@ export class TagService { @Output() refresh = new EventEmitter(); - constructor( - private apiService: AlfrescoApiService, - private userPreferencesService: UserPreferencesService, - private appConfigService: AppConfigService - ) {} - /** * Gets a list of tags added to a node. * diff --git a/lib/content-services/src/lib/tag/tag-actions/tag-actions.component.ts b/lib/content-services/src/lib/tag/tag-actions/tag-actions.component.ts index 60a78f1e23..15cb130ffe 100644 --- a/lib/content-services/src/lib/tag/tag-actions/tag-actions.component.ts +++ b/lib/content-services/src/lib/tag/tag-actions/tag-actions.component.ts @@ -42,6 +42,9 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; host: { class: 'adf-tag-node-actions-list' } }) export class TagActionsComponent implements OnChanges, OnInit { + private readonly tagService = inject(TagService); + private readonly translateService = inject(TranslationService); + /** The identifier of a node. */ @Input({ required: true }) nodeId: string; @@ -65,11 +68,6 @@ export class TagActionsComponent implements OnChanges, OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private tagService: TagService, - private translateService: TranslationService - ) {} - ngOnInit() { this.tagService.refresh.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.refreshTag()); } diff --git a/lib/content-services/src/lib/tag/tag-list/tag-list.component.ts b/lib/content-services/src/lib/tag/tag-list/tag-list.component.ts index 72fd335e11..10173098af 100644 --- a/lib/content-services/src/lib/tag/tag-list/tag-list.component.ts +++ b/lib/content-services/src/lib/tag/tag-list/tag-list.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, OnInit, Output, ViewEncapsulation, inject } from '@angular/core'; import { TagService } from '../services/tag.service'; import { IconModule, PaginationModel } from '@alfresco/adf-core'; import { TagEntry } from '@alfresco/js-api'; @@ -36,6 +36,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; host: { class: 'adf-tag-list' } }) export class TagListComponent implements OnInit { + private readonly tagService = inject(TagService); + /** Emitted when a tag is selected. */ @Output() result = new EventEmitter(); @@ -56,7 +58,7 @@ export class TagListComponent implements OnInit { isLoading = false; isSizeMinimum = true; - constructor(private tagService: TagService) { + constructor() { this.defaultPagination = { skipCount: 0, maxItems: this.size, diff --git a/lib/content-services/src/lib/tag/tag-node-list/tag-node-list.component.ts b/lib/content-services/src/lib/tag/tag-node-list/tag-node-list.component.ts index 3c33ffacb4..f4e49f707a 100644 --- a/lib/content-services/src/lib/tag/tag-node-list/tag-node-list.component.ts +++ b/lib/content-services/src/lib/tag/tag-node-list/tag-node-list.component.ts @@ -33,6 +33,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; encapsulation: ViewEncapsulation.None }) export class TagNodeListComponent implements OnChanges, OnInit { + private readonly tagService = inject(TagService); + /** The identifier of a node. */ @Input({ required: true }) nodeId: string; @@ -57,8 +59,6 @@ export class TagNodeListComponent implements OnChanges, OnInit { private readonly destroyRef = inject(DestroyRef); - constructor(private tagService: TagService) {} - ngOnChanges(): void { this.refreshTag(); } diff --git a/lib/content-services/src/lib/tag/tags-creator/tags-creator.component.ts b/lib/content-services/src/lib/tag/tags-creator/tags-creator.component.ts index e7c1f9d7b8..fff349e27c 100644 --- a/lib/content-services/src/lib/tag/tags-creator/tags-creator.component.ts +++ b/lib/content-services/src/lib/tag/tags-creator/tags-creator.component.ts @@ -82,6 +82,9 @@ const DEFAULT_TAGS_SORTING = { encapsulation: ViewEncapsulation.None }) export class TagsCreatorComponent implements OnInit, OnDestroy { + private readonly tagService = inject(TagService); + private readonly notificationService = inject(NotificationService); + /** * Mode for component. * In Create mode we can't select existing tags, we can only create them. @@ -161,9 +164,9 @@ export class TagsCreatorComponent implements OnInit, OnDestroy { private readonly existingTagsListLimit = 15; - private exactTagSet$ = new Subject<void>(); + private readonly exactTagSet$ = new Subject<void>(); private _tags: string[] = []; - private _tagNameControl = new FormControl<string>( + private readonly _tagNameControl = new FormControl<string>( '', [this.validateIfNotAlreadyAdded.bind(this), this.validateEmptyTag, this.validateSpecialCharacters], this.validateIfNotExistingTag.bind(this) @@ -175,19 +178,17 @@ export class TagsCreatorComponent implements OnInit, OnDestroy { private _spinnerVisible = false; private _typing = false; private _tagsListScrollbarVisible = false; - private cancelExistingTagsLoading$ = new Subject<void>(); + private readonly cancelExistingTagsLoading$ = new Subject<void>(); private existingExactTag: TagEntry; private _existingTagsPanelVisible: boolean; @ViewChild('tagsList') - private tagsListElement: ElementRef; + private readonly tagsListElement: ElementRef; @ViewChild('tagNameInput') - private tagNameInputElement: ElementRef; + private readonly tagNameInputElement: ElementRef; private readonly destroyRef = inject(DestroyRef); - constructor(private tagService: TagService, private notificationService: NotificationService) {} - ngOnInit(): void { this.tagNameControl.valueChanges .pipe( diff --git a/lib/content-services/src/lib/tree-view/components/tree-view.component.ts b/lib/content-services/src/lib/tree-view/components/tree-view.component.ts index fa75db5828..1ad42f590d 100644 --- a/lib/content-services/src/lib/tree-view/components/tree-view.component.ts +++ b/lib/content-services/src/lib/tree-view/components/tree-view.component.ts @@ -16,7 +16,7 @@ */ import { FlatTreeControl } from '@angular/cdk/tree'; -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, inject, Injector, runInInjectionContext } from '@angular/core'; import { TreeBaseNode } from '../models/tree-view.model'; import { TreeViewDataSource } from '../data/tree-view-datasource'; import { TreeViewService } from '../services/tree-view.service'; @@ -34,6 +34,9 @@ import { IconModule } from '@alfresco/adf-core'; styleUrls: ['./tree-view.component.scss'] }) export class TreeViewComponent implements OnChanges { + private readonly treeViewService = inject(TreeViewService); + private readonly injector = inject(Injector); + /** Identifier of the node to display. */ @Input({ required: true }) nodeId: string; @@ -49,9 +52,15 @@ export class TreeViewComponent implements OnChanges { treeControl: FlatTreeControl<TreeBaseNode>; dataSource: TreeViewDataSource; - constructor(private treeViewService: TreeViewService) { + constructor() { this.treeControl = new FlatTreeControl<TreeBaseNode>(this.getLevel, this.isExpandable); - this.dataSource = new TreeViewDataSource(this.treeControl, this.treeViewService); + this.dataSource = runInInjectionContext(this.injector, () => { + const injector = Injector.create({ + providers: [{ provide: FlatTreeControl, useValue: this.treeControl }], + parent: this.injector + }); + return runInInjectionContext(injector, () => new TreeViewDataSource()); + }); } ngOnChanges(changes: SimpleChanges) { diff --git a/lib/content-services/src/lib/tree-view/data/tree-view-datasource.ts b/lib/content-services/src/lib/tree-view/data/tree-view-datasource.ts index 9e19877a37..28447b75e5 100644 --- a/lib/content-services/src/lib/tree-view/data/tree-view-datasource.ts +++ b/lib/content-services/src/lib/tree-view/data/tree-view-datasource.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { CollectionViewer, SelectionChange } from '@angular/cdk/collections'; import { BehaviorSubject, merge, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -25,6 +25,9 @@ import { TreeViewService } from '../services/tree-view.service'; @Injectable() export class TreeViewDataSource { + private readonly treeControl = inject<FlatTreeControl<TreeBaseNode>>(FlatTreeControl); + private readonly treeViewService = inject(TreeViewService); + treeNodes: TreeBaseNode[]; dataChange = new BehaviorSubject<TreeBaseNode[]>([]); childrenSubscription = null; @@ -39,7 +42,7 @@ export class TreeViewDataSource { this.dataChange.next(value); } - constructor(private treeControl: FlatTreeControl<TreeBaseNode>, private treeViewService: TreeViewService) { + constructor() { this.dataChange.subscribe((treeNodes) => (this.treeNodes = treeNodes)); } diff --git a/lib/content-services/src/lib/tree-view/services/tree-view.service.ts b/lib/content-services/src/lib/tree-view/services/tree-view.service.ts index 109cb8368a..a6607e4bb8 100644 --- a/lib/content-services/src/lib/tree-view/services/tree-view.service.ts +++ b/lib/content-services/src/lib/tree-view/services/tree-view.service.ts @@ -26,7 +26,7 @@ import { map } from 'rxjs/operators'; providedIn: 'root' }) export class TreeViewService { - private nodeApi = inject(NodesApiService); + private readonly nodeApi = inject(NodesApiService); getTreeNodes(nodeId: string): Observable<TreeBaseNode[]> { return this.nodeApi.getNodeChildren(nodeId).pipe( diff --git a/lib/content-services/src/lib/tree/components/tree.component.ts b/lib/content-services/src/lib/tree/components/tree.component.ts index 229ae71ea2..92bdc13f2e 100644 --- a/lib/content-services/src/lib/tree/components/tree.component.ts +++ b/lib/content-services/src/lib/tree/components/tree.component.ts @@ -26,7 +26,8 @@ import { QueryList, TemplateRef, ViewChildren, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { BehaviorSubject, merge, Observable, Subject } from 'rxjs'; import { TreeNode, TreeNodeType } from '../models/tree-node.interface'; @@ -63,6 +64,9 @@ import { MatMenuModule } from '@angular/material/menu'; encapsulation: ViewEncapsulation.None }) export class TreeComponent<T extends TreeNode> implements OnInit, OnDestroy { + treeService = inject<TreeService<T>>(TreeService); + private readonly userPreferenceService = inject(UserPreferencesService); + /** TemplateRef to provide empty template when no nodes are loaded */ @Input() public emptyContentTemplate: TemplateRef<any>; @@ -107,18 +111,13 @@ export class TreeComponent<T extends TreeNode> implements OnInit, OnDestroy { @ViewChildren(MatCheckbox) public nodeCheckboxes: QueryList<MatCheckbox>; - private loadingRootSource = new BehaviorSubject<boolean>(false); + private readonly loadingRootSource = new BehaviorSubject<boolean>(false); private _contextMenuSource: T; private _contextMenuOptions: any[]; - private contextMenuOptionsChanged$ = new Subject<void>(); + private readonly contextMenuOptionsChanged$ = new Subject<void>(); public loadingRoot$: Observable<boolean>; public treeNodesSelection = new SelectionModel<T>(true, [], true, (node1: T, node2: T) => node1.id === node2.id); - constructor( - public treeService: TreeService<T>, - private userPreferenceService: UserPreferencesService - ) {} - set contextMenuSource(contextMenuSource: T) { this._contextMenuSource = contextMenuSource; } diff --git a/lib/content-services/src/lib/upload/components/file-uploading-dialog.component.ts b/lib/content-services/src/lib/upload/components/file-uploading-dialog.component.ts index aa4db94a32..771da44951 100644 --- a/lib/content-services/src/lib/upload/components/file-uploading-dialog.component.ts +++ b/lib/content-services/src/lib/upload/components/file-uploading-dialog.component.ts @@ -53,6 +53,11 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; encapsulation: ViewEncapsulation.None }) export class FileUploadingDialogComponent implements OnInit, OnDestroy { + private readonly uploadService = inject(UploadService); + private readonly changeDetector = inject(ChangeDetectorRef); + private readonly userPreferencesService = inject(UserPreferencesService); + private readonly elementRef = inject(ElementRef); + /** Dialog direction. Can be 'ltr' or 'rtl. */ private direction: Direction = 'ltr'; @@ -87,17 +92,10 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy { isDialogMinimized: boolean = false; isConfirmation: boolean = false; - private dialogActive = new Subject<boolean>(); + private readonly dialogActive = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef); - constructor( - private uploadService: UploadService, - private changeDetector: ChangeDetectorRef, - private userPreferencesService: UserPreferencesService, - private elementRef: ElementRef - ) {} - ngOnInit() { this.dialogActive.pipe(delay(100), takeUntilDestroyed(this.destroyRef)).subscribe(() => { const element: any = this.elementRef.nativeElement.querySelector('#upload-dialog'); diff --git a/lib/content-services/src/lib/upload/components/file-uploading-list.component.ts b/lib/content-services/src/lib/upload/components/file-uploading-list.component.ts index 6b7d92ca63..be284bd05a 100644 --- a/lib/content-services/src/lib/upload/components/file-uploading-list.component.ts +++ b/lib/content-services/src/lib/upload/components/file-uploading-list.component.ts @@ -19,7 +19,7 @@ import { TranslationService } from '@alfresco/adf-core'; import { UploadService } from '../../common/services/upload.service'; import { FileModel, FileUploadStatus } from '../../common/models/file.model'; -import { Component, ContentChild, Input, Output, TemplateRef, EventEmitter } from '@angular/core'; +import { Component, ContentChild, Input, Output, TemplateRef, EventEmitter, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; @Component({ @@ -29,6 +29,9 @@ import { CommonModule } from '@angular/common'; styleUrls: ['./file-uploading-list.component.scss'] }) export class FileUploadingListComponent { + private readonly uploadService = inject(UploadService); + private readonly translateService = inject(TranslationService); + @ContentChild(TemplateRef) template: any; @@ -39,8 +42,6 @@ export class FileUploadingListComponent { @Output() error = new EventEmitter<any>(); - constructor(private uploadService: UploadService, private translateService: TranslationService) {} - /** * Cancel file upload * diff --git a/lib/content-services/src/lib/upload/components/upload-button.component.ts b/lib/content-services/src/lib/upload/components/upload-button.component.ts index 5e5b706baf..d692ab6207 100644 --- a/lib/content-services/src/lib/upload/components/upload-button.component.ts +++ b/lib/content-services/src/lib/upload/components/upload-button.component.ts @@ -36,8 +36,8 @@ import { TranslatePipe } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None }) export class UploadButtonComponent extends UploadBase implements OnInit, OnChanges { - private contentService = inject(ContentService); - private nodesApiService = inject(NodesApiService); + private readonly contentService = inject(ContentService); + private readonly nodesApiService = inject(NodesApiService); /** Allows/disallows upload folders (only for Chrome). */ @Input() diff --git a/lib/content-services/src/lib/upload/components/upload-drag-area.component.ts b/lib/content-services/src/lib/upload/components/upload-drag-area.component.ts index 41ab09ac5f..ac16297e99 100644 --- a/lib/content-services/src/lib/upload/components/upload-drag-area.component.ts +++ b/lib/content-services/src/lib/upload/components/upload-drag-area.component.ts @@ -33,8 +33,8 @@ import { FileDraggableDirective } from '../directives/file-draggable.directive'; encapsulation: ViewEncapsulation.None }) export class UploadDragAreaComponent extends UploadBase { - private notificationService = inject(NotificationService); - private contentService = inject(ContentService); + private readonly notificationService = inject(NotificationService); + private readonly contentService = inject(ContentService); /** * Method called when files are dropped in the drag area. diff --git a/lib/content-services/src/lib/upload/components/upload-files.event.ts b/lib/content-services/src/lib/upload/components/upload-files.event.ts index e72fc66e3d..3822a3aee6 100644 --- a/lib/content-services/src/lib/upload/components/upload-files.event.ts +++ b/lib/content-services/src/lib/upload/components/upload-files.event.ts @@ -32,9 +32,9 @@ export class UploadFilesEvent { constructor( public files: Array<FileModel>, - private uploadService: UploadService, - private successEmitter: EventEmitter<any>, - private errorEmitter: EventEmitter<any> + private readonly uploadService: UploadService, + private readonly successEmitter: EventEmitter<any>, + private readonly errorEmitter: EventEmitter<any> ) {} pauseUpload() { diff --git a/lib/content-services/src/lib/upload/directives/file-draggable.directive.ts b/lib/content-services/src/lib/upload/directives/file-draggable.directive.ts index ef07dd8762..709797684f 100644 --- a/lib/content-services/src/lib/upload/directives/file-draggable.directive.ts +++ b/lib/content-services/src/lib/upload/directives/file-draggable.directive.ts @@ -18,7 +18,7 @@ /* eslint-disable @angular-eslint/no-input-rename */ import { FileUtils } from '@alfresco/adf-core'; -import { Directive, ElementRef, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from '@angular/core'; +import { Directive, ElementRef, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output, inject } from '@angular/core'; export const INPUT_FOCUS_CSS_CLASS = 'adf-file-draggable-input-focus'; export const DROP_EFFECT = 'copy'; @@ -27,6 +27,8 @@ export const DROP_EFFECT = 'copy'; selector: '[adf-file-draggable]' }) export class FileDraggableDirective implements OnInit, OnDestroy { + private readonly ngZone = inject(NgZone); + files: File[]; /** Enables/disables drag-and-drop functionality. */ @@ -41,9 +43,11 @@ export class FileDraggableDirective implements OnInit, OnDestroy { @Output() folderEntityDropped = new EventEmitter<any>(); - private element: HTMLElement; + private readonly element: HTMLElement; + + constructor() { + const el = inject(ElementRef); - constructor(el: ElementRef, private ngZone: NgZone) { this.element = el.nativeElement; } diff --git a/lib/content-services/src/lib/upload/pipes/file-upload-error.pipe.ts b/lib/content-services/src/lib/upload/pipes/file-upload-error.pipe.ts index a3bad1e925..4e93b18534 100644 --- a/lib/content-services/src/lib/upload/pipes/file-upload-error.pipe.ts +++ b/lib/content-services/src/lib/upload/pipes/file-upload-error.pipe.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { TranslationService } from '@alfresco/adf-core'; @Pipe({ @@ -23,7 +23,7 @@ import { TranslationService } from '@alfresco/adf-core'; pure: true }) export class FileUploadErrorPipe implements PipeTransform { - constructor(private translation: TranslationService) {} + private readonly translation = inject(TranslationService); transform(errorCode: number): string { return this.translation.instant(`FILE_UPLOAD.ERRORS.${errorCode || 'GENERIC'}`); diff --git a/lib/content-services/src/lib/version-compatibility/version-compatibility.directive.spec.ts b/lib/content-services/src/lib/version-compatibility/version-compatibility.directive.spec.ts index 31679b0c2b..4332b8fc3e 100644 --- a/lib/content-services/src/lib/version-compatibility/version-compatibility.directive.spec.ts +++ b/lib/content-services/src/lib/version-compatibility/version-compatibility.directive.spec.ts @@ -22,7 +22,8 @@ import { VersionCompatibilityService } from './version-compatibility.service'; import { VersionInfo } from '@alfresco/js-api'; import { RedirectAuthService } from '@alfresco/adf-core'; import { EMPTY, of } from 'rxjs'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { VersionCompatibilityDirective } from './version-compatibility.directive'; @Component({ @@ -52,8 +53,12 @@ describe('VersionCompatibilityDirective', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [VersionCompatibilityDirective, HttpClientTestingModule, TestComponent], - providers: [{ provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of() } }] + imports: [VersionCompatibilityDirective, TestComponent], + providers: [ + { provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of() } }, + provideHttpClient(), + provideHttpClientTesting() + ] }); fixture = TestBed.createComponent(TestComponent); versionCompatibilityService = TestBed.inject(VersionCompatibilityService); diff --git a/lib/content-services/src/lib/version-compatibility/version-compatibility.directive.ts b/lib/content-services/src/lib/version-compatibility/version-compatibility.directive.ts index 94a3806495..9060d1317c 100644 --- a/lib/content-services/src/lib/version-compatibility/version-compatibility.directive.ts +++ b/lib/content-services/src/lib/version-compatibility/version-compatibility.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, Input, ViewContainerRef, TemplateRef } from '@angular/core'; +import { Directive, Input, ViewContainerRef, TemplateRef, inject } from '@angular/core'; import { VersionCompatibilityService } from './version-compatibility.service'; import { take } from 'rxjs/operators'; @@ -23,18 +23,16 @@ import { take } from 'rxjs/operators'; selector: '[adf-acs-version]' }) export class VersionCompatibilityDirective { + private readonly templateRef = inject<TemplateRef<any>>(TemplateRef); + private readonly viewContainer = inject(ViewContainerRef); + private readonly versionCompatibilityService = inject(VersionCompatibilityService); + /** Minimum version required for component to work correctly . */ @Input('adf-acs-version') set version(requiredVersion: string) { this.validateAcsVersion(requiredVersion); } - constructor( - private templateRef: TemplateRef<any>, - private viewContainer: ViewContainerRef, - private versionCompatibilityService: VersionCompatibilityService - ) {} - private validateAcsVersion(requiredVersion: string) { this.versionCompatibilityService.acsVersionInitialized$.pipe(take(1)).subscribe(() => { this.viewContainer.clear(); diff --git a/lib/content-services/src/lib/version-compatibility/version-compatibility.service.spec.ts b/lib/content-services/src/lib/version-compatibility/version-compatibility.service.spec.ts index 7aa0f1ebba..06684ef347 100644 --- a/lib/content-services/src/lib/version-compatibility/version-compatibility.service.spec.ts +++ b/lib/content-services/src/lib/version-compatibility/version-compatibility.service.spec.ts @@ -23,7 +23,6 @@ import { VersionCompatibilityService } from './version-compatibility.service'; describe('VersionCompatibilityService', () => { let versionCompatibilityService: VersionCompatibilityService; - let discoveryApiService: DiscoveryApiService; const mockProductInfo = new BehaviorSubject<RepositoryInfo>(null); const acsResponseMock = { @@ -47,9 +46,8 @@ describe('VersionCompatibilityService', () => { } ] }); - discoveryApiService = TestBed.inject(DiscoveryApiService); mockProductInfo.next(acsResponseMock as RepositoryInfo); - versionCompatibilityService = new VersionCompatibilityService(discoveryApiService); + versionCompatibilityService = TestBed.inject(VersionCompatibilityService); }); it('should get ACS running version', () => { diff --git a/lib/content-services/src/lib/version-compatibility/version-compatibility.service.ts b/lib/content-services/src/lib/version-compatibility/version-compatibility.service.ts index e98f5dca84..5bafd26a6c 100644 --- a/lib/content-services/src/lib/version-compatibility/version-compatibility.service.ts +++ b/lib/content-services/src/lib/version-compatibility/version-compatibility.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { filter } from 'rxjs/operators'; import { ReplaySubject } from 'rxjs'; import { VersionInfo, RepositoryInfo } from '@alfresco/js-api'; @@ -25,11 +25,13 @@ import { DiscoveryApiService } from '../common/services/discovery-api.service'; providedIn: 'root' }) export class VersionCompatibilityService { + private readonly discoveryApiService = inject(DiscoveryApiService); + private acsVersion: VersionInfo; acsVersionInitialized$ = new ReplaySubject<void>(); - constructor(private discoveryApiService: DiscoveryApiService) { + constructor() { this.discoveryApiService.ecmProductInfo$ .pipe(filter((acsInfo) => !!acsInfo)) .subscribe((acsInfo: RepositoryInfo) => this.initializeAcsVersion(acsInfo.version)); diff --git a/lib/content-services/src/lib/version-manager/content-version.service.ts b/lib/content-services/src/lib/version-manager/content-version.service.ts index a70dea1976..ac9e57b29c 100644 --- a/lib/content-services/src/lib/version-manager/content-version.service.ts +++ b/lib/content-services/src/lib/version-manager/content-version.service.ts @@ -15,13 +15,15 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AlfrescoApiService } from '../services/alfresco-api.service'; import { ContentApi } from '@alfresco/js-api'; import { Observable, of } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class ContentVersionService { + private readonly alfrescoApi = inject(AlfrescoApiService); + private _contentApi: ContentApi; get contentApi(): ContentApi { @@ -31,8 +33,6 @@ export class ContentVersionService { return this._contentApi; } - constructor(private alfrescoApi: AlfrescoApiService) {} - /** * Get content URL for the given nodeId and specific version. * diff --git a/lib/content-services/src/lib/version-manager/version-comparison.component.ts b/lib/content-services/src/lib/version-manager/version-comparison.component.ts index 742d14e98b..09e35f63bb 100644 --- a/lib/content-services/src/lib/version-manager/version-comparison.component.ts +++ b/lib/content-services/src/lib/version-manager/version-comparison.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Input, ViewEncapsulation } from '@angular/core'; +import { Component, Input, ViewEncapsulation, inject } from '@angular/core'; import { Node } from '@alfresco/js-api'; import { FileTypePipe, ThumbnailService } from '@alfresco/adf-core'; import { CommonModule } from '@angular/common'; @@ -29,6 +29,8 @@ import { TranslatePipe } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None }) export class VersionComparisonComponent { + thumbnailService = inject(ThumbnailService); + /** Target node. */ @Input({ required: true }) node: Node; @@ -36,6 +38,4 @@ export class VersionComparisonComponent { /** New file for updating current version. */ @Input({ required: true }) newFileVersion: File; - - constructor(public thumbnailService: ThumbnailService) {} } diff --git a/lib/content-services/src/lib/version-manager/version-list.component.ts b/lib/content-services/src/lib/version-manager/version-list.component.ts index eb50dbe6a4..2d72218b21 100644 --- a/lib/content-services/src/lib/version-manager/version-list.component.ts +++ b/lib/content-services/src/lib/version-manager/version-list.component.ts @@ -37,8 +37,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export class VersionListDataSource extends InfiniteScrollDatasource<VersionEntry> { constructor( - private versionsApi: VersionsApi, - private node: Node + private readonly versionsApi: VersionsApi, + private readonly node: Node ) { super(); } @@ -72,6 +72,11 @@ export class VersionListDataSource extends InfiniteScrollDatasource<VersionEntry host: { class: 'adf-version-list' } }) export class VersionListComponent implements OnChanges, OnInit { + private readonly alfrescoApi = inject(AlfrescoApiService); + private readonly contentService = inject(ContentService); + private readonly contentVersionService = inject(ContentVersionService); + private readonly dialog = inject(MatDialog); + private _contentApi: ContentApi; get contentApi(): ContentApi { this._contentApi = this._contentApi ?? new ContentApi(this.alfrescoApi.getInstance()); @@ -135,13 +140,6 @@ export class VersionListComponent implements OnChanges, OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private alfrescoApi: AlfrescoApiService, - private contentService: ContentService, - private contentVersionService: ContentVersionService, - private dialog: MatDialog - ) {} - ngOnInit() { this.versionsDataSource = new VersionListDataSource(this.versionsApi, this.node); this.versionsDataSource.isLoading.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((isLoading) => { diff --git a/lib/content-services/src/lib/version-manager/version-manager.component.ts b/lib/content-services/src/lib/version-manager/version-manager.component.ts index f1ddda1aa1..22920a5054 100644 --- a/lib/content-services/src/lib/version-manager/version-manager.component.ts +++ b/lib/content-services/src/lib/version-manager/version-manager.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { Node } from '@alfresco/js-api'; import { VersionListComponent } from './version-list.component'; import { animate, state, style, transition, trigger } from '@angular/animations'; @@ -44,6 +44,9 @@ import { TranslatePipe } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None }) export class VersionManagerComponent implements OnInit { + private readonly contentService = inject(ContentService); + private readonly nodesApiService = inject(NodesApiService); + /** Target node to manage version history. */ @Input({ required: true }) node: Node; @@ -97,8 +100,6 @@ export class VersionManagerComponent implements OnInit { uploadState: string = 'close'; - constructor(private contentService: ContentService, private nodesApiService: NodesApiService) {} - ngOnInit() { if (this.newFileVersion) { this.toggleNewVersion(); diff --git a/lib/content-services/src/lib/version-manager/version-upload.component.ts b/lib/content-services/src/lib/version-manager/version-upload.component.ts index 1bc2821b36..25efd79f78 100644 --- a/lib/content-services/src/lib/version-manager/version-upload.component.ts +++ b/lib/content-services/src/lib/version-manager/version-upload.component.ts @@ -48,6 +48,9 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; host: { class: 'adf-version-upload' } }) export class VersionUploadComponent implements OnInit { + private readonly contentService = inject(ContentService); + private readonly uploadService = inject(UploadService); + semanticVersion: string = 'minor'; comment: string; uploadVersion: boolean = false; @@ -106,8 +109,6 @@ export class VersionUploadComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor(private contentService: ContentService, private uploadService: UploadService) {} - ngOnInit() { this.uploadService.fileUploadStarting.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event: FileUploadEvent) => { this.disabled = true; diff --git a/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.ts b/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.ts index 72d0acf30c..48a887bba2 100644 --- a/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.ts +++ b/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.ts @@ -70,6 +70,16 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; providers: [ViewUtilService] }) export class AlfrescoViewerComponent implements OnChanges, OnInit { + private readonly apiService = inject(AlfrescoApiService); + private readonly nodesApiService = inject(NodesApiService); + private readonly renditionService = inject(RenditionService); + private readonly viewUtilService = inject(ViewUtilService); + private readonly contentService = inject(ContentService); + private readonly uploadService = inject(UploadService); + dialog = inject(MatDialog); + private readonly cdr = inject(ChangeDetectorRef); + private readonly nodeActionsService = inject(NodeActionsService); + @ViewChild('adfViewer') adfViewer: ViewerComponent<{ node: Node }>; @@ -256,17 +266,9 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private apiService: AlfrescoApiService, - private nodesApiService: NodesApiService, - private renditionService: RenditionService, - private viewUtilService: ViewUtilService, - private contentService: ContentService, - private uploadService: UploadService, - public dialog: MatDialog, - private cdr: ChangeDetectorRef, - private nodeActionsService: NodeActionsService - ) { + constructor() { + const renditionService = this.renditionService; + renditionService.maxRetries = this.maxRetries; } diff --git a/lib/core/.storybook/main.ts b/lib/core/.storybook/main.ts index 4fcbeeb2b3..9b15340cd2 100644 --- a/lib/core/.storybook/main.ts +++ b/lib/core/.storybook/main.ts @@ -13,7 +13,10 @@ const config: StorybookConfig = { staticDirs: [ { from: '../src/lib/i18n', to: 'assets/adf-core/i18n' }, { from: '../src/lib/assets/images', to: 'assets/images' } - ] + ], + core: { + disableTelemetry: true + } }; export default config; diff --git a/lib/core/api/src/lib/adf-http-client.service.spec.ts b/lib/core/api/src/lib/adf-http-client.service.spec.ts index 5929a26236..70e77d9172 100644 --- a/lib/core/api/src/lib/adf-http-client.service.spec.ts +++ b/lib/core/api/src/lib/adf-http-client.service.spec.ts @@ -16,8 +16,8 @@ */ import { Emitters, RequestOptions, ResultListDataRepresentationTaskRepresentation, SecurityOptions } from '@alfresco/js-api'; -import { HttpParams } from '@angular/common/http'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { HttpParams, provideHttpClient } from '@angular/common/http'; +import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { AdfHttpClient } from './adf-http-client.service'; import { AlfrescoApiResponseError } from './alfresco-api/alfresco-api.response-error'; @@ -52,8 +52,7 @@ describe('AdfHttpClient', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [AdfHttpClient] + providers: [AdfHttpClient, provideHttpClient(), provideHttpClientTesting()] }); angularHttpClient = TestBed.inject(AdfHttpClient); controller = TestBed.inject(HttpTestingController); diff --git a/lib/core/api/src/lib/adf-http-client.service.ts b/lib/core/api/src/lib/adf-http-client.service.ts index 0ae87f7874..81fb4d8768 100644 --- a/lib/core/api/src/lib/adf-http-client.service.ts +++ b/lib/core/api/src/lib/adf-http-client.service.ts @@ -18,7 +18,7 @@ import { SHOULD_ADD_AUTH_TOKEN } from '@alfresco/adf-core/auth'; import { Emitters as JsApiEmitters, HttpClient as JsApiHttpClient, EventEmitterInstance, EventEmitterEvents } from '@alfresco/js-api'; import { HttpClient, HttpContext, HttpErrorResponse, HttpEvent, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { lastValueFrom, Observable, of, Subject, throwError } from 'rxjs'; import { catchError, map, takeUntil } from 'rxjs/operators'; import { @@ -45,7 +45,9 @@ export interface Emitters { providedIn: 'root' }) export class AdfHttpClient implements JsApiHttpClient { - private eventEmitter = new EventEmitter(); + private readonly httpClient = inject(HttpClient); + + private readonly eventEmitter = new EventEmitter(); _disableCsrf: boolean; @@ -64,10 +66,6 @@ export class AdfHttpClient implements JsApiHttpClient { defaultHeaders: {} }; - constructor(private httpClient: HttpClient) { - // No need for ee(this) anymore - we use composition instead of inheritance - } - // EventEmitter delegation methods on(event: EventEmitterEvents, fn: (...args: any[]) => void, context?: any): this { this.eventEmitter.on(event, fn, context); diff --git a/lib/core/auth/src/authentication-interceptor/authentication.interceptor.ts b/lib/core/auth/src/authentication-interceptor/authentication.interceptor.ts index 03824ac868..2bc160cd64 100644 --- a/lib/core/auth/src/authentication-interceptor/authentication.interceptor.ts +++ b/lib/core/auth/src/authentication-interceptor/authentication.interceptor.ts @@ -27,7 +27,7 @@ import { HttpSentEvent, HttpUserEvent } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, throwError as observableThrowError } from 'rxjs'; import { catchError, mergeMap } from 'rxjs/operators'; import { Authentication } from '../authentication'; @@ -36,7 +36,7 @@ export const SHOULD_ADD_AUTH_TOKEN = new HttpContextToken<boolean>(() => false); @Injectable() export class AuthenticationInterceptor implements HttpInterceptor { - constructor(private authService: Authentication) {} + private readonly authService = inject(Authentication); intercept( req: HttpRequest<any>, diff --git a/lib/core/breadcrumbs/src/components/breadcrumb/breadcrumb.component.ts b/lib/core/breadcrumbs/src/components/breadcrumb/breadcrumb.component.ts index f0b7ca3257..bdc57b881e 100644 --- a/lib/core/breadcrumbs/src/components/breadcrumb/breadcrumb.component.ts +++ b/lib/core/breadcrumbs/src/components/breadcrumb/breadcrumb.component.ts @@ -28,7 +28,8 @@ import { QueryList, SimpleChanges, TemplateRef, - ViewChildren + ViewChildren, + inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; @@ -46,6 +47,8 @@ import { IconModule } from '@alfresco/adf-core'; imports: [CommonModule, IconModule, TranslatePipe, MatButtonModule, BreadcrumbFocusDirective] }) export class BreadcrumbComponent implements AfterContentInit, OnChanges { + private readonly cdr = inject(ChangeDetectorRef); + private _breadcrumbTemplateRefs: Array<TemplateRef<unknown>> = []; @Input() @@ -62,8 +65,6 @@ export class BreadcrumbComponent implements AfterContentInit, OnChanges { selectedBreadcrumbs: Array<TemplateRef<unknown>> = []; - constructor(private cdr: ChangeDetectorRef) {} - ngAfterContentInit() { this.breadcrumbItems.changes .pipe( diff --git a/lib/core/breadcrumbs/src/directives/breadcrumb-focus.directive.ts b/lib/core/breadcrumbs/src/directives/breadcrumb-focus.directive.ts index ad8c872a79..42cc9f9fb9 100644 --- a/lib/core/breadcrumbs/src/directives/breadcrumb-focus.directive.ts +++ b/lib/core/breadcrumbs/src/directives/breadcrumb-focus.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, ElementRef } from '@angular/core'; +import { Directive, ElementRef, inject } from '@angular/core'; /** @internal */ @Directive({ @@ -26,7 +26,7 @@ import { Directive, ElementRef } from '@angular/core'; } }) export class BreadcrumbFocusDirective { - constructor(private elementRef: ElementRef) {} + private readonly elementRef = inject(ElementRef); focusOnFirstFocusableElement() { this.getFocusableElements(this.elementRef.nativeElement)[0].focus(); diff --git a/lib/core/feature-flags/src/lib/components/feature-override-indicator.component.ts b/lib/core/feature-flags/src/lib/components/feature-override-indicator.component.ts index 9cfe0eab46..4e3478e8db 100644 --- a/lib/core/feature-flags/src/lib/components/feature-override-indicator.component.ts +++ b/lib/core/feature-flags/src/lib/components/feature-override-indicator.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, ViewEncapsulation, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FeaturesServiceToken, IDebugFeaturesService } from '../interfaces/features.interface'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -48,16 +48,16 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; changeDetection: ChangeDetectionStrategy.OnPush }) export class FlagsOverrideComponent { + private readonly featuresService = inject<IDebugFeaturesService>(FeaturesServiceToken); + isEnabled = false; @Input() size: 'small' | 'medium' | 'large' = 'medium'; - constructor( - @Inject(FeaturesServiceToken) - private featuresService: IDebugFeaturesService, - changeDetectorRef: ChangeDetectorRef - ) { + constructor() { + const changeDetectorRef = inject(ChangeDetectorRef); + if (this.featuresService.isEnabled$) { this.featuresService .isEnabled$() diff --git a/lib/core/feature-flags/src/lib/components/flags/flags.component.ts b/lib/core/feature-flags/src/lib/components/flags/flags.component.ts index e8a6d15152..0a20c88a46 100644 --- a/lib/core/feature-flags/src/lib/components/flags/flags.component.ts +++ b/lib/core/feature-flags/src/lib/components/flags/flags.component.ts @@ -15,12 +15,11 @@ * limitations under the License. */ -import { ChangeDetectionStrategy, Component, Inject, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ViewEncapsulation, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FeaturesServiceToken, IDebugFeaturesService, - IFeaturesService, IWritableFeaturesService, WritableFeaturesServiceToken, WritableFlagChangeset @@ -60,6 +59,9 @@ import { IconModule } from '@alfresco/adf-core'; changeDetection: ChangeDetectionStrategy.OnPush }) export class FlagsComponent { + private readonly featuresService = inject<IDebugFeaturesService>(FeaturesServiceToken); + private readonly writableFeaturesService = inject<IWritableFeaturesService>(WritableFeaturesServiceToken); + displayedColumns: string[] = ['icon', 'flag', 'value']; flags$: Observable<{ fictive: boolean; flag: string; value: any }[]>; isEnabled = false; @@ -68,12 +70,7 @@ export class FlagsComponent { inputValue$ = new BehaviorSubject<string>(''); showPlusButton$!: Observable<boolean>; writableFlagChangeset: WritableFlagChangeset = {}; - constructor( - @Inject(FeaturesServiceToken) - private featuresService: IDebugFeaturesService & IFeaturesService<WritableFlagChangeset>, - @Inject(WritableFeaturesServiceToken) - private writableFeaturesService: IWritableFeaturesService - ) { + constructor() { if (this.featuresService.isEnabled$) { this.featuresService .isEnabled$() diff --git a/lib/core/feature-flags/src/lib/directives/features.directive.ts b/lib/core/feature-flags/src/lib/directives/features.directive.ts index 755df99060..4d6cddb734 100644 --- a/lib/core/feature-flags/src/lib/directives/features.directive.ts +++ b/lib/core/feature-flags/src/lib/directives/features.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, Inject, Input, TemplateRef, ViewContainerRef } from '@angular/core'; +import { Directive, Input, TemplateRef, ViewContainerRef, inject } from '@angular/core'; import { BehaviorSubject, combineLatest } from 'rxjs'; import { FeaturesServiceToken, FlagChangeset, IFeaturesService } from '../interfaces/features.interface'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -25,19 +25,19 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; selector: '[adfForFeatures]' }) export class FeaturesDirective { + private readonly featuresService = inject<IFeaturesService>(FeaturesServiceToken); + private readonly templateRef = inject<TemplateRef<any>>(TemplateRef); + private readonly viewContainer = inject(ViewContainerRef); + private hasView = false; - private inputUpdate$ = new BehaviorSubject([] as string[]); + private readonly inputUpdate$ = new BehaviorSubject([] as string[]); @Input() set adfForFeatures(feature: string[] | string) { this.inputUpdate$.next(Array.isArray(feature) ? feature : [feature]); } - constructor( - @Inject(FeaturesServiceToken) private featuresService: IFeaturesService, - private templateRef: TemplateRef<any>, - private viewContainer: ViewContainerRef - ) { + constructor() { combineLatest([this.featuresService.getFlags$(), this.inputUpdate$]) .pipe(takeUntilDestroyed()) .subscribe(([flags, features]: any) => this.updateView(flags, features)); diff --git a/lib/core/feature-flags/src/lib/directives/not-features.directive.ts b/lib/core/feature-flags/src/lib/directives/not-features.directive.ts index 9a05d582e4..7a47346a36 100644 --- a/lib/core/feature-flags/src/lib/directives/not-features.directive.ts +++ b/lib/core/feature-flags/src/lib/directives/not-features.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, Inject, Input, TemplateRef, ViewContainerRef } from '@angular/core'; +import { Directive, Input, TemplateRef, ViewContainerRef, inject } from '@angular/core'; import { BehaviorSubject, combineLatest } from 'rxjs'; import { FeaturesServiceToken, FlagChangeset, IFeaturesService } from '../interfaces/features.interface'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -25,19 +25,19 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; selector: '[adfNotForFeatures]' }) export class NotFeaturesDirective { + private readonly featuresService = inject<IFeaturesService>(FeaturesServiceToken); + private readonly templateRef = inject<TemplateRef<any>>(TemplateRef); + private readonly viewContainer = inject(ViewContainerRef); + private hasView = false; - private inputUpdate$ = new BehaviorSubject([] as string[]); + private readonly inputUpdate$ = new BehaviorSubject([] as string[]); @Input() set adfNotForFeatures(feature: string[] | string) { this.inputUpdate$.next(Array.isArray(feature) ? feature : [feature]); } - constructor( - @Inject(FeaturesServiceToken) private featuresService: IFeaturesService, - private templateRef: TemplateRef<any>, - private viewContainer: ViewContainerRef - ) { + constructor() { combineLatest([this.featuresService.getFlags$(), this.inputUpdate$]) .pipe(takeUntilDestroyed()) .subscribe(([flags, features]: any) => this.updateView(flags, features)); diff --git a/lib/core/feature-flags/src/lib/guards/is-feature-off.guard.ts b/lib/core/feature-flags/src/lib/guards/is-feature-off.guard.ts index c455e22a0a..2d6898f90f 100644 --- a/lib/core/feature-flags/src/lib/guards/is-feature-off.guard.ts +++ b/lib/core/feature-flags/src/lib/guards/is-feature-off.guard.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Inject, Injectable, inject } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { FeaturesServiceToken, IFeaturesService } from '../interfaces/features.interface'; import { Route } from '@angular/router'; import { Observable } from 'rxjs'; @@ -24,7 +24,7 @@ export const isFeatureOff = (flag: string) => () => inject(FeaturesServiceToken) @Injectable({ providedIn: 'root' }) export class IsFeatureOff { - constructor(@Inject(FeaturesServiceToken) private featuresServiceToken: IFeaturesService) {} + private readonly featuresServiceToken = inject<IFeaturesService>(FeaturesServiceToken); canMatch(route: Route): Observable<boolean> { return this.featuresServiceToken.isOff$(route?.data?.['feature']); diff --git a/lib/core/feature-flags/src/lib/guards/is-feature-on.guard.ts b/lib/core/feature-flags/src/lib/guards/is-feature-on.guard.ts index f262551b6d..0829df547c 100644 --- a/lib/core/feature-flags/src/lib/guards/is-feature-on.guard.ts +++ b/lib/core/feature-flags/src/lib/guards/is-feature-on.guard.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Inject, Injectable, inject } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { FeaturesServiceToken, IFeaturesService } from '../interfaces/features.interface'; import { Route } from '@angular/router'; import { Observable } from 'rxjs'; @@ -24,7 +24,7 @@ export const isFeatureOn = (flag: string) => () => inject(FeaturesServiceToken). @Injectable({ providedIn: 'root' }) export class IsFeatureOn { - constructor(@Inject(FeaturesServiceToken) private featuresServiceToken: IFeaturesService) {} + private readonly featuresServiceToken = inject<IFeaturesService>(FeaturesServiceToken); canMatch(route: Route): Observable<boolean> { return this.featuresServiceToken.isOn$(route?.data?.['feature']); diff --git a/lib/core/feature-flags/src/lib/guards/is-flags-override-on.guard.ts b/lib/core/feature-flags/src/lib/guards/is-flags-override-on.guard.ts index 4afae9c95d..c9ac80452a 100644 --- a/lib/core/feature-flags/src/lib/guards/is-flags-override-on.guard.ts +++ b/lib/core/feature-flags/src/lib/guards/is-flags-override-on.guard.ts @@ -15,14 +15,14 @@ * limitations under the License. */ -import { Inject, Injectable, Optional, inject } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { FlagsOverrideToken } from '../interfaces/features.interface'; export const isFlagsOverrideOn = () => () => inject(FlagsOverrideToken) ?? false; @Injectable({ providedIn: 'root' }) export class IsFlagsOverrideOn { - constructor(@Optional() @Inject(FlagsOverrideToken) private devToolsToken: boolean) {} + private readonly devToolsToken = inject(FlagsOverrideToken, { optional: true }); canMatch(): boolean { return !!this.devToolsToken; diff --git a/lib/core/feature-flags/src/lib/services/debug-features.service.ts b/lib/core/feature-flags/src/lib/services/debug-features.service.ts index 0a6434b5b8..26cd4ffb96 100644 --- a/lib/core/feature-flags/src/lib/services/debug-features.service.ts +++ b/lib/core/feature-flags/src/lib/services/debug-features.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Inject, Injectable, Optional } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { BehaviorSubject, Observable, combineLatest } from 'rxjs'; import { filter, switchMap } from 'rxjs/operators'; import { @@ -26,12 +26,15 @@ import { WritableFeaturesServiceToken, WritableFeaturesServiceConfigToken, WritableFeaturesServiceConfig, - FlagSet, - IWritableFeaturesService + FlagSet } from '../interfaces/features.interface'; @Injectable() export class DebugFeaturesService implements IDebugFeaturesService { + private readonly overriddenFeaturesService = inject<IFeaturesService>(OverridableFeaturesServiceToken); + private readonly writableFeaturesService = inject(WritableFeaturesServiceToken); + private readonly config = inject<WritableFeaturesServiceConfig>(WritableFeaturesServiceConfigToken, { optional: true }); + private readonly isInDebugModeSubject = new BehaviorSubject<boolean>(false); private readonly isInDebugMode$ = this.isInDebugModeSubject.asObservable(); private readonly initSubject = new BehaviorSubject<boolean>(false); @@ -40,11 +43,7 @@ export class DebugFeaturesService implements IDebugFeaturesService { return `${this.config?.storageKey || 'feature-flags'}-override`; } - constructor( - @Inject(OverridableFeaturesServiceToken) private overriddenFeaturesService: IFeaturesService, - @Inject(WritableFeaturesServiceToken) private writableFeaturesService: IFeaturesService & IWritableFeaturesService, - @Optional() @Inject(WritableFeaturesServiceConfigToken) private config?: WritableFeaturesServiceConfig - ) { + constructor() { this.init(); combineLatest({ diff --git a/lib/core/feature-flags/src/lib/services/qa-features.helper.ts b/lib/core/feature-flags/src/lib/services/qa-features.helper.ts index 32629e36ef..e84fbce046 100644 --- a/lib/core/feature-flags/src/lib/services/qa-features.helper.ts +++ b/lib/core/feature-flags/src/lib/services/qa-features.helper.ts @@ -15,12 +15,13 @@ * limitations under the License. */ -import { ApplicationRef, Inject, Injectable } from '@angular/core'; +import { ApplicationRef, Injectable, inject } from '@angular/core'; import { FeaturesServiceToken, FlagSet } from '../interfaces/features.interface'; import { DebugFeaturesService } from './debug-features.service'; @Injectable() export class QaFeaturesHelper { - constructor(private applicationRef: ApplicationRef, @Inject(FeaturesServiceToken) private debugFeaturesService: DebugFeaturesService) {} + private readonly applicationRef = inject(ApplicationRef); + private readonly debugFeaturesService = inject<DebugFeaturesService>(FeaturesServiceToken); isOn(key: string): boolean { let isOn = false; diff --git a/lib/core/feature-flags/src/lib/services/storage-features.service.ts b/lib/core/feature-flags/src/lib/services/storage-features.service.ts index 6dc9b07afe..9faf3c9e70 100644 --- a/lib/core/feature-flags/src/lib/services/storage-features.service.ts +++ b/lib/core/feature-flags/src/lib/services/storage-features.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Inject, Injectable, Optional } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; import { filter, map } from 'rxjs/operators'; import { @@ -31,12 +31,14 @@ import { FlagSetParser } from './flagset.parser'; @Injectable({ providedIn: 'root' }) export class StorageFeaturesService implements IFeaturesService, IWritableFeaturesService { + private readonly config = inject<WritableFeaturesServiceConfig>(WritableFeaturesServiceConfigToken, { optional: true }); + private currentFlagState: WritableFlagChangeset = {}; private readonly flags = new BehaviorSubject<WritableFlagChangeset>({}); private readonly flags$ = this.flags.asObservable(); private readonly initSubject = new BehaviorSubject<boolean>(false); - constructor(@Optional() @Inject(WritableFeaturesServiceConfigToken) private config?: WritableFeaturesServiceConfig) { + constructor() { combineLatest({ flags: this.flags, init: this.waitForInitializationToFinish() diff --git a/lib/core/shell/src/lib/components/shell/shell.component.ts b/lib/core/shell/src/lib/components/shell/shell.component.ts index 4ad1d913b4..58a8546805 100644 --- a/lib/core/shell/src/lib/components/shell/shell.component.ts +++ b/lib/core/shell/src/lib/components/shell/shell.component.ts @@ -23,7 +23,7 @@ import { SidenavLayoutNavigationDirective } from '@alfresco/adf-core'; import { DynamicExtensionComponent } from '@alfresco/adf-extensions'; -import { Component, DestroyRef, inject, Inject, OnInit, Optional, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { NavigationEnd, Router, RouterModule } from '@angular/router'; import { Observable } from 'rxjs'; import { filter, map, withLatestFrom } from 'rxjs/operators'; @@ -48,6 +48,11 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; host: { class: 'app-shell' } }) export class ShellLayoutComponent implements OnInit { + private readonly router = inject(Router); + private readonly appConfigService = inject(AppConfigService); + private readonly breakpointObserver = inject(BreakpointObserver); + private readonly shellService = inject<ShellAppService>(SHELL_APP_SERVICE); + @ViewChild('layout', { static: true }) layout: SidenavLayoutComponent; @@ -62,14 +67,10 @@ export class ShellLayoutComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private router: Router, - private appConfigService: AppConfigService, - private breakpointObserver: BreakpointObserver, - @Inject(SHELL_APP_SERVICE) private shellService: ShellAppService, - @Optional() @Inject(SHELL_NAVBAR_MIN_WIDTH) navBarMinWidth: number, - @Optional() @Inject(SHELL_NAVBAR_MAX_WIDTH) navbarMaxWidth: number - ) { + constructor() { + const navBarMinWidth = inject(SHELL_NAVBAR_MIN_WIDTH, { optional: true }); + const navbarMaxWidth = inject(SHELL_NAVBAR_MAX_WIDTH, { optional: true }); + this.sidenavMin = navBarMinWidth ?? 70; this.sidenavMax = navbarMaxWidth ?? 320; } diff --git a/lib/core/src/lib/about/about-license-list/about-license-list.component.ts b/lib/core/src/lib/about/about-license-list/about-license-list.component.ts index bbde079f8d..f5f8ec4e9b 100644 --- a/lib/core/src/lib/about/about-license-list/about-license-list.component.ts +++ b/lib/core/src/lib/about/about-license-list/about-license-list.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation, inject } from '@angular/core'; import { LicenseData } from '../interfaces'; import { CommonModule } from '@angular/common'; import { TranslatePipe, TranslateService } from '@ngx-translate/core'; @@ -30,6 +30,8 @@ import { MatTableModule } from '@angular/material/table'; imports: [CommonModule, TranslatePipe, MatTableModule] }) export class AboutLicenseListComponent { + private readonly translateService = inject(TranslateService); + columns = [ { columnDef: 'property', @@ -64,6 +66,4 @@ export class AboutLicenseListComponent { @Input({ required: true }) data: LicenseData[] = []; - - constructor(private readonly translateService: TranslateService) {} } diff --git a/lib/core/src/lib/about/about-server-settings/about-server-settings.component.ts b/lib/core/src/lib/about/about-server-settings/about-server-settings.component.ts index 2dc7348021..26b53ce992 100644 --- a/lib/core/src/lib/about/about-server-settings/about-server-settings.component.ts +++ b/lib/core/src/lib/about/about-server-settings/about-server-settings.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service'; import { CommonModule } from '@angular/common'; import { TranslatePipe } from '@ngx-translate/core'; @@ -29,11 +29,11 @@ import { MatCardModule } from '@angular/material/card'; imports: [CommonModule, TranslatePipe, MatCardModule] }) export class AboutServerSettingsComponent implements OnInit { + private readonly appConfig = inject(AppConfigService); + ecmHost = ''; bpmHost = ''; - constructor(private appConfig: AppConfigService) {} - ngOnInit() { this.ecmHost = this.appConfig.get(AppConfigValues.ECMHOST); this.bpmHost = this.appConfig.get(AppConfigValues.BPMHOST); diff --git a/lib/core/src/lib/about/about.component.stories.ts b/lib/core/src/lib/about/about.component.stories.ts index abcade3322..71dfe57540 100644 --- a/lib/core/src/lib/about/about.component.stories.ts +++ b/lib/core/src/lib/about/about.component.stories.ts @@ -30,7 +30,7 @@ import { BehaviorSubject, Observable } from 'rxjs'; }) export class AppExtensionServiceMock { references$: Observable<ExtensionRef[]>; - private _references = new BehaviorSubject<ExtensionRef[]>([]); + private readonly _references = new BehaviorSubject<ExtensionRef[]>([]); constructor() { this.references$ = this._references.asObservable(); diff --git a/lib/core/src/lib/app-config/app-config-storage-prefix.factory.spec.ts b/lib/core/src/lib/app-config/app-config-storage-prefix.factory.spec.ts index d5370ab0e8..4c5b05b35f 100644 --- a/lib/core/src/lib/app-config/app-config-storage-prefix.factory.spec.ts +++ b/lib/core/src/lib/app-config/app-config-storage-prefix.factory.spec.ts @@ -16,8 +16,10 @@ */ import { of } from 'rxjs'; -import { StoragePrefixFactory, StoragePrefixFactoryService } from './app-config-storage-prefix.factory'; +import { StoragePrefixFactory, StoragePrefixFactoryService, STORAGE_PREFIX_FACTORY_SERVICE } from './app-config-storage-prefix.factory'; import { AppConfigService } from './app-config.service'; +import { Injector, runInInjectionContext } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; type TestAppConfigService = Pick<AppConfigService, 'select'>; @@ -31,7 +33,12 @@ describe('StoragePrefixFactory', () => { } }; - const prefixFactory = new StoragePrefixFactory(appConfigService as AppConfigService); + const injector = Injector.create({ + providers: [{ provide: AppConfigService, useValue: appConfigService }], + parent: TestBed.inject(Injector) + }); + + const prefixFactory = runInInjectionContext(injector, () => new StoragePrefixFactory()); prefixFactory.getPrefix().subscribe((prefix) => { expect(prefix).toBe(appConfigPrefix); @@ -47,7 +54,12 @@ describe('StoragePrefixFactory', () => { } }; - const prefixFactory = new StoragePrefixFactory(appConfigService as AppConfigService); + const injector = Injector.create({ + providers: [{ provide: AppConfigService, useValue: appConfigService }], + parent: TestBed.inject(Injector) + }); + + const prefixFactory = runInInjectionContext(injector, () => new StoragePrefixFactory()); prefixFactory.getPrefix().subscribe((prefix) => { expect(prefix).toBe(appConfigPrefix); @@ -69,7 +81,15 @@ describe('StoragePrefixFactory', () => { } }; - const prefixFactory = new StoragePrefixFactory(appConfigService as AppConfigService, externalPrefixFactory); + const injector = Injector.create({ + providers: [ + { provide: AppConfigService, useValue: appConfigService }, + { provide: STORAGE_PREFIX_FACTORY_SERVICE, useValue: externalPrefixFactory } + ], + parent: TestBed.inject(Injector) + }); + + const prefixFactory = runInInjectionContext(injector, () => new StoragePrefixFactory()); prefixFactory.getPrefix().subscribe((prefix) => { expect(prefix).toBe('prefix-from-factory'); @@ -92,7 +112,15 @@ describe('StoragePrefixFactory', () => { } }; - const prefixFactory = new StoragePrefixFactory(appConfigService as AppConfigService, externalPrefixFactory); + const injector = Injector.create({ + providers: [ + { provide: AppConfigService, useValue: appConfigService }, + { provide: STORAGE_PREFIX_FACTORY_SERVICE, useValue: externalPrefixFactory } + ], + parent: TestBed.inject(Injector) + }); + + const prefixFactory = runInInjectionContext(injector, () => new StoragePrefixFactory()); prefixFactory.getPrefix().subscribe((prefix) => { expect(prefix).toBe(appConfigPrefix); diff --git a/lib/core/src/lib/app-config/app-config-storage-prefix.factory.ts b/lib/core/src/lib/app-config/app-config-storage-prefix.factory.ts index 6552df0316..cee94cc865 100644 --- a/lib/core/src/lib/app-config/app-config-storage-prefix.factory.ts +++ b/lib/core/src/lib/app-config/app-config-storage-prefix.factory.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Inject, Injectable, InjectionToken, Optional } from '@angular/core'; +import { Injectable, InjectionToken, inject } from '@angular/core'; import { AppConfigService, AppConfigValues } from './app-config.service'; import { Observable, of } from 'rxjs'; import { switchMap } from 'rxjs/operators'; @@ -30,12 +30,8 @@ export const STORAGE_PREFIX_FACTORY_SERVICE = new InjectionToken<StoragePrefixFa providedIn: 'root' }) export class StoragePrefixFactory { - constructor( - private appConfigService: AppConfigService, - @Optional() - @Inject(STORAGE_PREFIX_FACTORY_SERVICE) - private storagePrefixFactory?: StoragePrefixFactoryService - ) {} + private readonly appConfigService = inject(AppConfigService); + private readonly storagePrefixFactory = inject<StoragePrefixFactoryService>(STORAGE_PREFIX_FACTORY_SERVICE, { optional: true }); getPrefix(): Observable<string | undefined> { return this.appConfigService.select(AppConfigValues.STORAGE_PREFIX).pipe( diff --git a/lib/core/src/lib/app-config/app-config.pipe.ts b/lib/core/src/lib/app-config/app-config.pipe.ts index 07276605d9..10f4e95798 100644 --- a/lib/core/src/lib/app-config/app-config.pipe.ts +++ b/lib/core/src/lib/app-config/app-config.pipe.ts @@ -15,14 +15,14 @@ * limitations under the License. */ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { AppConfigService } from './app-config.service'; @Pipe({ name: 'adfAppConfig' }) export class AppConfigPipe implements PipeTransform { - constructor(private config: AppConfigService) {} + private readonly config = inject(AppConfigService); transform(value: string, fallback?: any): any { return this.config.get(value, fallback); diff --git a/lib/core/src/lib/auth/authentication-interceptor/auth-bearer.interceptor.ts b/lib/core/src/lib/auth/authentication-interceptor/auth-bearer.interceptor.ts index a279c57736..c35e33d086 100644 --- a/lib/core/src/lib/auth/authentication-interceptor/auth-bearer.interceptor.ts +++ b/lib/core/src/lib/auth/authentication-interceptor/auth-bearer.interceptor.ts @@ -16,7 +16,7 @@ */ import { throwError as observableThrowError, Observable } from 'rxjs'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpHandler, HttpInterceptor, @@ -33,12 +33,12 @@ import { AuthenticationService } from '../services/authentication.service'; @Injectable() export class AuthBearerInterceptor implements HttpInterceptor { - private _bearerExcludedUrls: readonly string[] = ['resources/', 'assets/', 'auth/realms', 'idp/']; + private readonly authenticationService = inject(AuthenticationService); + + private readonly _bearerExcludedUrls: readonly string[] = ['resources/', 'assets/', 'auth/realms', 'idp/']; private excludedUrlsRegex: RegExp[]; - constructor(private authenticationService: AuthenticationService) {} - private loadExcludedUrlsRegex() { const excludedUrls = this.bearerExcludedUrls; this.excludedUrlsRegex = excludedUrls.map((urlPattern) => new RegExp(`^https?://[^/]+/${urlPattern}`, 'i')) || []; diff --git a/lib/core/src/lib/auth/basic-auth/basic-alfresco-auth.service.ts b/lib/core/src/lib/auth/basic-auth/basic-alfresco-auth.service.ts index e028ffe05c..cac98f900a 100644 --- a/lib/core/src/lib/auth/basic-auth/basic-alfresco-auth.service.ts +++ b/lib/core/src/lib/auth/basic-auth/basic-alfresco-auth.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service'; import { Authentication } from '../interfaces/authentication.interface'; import { CookieService } from '../../common/services/cookie.service'; @@ -34,6 +34,9 @@ const REMEMBER_ME_UNTIL = 1000 * 60 * 60 * 24 * 30; providedIn: 'root' }) export class BasicAlfrescoAuthService extends BaseAuthenticationService { + private readonly contentAuth = inject(ContentAuth); + private readonly processAuth = inject(ProcessAuth); + protected redirectUrl: RedirectionModel = null; authentications: Authentication = { @@ -43,12 +46,10 @@ export class BasicAlfrescoAuthService extends BaseAuthenticationService { type: 'basic' }; - constructor( - appConfig: AppConfigService, - cookie: CookieService, - private readonly contentAuth: ContentAuth, - private readonly processAuth: ProcessAuth - ) { + constructor() { + const appConfig = inject(AppConfigService); + const cookie = inject(CookieService); + super(appConfig, cookie); this.appConfig.onLoad.subscribe(() => { diff --git a/lib/core/src/lib/auth/basic-auth/content-auth.ts b/lib/core/src/lib/auth/basic-auth/content-auth.ts index 6805c5b03f..cdbbb8f2e0 100644 --- a/lib/core/src/lib/auth/basic-auth/content-auth.ts +++ b/lib/core/src/lib/auth/basic-auth/content-auth.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AdfHttpClient } from '@alfresco/adf-core/api'; import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service'; import { StorageService } from '../../common/services/storage.service'; @@ -38,6 +38,10 @@ export interface TicketEntry { providedIn: 'root' }) export class ContentAuth { + private readonly appConfigService = inject(AppConfigService); + private readonly adfHttpClient = inject(AdfHttpClient); + private readonly storageService = inject(StorageService); + onLogin = new ReplaySubject<any>(1); onLogout = new ReplaySubject<any>(1); onError = new Subject<any>(); @@ -59,7 +63,7 @@ export class ContentAuth { return this.appConfigService.get<string>(AppConfigValues.ECMHOST) + '/' + contextRootEcm + '/api/-default-/public/authentication/versions/1'; } - constructor(private appConfigService: AppConfigService, private adfHttpClient: AdfHttpClient, private storageService: StorageService) { + constructor() { this.appConfigService.onLoad.subscribe(() => { this.setConfig(); }); diff --git a/lib/core/src/lib/auth/basic-auth/process-auth.ts b/lib/core/src/lib/auth/basic-auth/process-auth.ts index 836017ca97..df48b126c4 100644 --- a/lib/core/src/lib/auth/basic-auth/process-auth.ts +++ b/lib/core/src/lib/auth/basic-auth/process-auth.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AdfHttpClient } from '@alfresco/adf-core/api'; import { Authentication } from '../interfaces/authentication.interface'; import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service'; @@ -26,6 +26,10 @@ import { ReplaySubject, Subject } from 'rxjs'; providedIn: 'root' }) export class ProcessAuth { + private readonly appConfigService = inject(AppConfigService); + private readonly adfHttpClient = inject(AdfHttpClient); + private readonly storageService = inject(StorageService); + onLogin = new ReplaySubject<any>(1); onLogout = new ReplaySubject<any>(1); onError = new Subject<any>(); @@ -45,7 +49,7 @@ export class ProcessAuth { return this.appConfigService.get<string>(AppConfigValues.BPMHOST) + '/' + contextRootBpm; } - constructor(private appConfigService: AppConfigService, private adfHttpClient: AdfHttpClient, private storageService: StorageService) { + constructor() { this.appConfigService.onLoad.subscribe(() => { this.setConfig(); }); diff --git a/lib/core/src/lib/auth/guard/auth-guard.service.ts b/lib/core/src/lib/auth/guard/auth-guard.service.ts index fc04c49f86..c5033ea20c 100644 --- a/lib/core/src/lib/auth/guard/auth-guard.service.ts +++ b/lib/core/src/lib/auth/guard/auth-guard.service.ts @@ -23,21 +23,19 @@ import { MatDialog } from '@angular/material/dialog'; import { StorageService } from '../../common/services/storage.service'; import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.service'; import { OidcAuthenticationService } from '../oidc/oidc-authentication.service'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class AuthGuardService { - constructor( - private authenticationService: AuthenticationService, - private basicAlfrescoAuthService: BasicAlfrescoAuthService, - private oidcAuthenticationService: OidcAuthenticationService, - private router: Router, - private appConfigService: AppConfigService, - private dialog: MatDialog, - private storageService: StorageService - ) {} + private readonly authenticationService = inject(AuthenticationService); + private readonly basicAlfrescoAuthService = inject(BasicAlfrescoAuthService); + private readonly oidcAuthenticationService = inject(OidcAuthenticationService); + private readonly router = inject(Router); + private readonly appConfigService = inject(AppConfigService); + private readonly dialog = inject(MatDialog); + private readonly storageService = inject(StorageService); get withCredentials(): boolean { return this.appConfigService.get<boolean>('auth.withCredentials', false); diff --git a/lib/core/src/lib/auth/oidc/auth-config.service.spec.ts b/lib/core/src/lib/auth/oidc/auth-config.service.spec.ts index 56a44e3654..2051492557 100644 --- a/lib/core/src/lib/auth/oidc/auth-config.service.spec.ts +++ b/lib/core/src/lib/auth/oidc/auth-config.service.spec.ts @@ -15,7 +15,8 @@ * limitations under the License. */ -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; import { AppConfigService } from '../../app-config/app-config.service'; @@ -90,8 +91,7 @@ describe('AuthConfigService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [{ provide: AUTH_MODULE_CONFIG, useValue: { useHash: true } }] + providers: [{ provide: AUTH_MODULE_CONFIG, useValue: { useHash: true } }, provideHttpClient(), provideHttpClientTesting()] }); service = TestBed.inject(AuthConfigService); spyOn<any>(service, 'getLocationOrigin').and.returnValue('http://localhost:3000'); diff --git a/lib/core/src/lib/auth/oidc/auth-config.service.ts b/lib/core/src/lib/auth/oidc/auth-config.service.ts index 40cf4c1eb9..e496d8b9f0 100644 --- a/lib/core/src/lib/auth/oidc/auth-config.service.ts +++ b/lib/core/src/lib/auth/oidc/auth-config.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Inject, Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AuthConfig } from 'angular-oauth2-oidc'; import { take } from 'rxjs/operators'; import { AppConfigService } from '../../app-config/app-config.service'; @@ -37,12 +37,10 @@ export function authConfigFactory(authConfigService: AuthConfigService): Promise providedIn: 'root' }) export class AuthConfigService { - constructor( - private appConfigService: AppConfigService, - @Inject(AUTH_MODULE_CONFIG) private readonly authModuleConfig: AuthModuleConfig - ) {} + private readonly appConfigService = inject(AppConfigService); + private readonly authModuleConfig = inject<AuthModuleConfig>(AUTH_MODULE_CONFIG); - private _authConfig!: AuthConfig; + private readonly _authConfig!: AuthConfig; get authConfig(): AuthConfig { return this._authConfig; } diff --git a/lib/core/src/lib/auth/oidc/oidc-authentication.service.ts b/lib/core/src/lib/auth/oidc/oidc-authentication.service.ts index 0e413fef35..dc7b2e3d8a 100644 --- a/lib/core/src/lib/auth/oidc/oidc-authentication.service.ts +++ b/lib/core/src/lib/auth/oidc/oidc-authentication.service.ts @@ -33,6 +33,11 @@ import { HttpHeaders } from '@angular/common/http'; providedIn: 'root' }) export class OidcAuthenticationService extends BaseAuthenticationService { + private readonly jwtHelperService = inject(JwtHelperService); + private readonly authStorage = inject(OAuthStorage); + private readonly oauthService = inject(OAuthService); + private readonly authConfig = inject(AuthConfigService); + private readonly auth = inject(AuthService); /** @@ -46,14 +51,10 @@ export class OidcAuthenticationService extends BaseAuthenticationService { map(([authenticated, isDiscoveryDocumentLoaded]) => !authenticated && isDiscoveryDocumentLoaded) ); - constructor( - appConfig: AppConfigService, - cookie: CookieService, - private jwtHelperService: JwtHelperService, - private authStorage: OAuthStorage, - private oauthService: OAuthService, - private readonly authConfig: AuthConfigService - ) { + constructor() { + const appConfig = inject(AppConfigService); + const cookie = inject(CookieService); + super(appConfig, cookie); } diff --git a/lib/core/src/lib/auth/oidc/redirect-auth.service.ts b/lib/core/src/lib/auth/oidc/redirect-auth.service.ts index 771804a096..fb06528ea4 100644 --- a/lib/core/src/lib/auth/oidc/redirect-auth.service.ts +++ b/lib/core/src/lib/auth/oidc/redirect-auth.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Inject, Injectable, inject } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AuthConfig, AUTH_CONFIG, @@ -40,12 +40,15 @@ const isPromise = <T>(value: T | Promise<T>): value is Promise<T> => value && ty @Injectable() export class RedirectAuthService extends AuthService { + private readonly oauthService = inject(OAuthService); + private readonly _oauthStorage = inject(OAuthStorage); + readonly authModuleConfig: AuthModuleConfig = inject(AUTH_MODULE_CONFIG); private readonly _retryLoginService: RetryLoginService = inject(RetryLoginService); private readonly _oauthLogger: OAuthLogger = inject(OAuthLogger); private readonly _timeSyncService: TimeSyncService = inject(TimeSyncService); - private _isDiscoveryDocumentLoadedSubject$ = new ReplaySubject<boolean>(); + private readonly _isDiscoveryDocumentLoadedSubject$ = new ReplaySubject<boolean>(); public isDiscoveryDocumentLoaded$ = this._isDiscoveryDocumentLoadedSubject$.asObservable(); onLogin: Observable<any>; @@ -119,7 +122,7 @@ export class RedirectAuthService extends AuthService { return this.oauthService.hasValidIdToken() && this.oauthService.hasValidAccessToken(); } - private authConfig!: AuthConfig | Promise<AuthConfig>; + private readonly authConfig!: AuthConfig | Promise<AuthConfig>; private readonly AUTH_STORAGE_ITEMS: string[] = [ 'access_token', @@ -136,8 +139,11 @@ export class RedirectAuthService extends AuthService { 'session_state' ]; - constructor(private oauthService: OAuthService, private _oauthStorage: OAuthStorage, @Inject(AUTH_CONFIG) authConfig: AuthConfig) { + constructor() { + const authConfig = inject<AuthConfig>(AUTH_CONFIG); + super(); + const oauthService = this.oauthService; this.authConfig = authConfig; diff --git a/lib/core/src/lib/auth/oidc/retry-login.service.ts b/lib/core/src/lib/auth/oidc/retry-login.service.ts index 40a6ab4f23..56270d8202 100644 --- a/lib/core/src/lib/auth/oidc/retry-login.service.ts +++ b/lib/core/src/lib/auth/oidc/retry-login.service.ts @@ -22,7 +22,7 @@ import { LoginOptions, OAuthErrorEvent, OAuthService } from 'angular-oauth2-oidc providedIn: 'root' }) export class RetryLoginService { - private oauthService = inject(OAuthService); + private readonly oauthService = inject(OAuthService); /** * Attempts to log in a specified number of times if the initial login attempt fails. diff --git a/lib/core/src/lib/auth/oidc/token.interceptor.spec.ts b/lib/core/src/lib/auth/oidc/token.interceptor.spec.ts index 7932cfd7a4..b6e6e3f3dc 100644 --- a/lib/core/src/lib/auth/oidc/token.interceptor.spec.ts +++ b/lib/core/src/lib/auth/oidc/token.interceptor.spec.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { HTTP_INTERCEPTORS, HttpClient, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; import { TokenInterceptor } from './token.interceptor'; @@ -38,7 +38,6 @@ describe('TokenInterceptor', () => { }; TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], providers: [ { provide: OAuthService, useValue: oauthServiceMock }, { provide: OAuthStorage, useValue: oauthStorageMock }, @@ -46,7 +45,9 @@ describe('TokenInterceptor', () => { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true - } + }, + provideHttpClient(withInterceptorsFromDi()), + provideHttpClientTesting() ] }); diff --git a/lib/core/src/lib/auth/services/authentication.service.spec.ts b/lib/core/src/lib/auth/services/authentication.service.spec.ts index 7a3d6ef57b..4b9e1f0770 100644 --- a/lib/core/src/lib/auth/services/authentication.service.spec.ts +++ b/lib/core/src/lib/auth/services/authentication.service.spec.ts @@ -18,6 +18,7 @@ import { TestBed } from '@angular/core/testing'; import { AuthenticationService } from './authentication.service'; import { CookieService } from '../../common/services/cookie.service'; +import { Injector, runInInjectionContext } from '@angular/core'; import { AppConfigService } from '../../app-config/app-config.service'; import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.service'; import { provideCoreAuth } from '../oidc/auth.module'; @@ -28,7 +29,6 @@ import { OidcAuthenticationService } from '../oidc/oidc-authentication.service'; import { OAuthEvent } from 'angular-oauth2-oidc'; import { firstValueFrom, of, Subject, throwError } from 'rxjs'; import { RedirectAuthService } from '../oidc/redirect-auth.service'; -import { Injector } from '@angular/core'; import { ContentAuth, ProcessAuth } from '../public-api'; declare let jasmine: any; @@ -335,8 +335,15 @@ describe('AuthenticationService', () => { redirectAuthService = TestBed.inject(RedirectAuthService); redirectAuthService.onTokenReceived = onTokenReceived$; - const injector = TestBed.inject(Injector); - authenticationService = new AuthenticationService(injector, redirectAuthService); + const injector = Injector.create({ + providers: [ + { provide: Injector, useValue: TestBed.inject(Injector) }, + { provide: RedirectAuthService, useValue: redirectAuthService } + ], + parent: TestBed.inject(Injector) + }); + + authenticationService = runInInjectionContext(injector, () => new AuthenticationService()); }); it('should emit event when RedirectAuthService onTokenReceived emits', () => { diff --git a/lib/core/src/lib/auth/services/authentication.service.ts b/lib/core/src/lib/auth/services/authentication.service.ts index 2390a05668..199aeb49d5 100644 --- a/lib/core/src/lib/auth/services/authentication.service.ts +++ b/lib/core/src/lib/auth/services/authentication.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { OidcAuthenticationService } from '../oidc/oidc-authentication.service'; import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.service'; import { Observable, Subject, from } from 'rxjs'; @@ -28,14 +28,14 @@ type EventEmitterInstance = InstanceType<typeof EventEmitter>; providedIn: 'root' }) export class AuthenticationService implements AuthenticationServiceInterface { + private readonly injector = inject(Injector); + private readonly redirectAuthService = inject(RedirectAuthService); + onLogin: Subject<any> = new Subject<any>(); onLogout: Subject<any> = new Subject<any>(); onTokenReceived: Subject<any> = new Subject<any>(); - constructor( - private readonly injector: Injector, - private readonly redirectAuthService: RedirectAuthService - ) { + constructor() { this.redirectAuthService.onLogin.subscribe((value) => this.onLogin.next(value)); this.redirectAuthService.onTokenReceived.subscribe((value) => this.onTokenReceived.next(value)); diff --git a/lib/core/src/lib/auth/services/identity-group.service.ts b/lib/core/src/lib/auth/services/identity-group.service.ts index 49d5b0726c..6e38d3c13b 100644 --- a/lib/core/src/lib/auth/services/identity-group.service.ts +++ b/lib/core/src/lib/auth/services/identity-group.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { AppConfigService } from '../../app-config/app-config.service'; @@ -32,7 +32,8 @@ import { OAuth2Service } from './oauth2.service'; @Injectable({ providedIn: 'root' }) export class IdentityGroupService implements IdentityGroupServiceInterface { - constructor(private oAuth2Service: OAuth2Service, private appConfigService: AppConfigService) {} + private readonly oAuth2Service = inject(OAuth2Service); + private readonly appConfigService = inject(AppConfigService); private get identityHost(): string { return `${this.appConfigService.get('identityHost')}`; @@ -133,7 +134,7 @@ export class IdentityGroupService implements IdentityGroupServiceInterface { hasMoreItems: false, totalItems: totalCount.count } - } as IdentityGroupQueryResponse) + }) as IdentityGroupQueryResponse ) ) ) diff --git a/lib/core/src/lib/auth/services/identity-role.service.ts b/lib/core/src/lib/auth/services/identity-role.service.ts index bfaf3066cb..351c4385f7 100644 --- a/lib/core/src/lib/auth/services/identity-role.service.ts +++ b/lib/core/src/lib/auth/services/identity-role.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, of } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -32,10 +32,13 @@ export interface IdentityRoleResponseModel { providedIn: 'root' }) export class IdentityRoleService { + protected http = inject(HttpClient); + protected appConfig = inject(AppConfigService); + contextRoot = ''; identityHost = ''; - constructor(protected http: HttpClient, protected appConfig: AppConfigService) { + constructor() { this.contextRoot = this.appConfig.get('apiHost', ''); this.identityHost = this.appConfig.get('identityHost'); } diff --git a/lib/core/src/lib/auth/services/identity-user.service.ts b/lib/core/src/lib/auth/services/identity-user.service.ts index e0a031bcc0..ac580a8903 100644 --- a/lib/core/src/lib/auth/services/identity-user.service.ts +++ b/lib/core/src/lib/auth/services/identity-user.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { firstValueFrom, Observable, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { AppConfigService } from '../../app-config/app-config.service'; @@ -36,11 +36,9 @@ import { OAuth2Service } from './oauth2.service'; providedIn: 'root' }) export class IdentityUserService implements IdentityUserServiceInterface { - constructor( - private jwtHelperService: JwtHelperService, - private oAuth2Service: OAuth2Service, - private appConfigService: AppConfigService - ) {} + private readonly jwtHelperService = inject(JwtHelperService); + private readonly oAuth2Service = inject(OAuth2Service); + private readonly appConfigService = inject(AppConfigService); private get identityHost(): string { return `${this.appConfigService.get('identityHost')}`; diff --git a/lib/core/src/lib/auth/services/jwt-helper.service.ts b/lib/core/src/lib/auth/services/jwt-helper.service.ts index f71ee2d065..2ef298a0df 100644 --- a/lib/core/src/lib/auth/services/jwt-helper.service.ts +++ b/lib/core/src/lib/auth/services/jwt-helper.service.ts @@ -33,7 +33,7 @@ export class JwtHelperService { static USER_PREFERRED_USERNAME = 'preferred_username'; static HXP_AUTHORIZATION = 'hxp_authorization'; - private storageService: OAuthStorage = inject(OAuthStorage); + private readonly storageService: OAuthStorage = inject(OAuthStorage); /** * Decodes a JSON web token into a JS object. diff --git a/lib/core/src/lib/auth/services/oauth2.service.ts b/lib/core/src/lib/auth/services/oauth2.service.ts index c8e791c5a9..e6022ac6be 100644 --- a/lib/core/src/lib/auth/services/oauth2.service.ts +++ b/lib/core/src/lib/auth/services/oauth2.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from } from 'rxjs'; import { AdfHttpClient } from '@alfresco/adf-core/api'; @@ -31,7 +31,7 @@ export interface OAuth2RequestParams { @Injectable({ providedIn: 'root' }) export class OAuth2Service { - constructor(private adfHttpClient: AdfHttpClient) {} + private readonly adfHttpClient = inject(AdfHttpClient); request<T>(opts: OAuth2RequestParams): Observable<T> { const { httpMethod, url, bodyParam, queryParams } = opts; diff --git a/lib/core/src/lib/auth/services/time-sync.service.spec.ts b/lib/core/src/lib/auth/services/time-sync.service.spec.ts index d6d7157a81..a2c8d6b66a 100644 --- a/lib/core/src/lib/auth/services/time-sync.service.spec.ts +++ b/lib/core/src/lib/auth/services/time-sync.service.spec.ts @@ -15,7 +15,8 @@ * limitations under the License. */ -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { AppConfigService } from '../../app-config/app-config.service'; import { TimeSyncService } from './time-sync.service'; @@ -30,8 +31,7 @@ describe('TimeSyncService', () => { appConfigSpy = jasmine.createSpyObj('AppConfigService', ['get']); TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [TimeSyncService, { provide: AppConfigService, useValue: appConfigSpy }] + providers: [TimeSyncService, { provide: AppConfigService, useValue: appConfigSpy }, provideHttpClient(), provideHttpClientTesting()] }); service = TestBed.inject(TimeSyncService); diff --git a/lib/core/src/lib/auth/services/time-sync.service.ts b/lib/core/src/lib/auth/services/time-sync.service.ts index 58d71aba17..a806d17777 100644 --- a/lib/core/src/lib/auth/services/time-sync.service.ts +++ b/lib/core/src/lib/auth/services/time-sync.service.ts @@ -16,7 +16,7 @@ */ import { HttpClient } from '@angular/common/http'; -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, inject } from '@angular/core'; import { AppConfigService } from '../../app-config/app-config.service'; import { from, Observable, throwError } from 'rxjs'; import { catchError, map, timeout } from 'rxjs/operators'; @@ -32,9 +32,12 @@ export interface TimeSync { providedIn: 'root' }) export class TimeSyncService { + private readonly _injector = inject(Injector); + private readonly _appConfigService = inject(AppConfigService); + private readonly _http: HttpClient; - constructor(private _injector: Injector, private _appConfigService: AppConfigService) { + constructor() { this._http = this._injector.get(HttpClient); } diff --git a/lib/core/src/lib/auth/services/user-access.service.spec.ts b/lib/core/src/lib/auth/services/user-access.service.spec.ts index 3c752c5cba..908701dafb 100644 --- a/lib/core/src/lib/auth/services/user-access.service.spec.ts +++ b/lib/core/src/lib/auth/services/user-access.service.spec.ts @@ -19,7 +19,8 @@ import { TestBed } from '@angular/core/testing'; import { UserAccessService } from './user-access.service'; import { JwtHelperService } from './jwt-helper.service'; import { AppConfigService } from '../../app-config'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { JWT_STORAGE_SERVICE, provideCoreAuth } from '../oidc/auth.module'; import { StorageService } from '../../common/services/storage.service'; @@ -30,8 +31,12 @@ describe('UserAccessService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [provideCoreAuth({ useHash: true }), { provide: JWT_STORAGE_SERVICE, useClass: StorageService }] + providers: [ + provideCoreAuth({ useHash: true }), + { provide: JWT_STORAGE_SERVICE, useClass: StorageService }, + provideHttpClient(), + provideHttpClientTesting() + ] }); userAccessService = TestBed.inject(UserAccessService); jwtHelperService = TestBed.inject(JwtHelperService); diff --git a/lib/core/src/lib/auth/services/user-access.service.ts b/lib/core/src/lib/auth/services/user-access.service.ts index 6dc56ba438..f7b9624689 100644 --- a/lib/core/src/lib/auth/services/user-access.service.ts +++ b/lib/core/src/lib/auth/services/user-access.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { JwtHelperService } from './jwt-helper.service'; import { ApplicationAccessModel } from '../models/application-access.model'; import { AppConfigService } from '../../app-config/app-config.service'; @@ -24,11 +24,12 @@ import { AppConfigService } from '../../app-config/app-config.service'; providedIn: 'root' }) export class UserAccessService { + private readonly jwtHelperService = inject(JwtHelperService); + private readonly appConfigService = inject(AppConfigService); + private globalAccess: string[]; private applicationAccess: ApplicationAccessModel[]; - constructor(private jwtHelperService: JwtHelperService, private appConfigService: AppConfigService) {} - fetchUserAccess() { if (this.hasRolesInRealmAccess()) { this.fetchAccessFromRealmAccess(); diff --git a/lib/core/src/lib/card-view/components/card-view-dateitem/card-view-dateitem.component.ts b/lib/core/src/lib/card-view/components/card-view-dateitem/card-view-dateitem.component.ts index 3241f6b91f..4f77c1e698 100644 --- a/lib/core/src/lib/card-view/components/card-view-dateitem/card-view-dateitem.component.ts +++ b/lib/core/src/lib/card-view/components/card-view-dateitem/card-view-dateitem.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, effect, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, effect, Input, OnInit, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { DatetimeAdapter, @@ -69,6 +69,11 @@ import { IconModule } from '../../../icon/icon.module'; host: { class: 'adf-card-view-dateitem' } }) export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemModel> implements OnInit { + private readonly dateAdapter = inject<DateAdapter<Date>>(DateAdapter); + private readonly userPreferencesService = inject(UserPreferencesService); + private readonly clipboardService = inject(ClipboardService); + private readonly translateService = inject(TranslationService); + @Input() displayEmpty = true; @@ -82,12 +87,7 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode cardViewDateTimeControl: FormControl<Date> = new FormControl<Date>(null); - constructor( - private dateAdapter: DateAdapter<Date>, - private userPreferencesService: UserPreferencesService, - private clipboardService: ClipboardService, - private translateService: TranslationService - ) { + constructor() { super(); // Use effect to react to locale signal changes (must be in injection context) effect(() => { diff --git a/lib/core/src/lib/card-view/components/card-view-item-dispatcher/card-view-item-dispatcher.component.ts b/lib/core/src/lib/card-view/components/card-view-item-dispatcher/card-view-item-dispatcher.component.ts index 79a926c3d0..d1941ad855 100644 --- a/lib/core/src/lib/card-view/components/card-view-item-dispatcher/card-view-item-dispatcher.component.ts +++ b/lib/core/src/lib/card-view/components/card-view-item-dispatcher/card-view-item-dispatcher.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Input, OnChanges, SimpleChange, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core'; +import { Component, Input, OnChanges, SimpleChange, SimpleChanges, ViewChild, ViewContainerRef, inject } from '@angular/core'; import { CardViewItem } from '../../interfaces/card-view-item.interface'; import { CardItemTypeService } from '../../services/card-item-types.service'; import { DEFAULT_SEPARATOR } from '../card-view-textitem/card-view-textitem.component'; @@ -25,6 +25,8 @@ import { DEFAULT_SEPARATOR } from '../card-view-textitem/card-view-textitem.comp template: '<ng-template #content />' }) export class CardViewItemDispatcherComponent implements OnChanges { + private readonly cardItemTypeService = inject(CardItemTypeService); + @Input() property: CardViewItem; @@ -58,7 +60,7 @@ export class CardViewItemDispatcherComponent implements OnChanges { @ViewChild('content', { read: ViewContainerRef, static: true }) content!: ViewContainerRef; - constructor(private cardItemTypeService: CardItemTypeService) { + constructor() { const dynamicLifeCycleMethods = [ 'ngOnInit', 'ngDoCheck', diff --git a/lib/core/src/lib/card-view/components/card-view-selectitem/select-filter-input/select-filter-input.component.ts b/lib/core/src/lib/card-view/components/card-view-selectitem/select-filter-input/select-filter-input.component.ts index ac55499af4..e7216ff9dd 100644 --- a/lib/core/src/lib/card-view/components/card-view-selectitem/select-filter-input/select-filter-input.component.ts +++ b/lib/core/src/lib/card-view/components/card-view-selectitem/select-filter-input/select-filter-input.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, DestroyRef, ElementRef, EventEmitter, Inject, inject, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, ElementRef, EventEmitter, inject, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; import { MatSelect } from '@angular/material/select'; import { CommonModule } from '@angular/common'; import { MatFormFieldModule } from '@angular/material/form-field'; @@ -35,6 +35,8 @@ import { IconModule } from '../../../../icon/icon.module'; encapsulation: ViewEncapsulation.None }) export class SelectFilterInputComponent implements OnInit { + private readonly matSelect = inject<MatSelect>(MatSelect); + @ViewChild('selectFilterInput', { read: ElementRef, static: false }) selectFilterInput: ElementRef; @Output() change = new EventEmitter<string>(); @@ -43,8 +45,6 @@ export class SelectFilterInputComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor(@Inject(MatSelect) private matSelect: MatSelect) {} - onModelChange(value: string) { this.change.next(value); } diff --git a/lib/core/src/lib/card-view/components/card-view-textitem/card-view-textitem.component.ts b/lib/core/src/lib/card-view/components/card-view-textitem/card-view-textitem.component.ts index e9f0725fd1..3294e996e4 100644 --- a/lib/core/src/lib/card-view/components/card-view-textitem/card-view-textitem.component.ts +++ b/lib/core/src/lib/card-view/components/card-view-textitem/card-view-textitem.component.ts @@ -61,6 +61,10 @@ const templateTypes = { host: { class: 'adf-card-view-textitem' } }) export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemModel> implements OnChanges { + private readonly clipboardService = inject(ClipboardService); + private readonly translateService = inject(TranslationService); + private readonly cd = inject(ChangeDetectorRef); + @Input() displayEmpty = true; @@ -80,14 +84,6 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode private readonly destroyRef = inject(DestroyRef); - constructor( - private clipboardService: ClipboardService, - private translateService: TranslationService, - private cd: ChangeDetectorRef - ) { - super(); - } - ngOnChanges(changes: SimpleChanges): void { if (changes.property?.firstChange) { this.textInput.valueChanges diff --git a/lib/core/src/lib/card-view/directives/card-view-property-validator.directive.ts b/lib/core/src/lib/card-view/directives/card-view-property-validator.directive.ts index 99a72f6181..38522b32e9 100644 --- a/lib/core/src/lib/card-view/directives/card-view-property-validator.directive.ts +++ b/lib/core/src/lib/card-view/directives/card-view-property-validator.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, EventEmitter, forwardRef, Input, Output } from '@angular/core'; +import { Directive, EventEmitter, forwardRef, Input, Output, inject } from '@angular/core'; import { AbstractControl, NG_VALIDATORS, ValidationErrors } from '@angular/forms'; import { CardViewBaseItemModel } from '../models/card-view-baseitem.model'; import { TranslateService } from '@ngx-translate/core'; @@ -31,14 +31,14 @@ import { TranslateService } from '@ngx-translate/core'; ] }) export class CardViewPropertyValidatorDirective { + private readonly translateService = inject(TranslateService); + @Input() property: CardViewBaseItemModel; @Output() validated = new EventEmitter<string[]>(); - constructor(private readonly translateService: TranslateService) {} - validate(control: AbstractControl): ValidationErrors | null { const errors: ValidationErrors | null = this.property.isValid(control.value) ? null diff --git a/lib/core/src/lib/card-view/models/card-view-dateitem.model.ts b/lib/core/src/lib/card-view/models/card-view-dateitem.model.ts index 97a72a3e9a..398626a0dd 100644 --- a/lib/core/src/lib/card-view/models/card-view-dateitem.model.ts +++ b/lib/core/src/lib/card-view/models/card-view-dateitem.model.ts @@ -19,7 +19,7 @@ import { CardViewItem } from '../interfaces/card-view-item.interface'; import { DynamicComponentModel } from '../../common/services/dynamic-component-mapper.service'; import { CardViewBaseItemModel } from './card-view-baseitem.model'; import { CardViewDateItemProperties } from '../interfaces/card-view.interfaces'; -import { LocalizedDatePipe } from '../../pipes/localized-date.pipe'; +import { DatePipe } from '@angular/common'; import { DateFnsUtils } from '../../common/utils/date-fns-utils'; type DateItemType = Date | Date[] | null; @@ -29,8 +29,6 @@ export class CardViewDateItemModel extends CardViewBaseItemModel<DateItemType> i format: string; locale: string; - localizedDatePipe: LocalizedDatePipe; - constructor(cardViewDateItemProperties: CardViewDateItemProperties) { super(cardViewDateItemProperties); @@ -56,8 +54,10 @@ export class CardViewDateItemModel extends CardViewBaseItemModel<DateItemType> i } transformDate(value: Date | string | number): string { - this.localizedDatePipe = new LocalizedDatePipe(); - return this.localizedDatePipe.transform(value, this.format, this.locale); + const actualFormat = this.format || 'mediumDate'; + const actualLocale = this.locale || 'en-US'; + const datePipe = new DatePipe(actualLocale); + return datePipe.transform(value, actualFormat); } private prepareDate(date: Date): Date { diff --git a/lib/core/src/lib/card-view/validators/card-view-item-length.validator.ts b/lib/core/src/lib/card-view/validators/card-view-item-length.validator.ts index a518534ed5..2f112cbc18 100644 --- a/lib/core/src/lib/card-view/validators/card-view-item-length.validator.ts +++ b/lib/core/src/lib/card-view/validators/card-view-item-length.validator.ts @@ -25,7 +25,10 @@ export interface LengthValidatorParams { export class CardViewItemLengthValidator implements CardViewItemValidator { message = 'CORE.CARDVIEW.VALIDATORS.LENGTH_VALIDATION_ERROR'; - constructor(private minLength: number, private maxLength: number) {} + constructor( + private readonly minLength: number, + private readonly maxLength: number + ) {} isValid(value: string | string[]): boolean { if (Array.isArray(value)) { diff --git a/lib/core/src/lib/card-view/validators/card-view-item-match.validator.ts b/lib/core/src/lib/card-view/validators/card-view-item-match.validator.ts index 5a82d04fae..1b10ec458d 100644 --- a/lib/core/src/lib/card-view/validators/card-view-item-match.validator.ts +++ b/lib/core/src/lib/card-view/validators/card-view-item-match.validator.ts @@ -26,7 +26,11 @@ export interface MatchValidatorParams { export class CardViewItemMatchValidator implements CardViewItemValidator { message = 'CORE.CARDVIEW.VALIDATORS.MATCH_VALIDATION_ERROR'; - constructor(private expression: string, private flags?: string, private requiresMatch?: boolean) {} + constructor( + private readonly expression: string, + private readonly flags?: string, + private readonly requiresMatch?: boolean + ) {} isValid(value: string | string[]): boolean { const regex = new RegExp(this.expression, this?.flags); diff --git a/lib/core/src/lib/card-view/validators/card-view-item-minmax.validator.ts b/lib/core/src/lib/card-view/validators/card-view-item-minmax.validator.ts index d55e8b4be0..9f005c3cdb 100644 --- a/lib/core/src/lib/card-view/validators/card-view-item-minmax.validator.ts +++ b/lib/core/src/lib/card-view/validators/card-view-item-minmax.validator.ts @@ -25,9 +25,12 @@ export interface MinMaxValidatorParams { export class CardViewItemMinMaxValidator implements CardViewItemValidator { message = 'CORE.CARDVIEW.VALIDATORS.MINMAX_VALIDATION_ERROR'; - private intValidator: CardViewItemIntValidator; + private readonly intValidator: CardViewItemIntValidator; - constructor(private minValue: number, private maxValue: number) { + constructor( + private readonly minValue: number, + private readonly maxValue: number + ) { this.intValidator = new CardViewItemIntValidator(); } diff --git a/lib/core/src/lib/clipboard/clipboard.directive.ts b/lib/core/src/lib/clipboard/clipboard.directive.ts index 2ee248a02b..1040c763cd 100644 --- a/lib/core/src/lib/clipboard/clipboard.directive.ts +++ b/lib/core/src/lib/clipboard/clipboard.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, Input, HostListener, ViewContainerRef, Self, Optional } from '@angular/core'; +import { Directive, Input, HostListener, ViewContainerRef, inject } from '@angular/core'; import { ClipboardService } from './clipboard.service'; import { TranslateService } from '@ngx-translate/core'; import { MatTooltip } from '@angular/material/tooltip'; @@ -27,6 +27,11 @@ import { MatTooltip } from '@angular/material/tooltip'; hostDirectives: [MatTooltip] }) export class ClipboardDirective { + private readonly clipboardService = inject(ClipboardService); + viewContainerRef = inject(ViewContainerRef); + private readonly matTooltip = inject(MatTooltip, { self: true }); + private readonly translate = inject(TranslateService, { optional: true }); + /** Translation key or message for the tooltip. */ // eslint-disable-next-line @angular-eslint/no-input-rename @Input('adf-clipboard') @@ -40,13 +45,6 @@ export class ClipboardDirective { // eslint-disable-next-line @angular-eslint/no-input-rename @Input('clipboard-notification') message: string; - constructor( - private readonly clipboardService: ClipboardService, - public viewContainerRef: ViewContainerRef, - @Self() private readonly matTooltip: MatTooltip, - @Optional() private readonly translate: TranslateService - ) {} - @HostListener('mouseenter') showTooltip() { const messageKey = this.placeholder || 'CLIPBOARD.CLICK_TO_COPY'; diff --git a/lib/core/src/lib/clipboard/clipboard.service.ts b/lib/core/src/lib/clipboard/clipboard.service.ts index efc931366f..cfa0659689 100644 --- a/lib/core/src/lib/clipboard/clipboard.service.ts +++ b/lib/core/src/lib/clipboard/clipboard.service.ts @@ -15,13 +15,14 @@ * limitations under the License. */ -import { Injectable, Inject } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { DOCUMENT } from '@angular/common'; import { NotificationService } from '../notifications/services/notification.service'; @Injectable({ providedIn: 'root' }) export class ClipboardService { - constructor(@Inject(DOCUMENT) private document: any, private notificationService: NotificationService) {} + private readonly document = inject(DOCUMENT); + private readonly notificationService = inject(NotificationService); /** * Checks if the target element can have its text copied. diff --git a/lib/core/src/lib/comments/comment-list/comment-list.component.ts b/lib/core/src/lib/comments/comment-list/comment-list.component.ts index 68ea8594c3..c736fad16e 100644 --- a/lib/core/src/lib/comments/comment-list/comment-list.component.ts +++ b/lib/core/src/lib/comments/comment-list/comment-list.component.ts @@ -41,7 +41,7 @@ export class CommentListComponent { @Output() clickRow = new EventEmitter<CommentModel>(); - private commentsService = inject<CommentsService>(ADF_COMMENTS_SERVICE); + private readonly commentsService = inject<CommentsService>(ADF_COMMENTS_SERVICE); selectComment(comment: CommentModel): void { this.clickRow.emit(comment); diff --git a/lib/core/src/lib/comments/comments.component.ts b/lib/core/src/lib/comments/comments.component.ts index 70c001d1cf..3461720c7f 100644 --- a/lib/core/src/lib/comments/comments.component.ts +++ b/lib/core/src/lib/comments/comments.component.ts @@ -16,17 +16,7 @@ */ import { CommentModel } from '../models/comment.model'; -import { - Component, ElementRef, - EventEmitter, - inject, - Input, - OnChanges, - Output, - SimpleChanges, - ViewChild, - ViewEncapsulation -} from '@angular/core'; +import { Component, ElementRef, EventEmitter, inject, Input, OnChanges, Output, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core'; import { ADF_COMMENTS_SERVICE } from './interfaces/comments.token'; import { CommentsService } from './interfaces/comments-service.interface'; import { CommonModule } from '@angular/common'; @@ -75,7 +65,7 @@ export class CommentsComponent implements OnChanges { comments: CommentModel[] = []; beingAdded: boolean = false; - private commentsService = inject<CommentsService>(ADF_COMMENTS_SERVICE); + private readonly commentsService = inject<CommentsService>(ADF_COMMENTS_SERVICE); private readonly _commentControl = new FormControl('', [this.validateEmptyComment]); diff --git a/lib/core/src/lib/common/services/log.service.spec.ts b/lib/core/src/lib/common/services/log.service.spec.ts index e6d468da08..8280d81472 100644 --- a/lib/core/src/lib/common/services/log.service.spec.ts +++ b/lib/core/src/lib/common/services/log.service.spec.ts @@ -18,7 +18,7 @@ /* eslint-disable no-console */ import { HttpClientModule } from '@angular/common/http'; -import { Component } from '@angular/core'; +import { Component, inject as inject_1 } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; import { AppConfigService } from '../../app-config/app-config.service'; import { LogService } from './log.service'; @@ -28,7 +28,7 @@ import { LogService } from './log.service'; providers: [LogService] }) class ProvidesLogComponent { - constructor(public logService: LogService) {} + logService = inject_1(LogService); error() { this.logService.error('Test message'); diff --git a/lib/core/src/lib/common/services/log.service.ts b/lib/core/src/lib/common/services/log.service.ts index 481623b7b2..d7c3aa799c 100644 --- a/lib/core/src/lib/common/services/log.service.ts +++ b/lib/core/src/lib/common/services/log.service.ts @@ -17,18 +17,17 @@ /* eslint-disable no-console */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service'; import { logLevels, LogLevelsEnum } from '../models/log-levels.model'; import { Subject } from 'rxjs'; -/** - * @deprecated This service is deprecated and will be removed in future versions. - */ @Injectable({ providedIn: 'root' }) export class LogService { + private readonly appConfig = inject(AppConfigService); + get currentLogLevel(): number { const configLevel: string = this.appConfig.get<string>(AppConfigValues.LOG_LEVEL); @@ -41,7 +40,7 @@ export class LogService { onMessage: Subject<any>; - constructor(private appConfig: AppConfigService) { + constructor() { this.onMessage = new Subject(); } diff --git a/lib/core/src/lib/common/services/page-title.service.ts b/lib/core/src/lib/common/services/page-title.service.ts index cfad5c19f4..63165dd890 100644 --- a/lib/core/src/lib/common/services/page-title.service.ts +++ b/lib/core/src/lib/common/services/page-title.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { AppConfigService } from '../../app-config/app-config.service'; import { TranslationService } from '../../translation/translation.service'; @@ -24,10 +24,16 @@ import { TranslationService } from '../../translation/translation.service'; providedIn: 'root' }) export class PageTitleService { + private readonly titleService = inject(Title); + private readonly appConfig = inject(AppConfigService); + private readonly translationService = inject(TranslationService); + private originalTitle: string = ''; private translatedTitle: string = ''; - constructor(private titleService: Title, private appConfig: AppConfigService, private translationService: TranslationService) { + constructor() { + const translationService = this.translationService; + translationService.translate.onLangChange.subscribe(() => this.onLanguageChanged()); translationService.translate.onTranslationChange.subscribe(() => this.onLanguageChanged()); } diff --git a/lib/core/src/lib/common/services/thumbnail.service.ts b/lib/core/src/lib/common/services/thumbnail.service.ts index 304cb07a0e..08b4bf3057 100644 --- a/lib/core/src/lib/common/services/thumbnail.service.ts +++ b/lib/core/src/lib/common/services/thumbnail.service.ts @@ -16,7 +16,7 @@ */ /* spellchecker: disable */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { MatIconRegistry } from '@angular/material/icon'; import { DomSanitizer } from '@angular/platform-browser'; @@ -161,7 +161,10 @@ export class ThumbnailService { 'multipart/related': './assets/images/ft_ic_website.svg' }; - constructor(matIconRegistry: MatIconRegistry, sanitizer: DomSanitizer) { + constructor() { + const matIconRegistry = inject(MatIconRegistry); + const sanitizer = inject(DomSanitizer); + Object.keys(this.mimeTypeIcons).forEach((key) => { const url = sanitizer.bypassSecurityTrustResourceUrl(this.mimeTypeIcons[key]); diff --git a/lib/core/src/lib/common/services/url.service.ts b/lib/core/src/lib/common/services/url.service.ts index 68e364bac7..3507e40e8a 100644 --- a/lib/core/src/lib/common/services/url.service.ts +++ b/lib/core/src/lib/common/services/url.service.ts @@ -15,14 +15,14 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; @Injectable({ providedIn: 'root' }) export class UrlService { - constructor(private sanitizer: DomSanitizer) {} + private readonly sanitizer = inject(DomSanitizer); /** * Creates a trusted object URL from the Blob. diff --git a/lib/core/src/lib/common/services/user-preferences.service.ts b/lib/core/src/lib/common/services/user-preferences.service.ts index 2c5043941d..5577feb04d 100644 --- a/lib/core/src/lib/common/services/user-preferences.service.ts +++ b/lib/core/src/lib/common/services/user-preferences.service.ts @@ -40,9 +40,13 @@ export type UserPreferenceValues = (typeof UserPreferenceValues)[keyof typeof Us providedIn: 'root' }) export class UserPreferencesService { - private document = inject(DOCUMENT); - private rendererFactory = inject(RendererFactory2); - private directionality = inject(Directionality); + translate = inject(TranslateService); + private readonly appConfig = inject(AppConfigService); + private readonly storage = inject(StorageService); + + private readonly document = inject(DOCUMENT); + private readonly rendererFactory = inject(RendererFactory2); + private readonly directionality = inject(Directionality); defaults = { paginationSize: 25, @@ -52,7 +56,7 @@ export class UserPreferencesService { }; private userPreferenceStatus: any = { ...this.defaults }; - private onChangeSubject: BehaviorSubject<any>; + private readonly onChangeSubject: BehaviorSubject<any>; onChange: Observable<any>; /** @@ -107,11 +111,7 @@ export class UserPreferencesService { */ readonly supportedPageSizesSignal: Signal<number[]>; - constructor( - public translate: TranslateService, - private appConfig: AppConfigService, - private storage: StorageService - ) { + constructor() { this.onChangeSubject = new BehaviorSubject(this.userPreferenceStatus); this.onChange = this.onChangeSubject.asObservable(); diff --git a/lib/core/src/lib/common/utils/date-fns-adapter.ts b/lib/core/src/lib/common/utils/date-fns-adapter.ts index a951f4b394..3b48082a36 100644 --- a/lib/core/src/lib/common/utils/date-fns-adapter.ts +++ b/lib/core/src/lib/common/utils/date-fns-adapter.ts @@ -17,7 +17,7 @@ import { DateFnsAdapter } from '@angular/material-date-fns-adapter'; import { DateFnsUtils } from './date-fns-utils'; -import { effect, Inject, Injectable, Optional } from '@angular/core'; +import { effect, Injectable, inject } from '@angular/core'; import { MAT_DATE_FORMATS, MAT_DATE_LOCALE, MatDateFormats } from '@angular/material/core'; import { UserPreferencesService } from '../services/user-preferences.service'; import { isValid, Locale, parse } from 'date-fns'; @@ -68,6 +68,8 @@ export const ADF_DATE_FORMATS: MatDateFormats = { @Injectable({ providedIn: 'root' }) export class AdfDateFnsAdapter extends DateFnsAdapter { + private readonly formats = inject<MatDateFormats>(MAT_DATE_FORMATS, { optional: true }); + private _displayFormat?: string = null; get displayFormat(): string | null { @@ -78,11 +80,10 @@ export class AdfDateFnsAdapter extends DateFnsAdapter { this._displayFormat = value ? DateFnsUtils.convertMomentToDateFnsFormat(value) : null; } - constructor( - @Optional() @Inject(MAT_DATE_LOCALE) matDateLocale: Locale, - @Optional() @Inject(MAT_DATE_FORMATS) private formats: MatDateFormats, - preferences: UserPreferencesService - ) { + constructor() { + const matDateLocale = inject<Locale>(MAT_DATE_LOCALE, { optional: true }); + const preferences = inject(UserPreferencesService); + // Ensure we have a valid locale for the base class // If matDateLocale is not provided, use enUS as default super(matDateLocale || enUS); diff --git a/lib/core/src/lib/common/utils/date-fns-utils.ts b/lib/core/src/lib/common/utils/date-fns-utils.ts index c19ceeda9b..3e9e3f4ff1 100644 --- a/lib/core/src/lib/common/utils/date-fns-utils.ts +++ b/lib/core/src/lib/common/utils/date-fns-utils.ts @@ -89,7 +89,7 @@ export class DateFnsUtils { return dateFnsLocale; } - private static momentToDateFnsMap = { + private static readonly momentToDateFnsMap = { D: 'd', Y: 'y', AZ: 'aa', diff --git a/lib/core/src/lib/common/utils/datetime-fns-adapter.ts b/lib/core/src/lib/common/utils/datetime-fns-adapter.ts index d2297afa39..a9e7610d5d 100644 --- a/lib/core/src/lib/common/utils/datetime-fns-adapter.ts +++ b/lib/core/src/lib/common/utils/datetime-fns-adapter.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Inject, Injectable, Optional } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { DateFnsUtils } from './date-fns-utils'; import { DatetimeAdapter, MAT_DATETIME_FORMATS, MatDatetimeFormats } from '@mat-datetimepicker/core'; import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core'; @@ -60,6 +60,8 @@ function range<T>(length: number, valueFunction: (index: number) => T): T[] { @Injectable({ providedIn: 'root' }) export class AdfDateTimeFnsAdapter extends DatetimeAdapter<Date> { + private readonly formats = inject<MatDatetimeFormats>(MAT_DATETIME_FORMATS, { optional: true }); + private _displayFormat?: string = null; get displayFormat(): string | null { @@ -70,11 +72,10 @@ export class AdfDateTimeFnsAdapter extends DatetimeAdapter<Date> { this._displayFormat = value ? DateFnsUtils.convertMomentToDateFnsFormat(value) : null; } - constructor( - @Optional() @Inject(MAT_DATE_LOCALE) matDateLocale: Locale, - @Optional() @Inject(MAT_DATETIME_FORMATS) private formats: MatDatetimeFormats, - dateAdapter: DateAdapter<Date, Locale> - ) { + constructor() { + const matDateLocale = inject<Locale>(MAT_DATE_LOCALE, { optional: true }); + const dateAdapter = inject<DateAdapter<Date, Locale>>(DateAdapter); + super(dateAdapter); this.setLocale(matDateLocale); } diff --git a/lib/core/src/lib/common/utils/moment-date-adapter.ts b/lib/core/src/lib/common/utils/moment-date-adapter.ts index f29a0a4613..808c15963b 100644 --- a/lib/core/src/lib/common/utils/moment-date-adapter.ts +++ b/lib/core/src/lib/common/utils/moment-date-adapter.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { effect, Injectable } from '@angular/core'; +import { effect, Injectable, inject } from '@angular/core'; import { DateAdapter } from '@angular/material/core'; import { UserPreferencesService } from '../services/user-preferences.service'; @@ -34,7 +34,9 @@ export class MomentDateAdapter extends DateAdapter<Moment> { overrideDisplayFormat: string; - constructor(preferences: UserPreferencesService) { + constructor() { + const preferences = inject(UserPreferencesService); + super(); // Use effect to reactively update locale when signal changes diff --git a/lib/core/src/lib/context-menu/context-menu-list.component.ts b/lib/core/src/lib/context-menu/context-menu-list.component.ts index c69917add1..c2a01c7f31 100644 --- a/lib/core/src/lib/context-menu/context-menu-list.component.ts +++ b/lib/core/src/lib/context-menu/context-menu-list.component.ts @@ -21,7 +21,7 @@ import { MatMenuItem, MatMenuModule } from '@angular/material/menu'; import { ContextMenuOverlayRef } from './context-menu-overlay'; import { contextMenuAnimation } from './animations'; import { CONTEXT_MENU_DATA } from './context-menu.tokens'; -import { AfterViewInit, Component, HostListener, Inject, Optional, QueryList, ViewChildren, ViewEncapsulation } from '@angular/core'; +import { AfterViewInit, Component, HostListener, QueryList, ViewChildren, ViewEncapsulation, inject } from '@angular/core'; import { NgForOf, NgIf } from '@angular/common'; import { TranslatePipe } from '@ngx-translate/core'; import { DOWN_ARROW, UP_ARROW } from '@angular/cdk/keycodes'; @@ -41,6 +41,9 @@ import { ContextMenuItem } from './interfaces'; animations: [trigger('panelAnimation', contextMenuAnimation)] }) export class ContextMenuListComponent implements AfterViewInit { + private readonly contextMenuOverlayRef = inject<ContextMenuOverlayRef>(ContextMenuOverlayRef); + private readonly data = inject(CONTEXT_MENU_DATA, { optional: true }); + private keyManager: FocusKeyManager<MatMenuItem>; @ViewChildren(MatMenuItem) items: QueryList<MatMenuItem>; links: ContextMenuItem[]; @@ -62,10 +65,7 @@ export class ContextMenuListComponent implements AfterViewInit { } } - constructor( - @Inject(ContextMenuOverlayRef) private contextMenuOverlayRef: ContextMenuOverlayRef, - @Optional() @Inject(CONTEXT_MENU_DATA) private data: ContextMenuItem[] - ) { + constructor() { this.links = this.data; } diff --git a/lib/core/src/lib/context-menu/context-menu-overlay.service.spec.ts b/lib/core/src/lib/context-menu/context-menu-overlay.service.spec.ts index ee2211168a..2c10c59d80 100644 --- a/lib/core/src/lib/context-menu/context-menu-overlay.service.spec.ts +++ b/lib/core/src/lib/context-menu/context-menu-overlay.service.spec.ts @@ -17,7 +17,7 @@ import { Overlay } from '@angular/cdk/overlay'; import { ContextMenuOverlayService } from './context-menu-overlay.service'; -import { Injector } from '@angular/core'; +import { Injector, runInInjectionContext } from '@angular/core'; import { TestBed } from '@angular/core/testing'; describe('ContextMenuOverlayService', () => { @@ -43,7 +43,15 @@ describe('ContextMenuOverlayService', () => { describe('Overlay', () => { beforeEach(() => { - contextMenuOverlayService = new ContextMenuOverlayService(injector, overlay); + const testInjector = Injector.create({ + providers: [ + { provide: Injector, useValue: injector }, + { provide: Overlay, useValue: overlay } + ], + parent: TestBed.inject(Injector) + }); + + contextMenuOverlayService = runInInjectionContext(testInjector, () => new ContextMenuOverlayService()); }); it('should create a custom overlay', () => { diff --git a/lib/core/src/lib/context-menu/context-menu-overlay.service.ts b/lib/core/src/lib/context-menu/context-menu-overlay.service.ts index a845b47d99..b5797da21e 100644 --- a/lib/core/src/lib/context-menu/context-menu-overlay.service.ts +++ b/lib/core/src/lib/context-menu/context-menu-overlay.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable, Injector, ElementRef, ComponentRef } from '@angular/core'; +import { Injectable, Injector, ElementRef, ComponentRef, inject } from '@angular/core'; import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; import { ContextMenuOverlayRef } from './context-menu-overlay'; @@ -33,10 +33,8 @@ const DEFAULT_CONFIG: ContextMenuOverlayConfig = { providedIn: 'root' }) export class ContextMenuOverlayService { - constructor( - private injector: Injector, - private overlay: Overlay - ) {} + private readonly injector = inject(Injector); + private readonly overlay = inject(Overlay); open(config: ContextMenuOverlayConfig): ContextMenuOverlayRef { const overlayConfig = { ...DEFAULT_CONFIG, ...config }; diff --git a/lib/core/src/lib/context-menu/context-menu-overlay.ts b/lib/core/src/lib/context-menu/context-menu-overlay.ts index 2bd0838fb2..cd6936af1e 100644 --- a/lib/core/src/lib/context-menu/context-menu-overlay.ts +++ b/lib/core/src/lib/context-menu/context-menu-overlay.ts @@ -18,7 +18,7 @@ import { OverlayRef } from '@angular/cdk/overlay'; export class ContextMenuOverlayRef { - constructor(private overlayRef: OverlayRef) {} + constructor(private readonly overlayRef: OverlayRef) {} close(): void { this.overlayRef.dispose(); diff --git a/lib/core/src/lib/context-menu/context-menu.directive.ts b/lib/core/src/lib/context-menu/context-menu.directive.ts index b0af3ccaa2..5d0736d704 100644 --- a/lib/core/src/lib/context-menu/context-menu.directive.ts +++ b/lib/core/src/lib/context-menu/context-menu.directive.ts @@ -17,13 +17,15 @@ /* eslint-disable @angular-eslint/no-input-rename */ -import { Directive, HostListener, Input } from '@angular/core'; +import { Directive, HostListener, Input, inject } from '@angular/core'; import { ContextMenuOverlayService } from './context-menu-overlay.service'; @Directive({ selector: '[adf-context-menu]' }) export class ContextMenuDirective { + private readonly contextMenuService = inject(ContextMenuOverlayService); + /** Items for the menu. */ @Input('adf-context-menu') links: any[] | (() => any[]); @@ -32,8 +34,6 @@ export class ContextMenuDirective { @Input('adf-context-menu-enabled') enabled: boolean = false; - constructor(private contextMenuService: ContextMenuOverlayService) {} - @HostListener('contextmenu', ['$event']) onShowContextMenu(event?: MouseEvent) { if (this.enabled) { diff --git a/lib/core/src/lib/datatable/components/columns-selector/columns-search-filter.pipe.ts b/lib/core/src/lib/datatable/components/columns-selector/columns-search-filter.pipe.ts index 3834e7ecd5..4ce5c3149a 100644 --- a/lib/core/src/lib/datatable/components/columns-selector/columns-search-filter.pipe.ts +++ b/lib/core/src/lib/datatable/components/columns-selector/columns-search-filter.pipe.ts @@ -23,7 +23,7 @@ import { TranslationService } from '../../../translation'; name: 'columnsSearchFilter' }) export class ColumnsSearchFilterPipe implements PipeTransform { - private translationService = inject(TranslationService); + private readonly translationService = inject(TranslationService); transform(columns: DataColumn[], searchByName: string): DataColumn[] { const result = []; diff --git a/lib/core/src/lib/datatable/components/datatable-row/datatable-row.component.ts b/lib/core/src/lib/datatable/components/datatable-row/datatable-row.component.ts index 40ea21ef70..311773c17c 100644 --- a/lib/core/src/lib/datatable/components/datatable-row/datatable-row.component.ts +++ b/lib/core/src/lib/datatable/components/datatable-row/datatable-row.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, Output, ViewEncapsulation } from '@angular/core'; +import { Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, Output, ViewEncapsulation, inject } from '@angular/core'; import { FocusableOption } from '@angular/cdk/a11y'; import { DataRow } from '../../data/data-row.model'; @@ -29,6 +29,8 @@ import { DataRow } from '../../data/data-row.model'; } }) export class DataTableRowComponent implements FocusableOption { + private readonly element = inject(ElementRef); + @Input() row: DataRow; @Input() disabled = true; @@ -74,8 +76,6 @@ export class DataTableRowComponent implements FocusableOption { } } - constructor(private element: ElementRef) {} - focus() { this.element.nativeElement.focus(); } diff --git a/lib/core/src/lib/datatable/components/datatable/datatable.component.spec.ts b/lib/core/src/lib/datatable/components/datatable/datatable.component.spec.ts index b8e051bef2..935801a27f 100644 --- a/lib/core/src/lib/datatable/components/datatable/datatable.component.spec.ts +++ b/lib/core/src/lib/datatable/components/datatable/datatable.component.spec.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, DebugElement, NO_ERRORS_SCHEMA, QueryList, SimpleChange, TemplateRef, ViewChild } from '@angular/core'; +import { Component, DebugElement, QueryList, SimpleChange, TemplateRef, ViewChild } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { MatCheckboxChange } from '@angular/material/checkbox'; import { DataColumn } from '../../data/data-column.model'; @@ -1631,8 +1631,7 @@ describe('Accessibility', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [CustomColumnTemplateComponent], - providers: [{ provide: ConfigurableFocusTrapFactory, useValue: focusTrapFactory }], - schemas: [NO_ERRORS_SCHEMA] + providers: [{ provide: ConfigurableFocusTrapFactory, useValue: focusTrapFactory }] }); columnCustomTemplate = TestBed.createComponent(CustomColumnTemplateComponent).componentInstance.templateRef; fixture = TestBed.createComponent(DataTableComponent); @@ -2020,8 +2019,7 @@ describe('Drag&Drop column header', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [CustomColumnTemplateComponent], - schemas: [NO_ERRORS_SCHEMA] + imports: [CustomColumnTemplateComponent] }); fixture = TestBed.createComponent(DataTableComponent); testingUtils = new UnitTestingUtils(fixture.debugElement); @@ -2114,8 +2112,7 @@ describe('Show/hide columns', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [CustomColumnTemplateComponent], - schemas: [NO_ERRORS_SCHEMA] + imports: [CustomColumnTemplateComponent] }); fixture = TestBed.createComponent(DataTableComponent); dataTable = fixture.componentInstance; @@ -2222,8 +2219,7 @@ describe('Column Resizing', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [CustomColumnTemplateComponent], - schemas: [NO_ERRORS_SCHEMA] + imports: [CustomColumnTemplateComponent] }); fixture = TestBed.createComponent(DataTableComponent); dataTable = fixture.componentInstance; diff --git a/lib/core/src/lib/datatable/components/datatable/datatable.component.ts b/lib/core/src/lib/datatable/components/datatable/datatable.component.ts index b0e5f3bc79..6748a035d3 100644 --- a/lib/core/src/lib/datatable/components/datatable/datatable.component.ts +++ b/lib/core/src/lib/datatable/components/datatable/datatable.component.ts @@ -130,7 +130,12 @@ export type ShowHeaderMode = (typeof ShowHeaderMode)[keyof typeof ShowHeaderMode host: { class: 'adf-datatable' } }) export class DataTableComponent implements OnInit, AfterContentInit, OnChanges, DoCheck, OnDestroy, AfterViewInit { - private static MINIMUM_COLUMN_SIZE = 100; + private readonly elementRef = inject(ElementRef); + private readonly matIconRegistry = inject(MatIconRegistry); + private readonly sanitizer = inject(DomSanitizer); + private readonly focusTrapFactory = inject(ConfigurableFocusTrapFactory); + + private static readonly MINIMUM_COLUMN_SIZE = 100; @ViewChildren(DataTableRowComponent) rowsList: QueryList<DataTableRowComponent>; @@ -333,9 +338,9 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges, private keyManager: FocusKeyManager<DataTableRowComponent>; private clickObserver: Observer<DataRowEvent>; - private click$: Observable<DataRowEvent>; + private readonly click$: Observable<DataRowEvent>; - private differ: any; + private readonly differ: any; private rowMenuCache: any = {}; private singleClickStreamSub: Subscription; @@ -376,13 +381,9 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges, } } - constructor( - private readonly elementRef: ElementRef, - differs: IterableDiffers, - private readonly matIconRegistry: MatIconRegistry, - private readonly sanitizer: DomSanitizer, - private readonly focusTrapFactory: ConfigurableFocusTrapFactory - ) { + constructor() { + const differs = inject(IterableDiffers); + if (differs) { this.differ = differs.find([]).create(null); } diff --git a/lib/core/src/lib/datatable/components/json-cell/json-cell.component.ts b/lib/core/src/lib/datatable/components/json-cell/json-cell.component.ts index 9a644286b6..d86e45d129 100644 --- a/lib/core/src/lib/datatable/components/json-cell/json-cell.component.ts +++ b/lib/core/src/lib/datatable/components/json-cell/json-cell.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ChangeDetectionStrategy, Component, ViewEncapsulation, Input, computed } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ViewEncapsulation, Input, computed, inject } from '@angular/core'; import { DataTableCellComponent } from '../datatable-cell/datatable-cell.component'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { EditJsonDialogComponent, EditJsonDialogSettings } from '../../../dialogs/edit-json/edit-json.dialog'; @@ -36,6 +36,8 @@ import { toSignal } from '@angular/core/rxjs-interop'; host: { class: 'adf-datatable-content-cell' } }) export class JsonCellComponent extends DataTableCellComponent { + private readonly dialog = inject(MatDialog); + /** Editable JSON. */ @Input() editable: boolean = false; @@ -47,10 +49,6 @@ export class JsonCellComponent extends DataTableCellComponent { return !!value || this.editable; }); - constructor(private dialog: MatDialog) { - super(); - } - view() { const rawValue = this.data.getValue(this.row, this.column, this.resolverFn); const value = typeof rawValue === 'object' ? JSON.stringify(rawValue || {}, null, 2) : String(rawValue ?? ''); diff --git a/lib/core/src/lib/datatable/data-column/data-column-header.component.ts b/lib/core/src/lib/datatable/data-column/data-column-header.component.ts index 172603021d..9aea346458 100644 --- a/lib/core/src/lib/datatable/data-column/data-column-header.component.ts +++ b/lib/core/src/lib/datatable/data-column/data-column-header.component.ts @@ -17,7 +17,7 @@ /* eslint-disable @angular-eslint/component-selector, @angular-eslint/no-input-rename */ -import { AfterContentInit, Component, ContentChild, TemplateRef } from '@angular/core'; +import { AfterContentInit, Component, ContentChild, TemplateRef, inject } from '@angular/core'; import { DataColumnComponent } from './data-column.component'; @Component({ @@ -25,11 +25,11 @@ import { DataColumnComponent } from './data-column.component'; template: '' }) export class DateColumnHeaderComponent implements AfterContentInit { + private readonly columnComponent = inject(DataColumnComponent); + @ContentChild(TemplateRef) public header: TemplateRef<any>; - constructor(private columnComponent: DataColumnComponent) {} - ngAfterContentInit() { if (this.columnComponent) { this.columnComponent.header = this.header; diff --git a/lib/core/src/lib/datatable/data/data-table.schema.ts b/lib/core/src/lib/datatable/data/data-table.schema.ts index d59bac885a..b0b2a1de91 100644 --- a/lib/core/src/lib/datatable/data/data-table.schema.ts +++ b/lib/core/src/lib/datatable/data/data-table.schema.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ContentChild, Input, Directive } from '@angular/core'; +import { ContentChild, Input, Directive, inject } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { AppConfigService } from '../../app-config/app-config.service'; import { DataColumnListComponent } from '../data-column/data-column-list.component'; @@ -45,11 +45,15 @@ export abstract class DataTableSchema<T = unknown> { protected columnsSchemaSubject$ = new BehaviorSubject<boolean>(false); isColumnSchemaCreated$ = this.columnsSchemaSubject$.asObservable(); - constructor( - private appConfigService: AppConfigService, - protected presetKey: string, - protected presetsModel: any - ) {} + protected presetKey: string; + protected presetsModel: any; + + protected readonly appConfigService = inject(AppConfigService); + + constructor(presetKey: string, presetsModel: any) { + this.presetKey = presetKey; + this.presetsModel = presetsModel; + } public createDatatableSchema(): void { this.loadLayoutPresets(); diff --git a/lib/core/src/lib/datatable/data/object-datarow.model.ts b/lib/core/src/lib/datatable/data/object-datarow.model.ts index 059590b64a..4e1cced131 100644 --- a/lib/core/src/lib/datatable/data/object-datarow.model.ts +++ b/lib/core/src/lib/datatable/data/object-datarow.model.ts @@ -20,7 +20,11 @@ import { DataRow } from './data-row.model'; // Simple implementation of the DataRow interface. export class ObjectDataRow implements DataRow { - constructor(private obj: any, public isSelected: boolean = false, public isSelectable: boolean = true) { + constructor( + private readonly obj: any, + public isSelected: boolean = false, + public isSelectable: boolean = true + ) { if (!obj) { throw new Error('Object source not found'); } diff --git a/lib/core/src/lib/datatable/directives/drop-zone.directive.ts b/lib/core/src/lib/datatable/directives/drop-zone.directive.ts index bc8311af12..14e2ee452c 100644 --- a/lib/core/src/lib/datatable/directives/drop-zone.directive.ts +++ b/lib/core/src/lib/datatable/directives/drop-zone.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, Input, ElementRef, NgZone, OnInit, OnDestroy } from '@angular/core'; +import { Directive, Input, ElementRef, NgZone, OnInit, OnDestroy, inject } from '@angular/core'; import { DataRow } from '../data/data-row.model'; import { DataColumn } from '../data/data-column.model'; @@ -23,7 +23,9 @@ import { DataColumn } from '../data/data-column.model'; selector: '[adf-drop-zone]' }) export class DropZoneDirective implements OnInit, OnDestroy { - private element: HTMLElement; + private readonly ngZone = inject(NgZone); + + private readonly element: HTMLElement; @Input() dropTarget: 'header' | 'cell' = 'cell'; @@ -34,7 +36,9 @@ export class DropZoneDirective implements OnInit, OnDestroy { @Input() dropColumn: DataColumn; - constructor(elementRef: ElementRef, private ngZone: NgZone) { + constructor() { + const elementRef = inject(ElementRef); + this.element = elementRef.nativeElement; } diff --git a/lib/core/src/lib/datatable/directives/header-filter-template.directive.spec.ts b/lib/core/src/lib/datatable/directives/header-filter-template.directive.spec.ts index 1de9e4f9d3..f515ed2837 100644 --- a/lib/core/src/lib/datatable/directives/header-filter-template.directive.spec.ts +++ b/lib/core/src/lib/datatable/directives/header-filter-template.directive.spec.ts @@ -18,6 +18,7 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { DataTableComponent } from '../components/datatable/datatable.component'; import { HeaderFilterTemplateDirective } from './header-filter-template.directive'; +import { Injector, runInInjectionContext } from '@angular/core'; describe('HeaderFilterTemplateDirective', () => { let fixture: ComponentFixture<DataTableComponent>; @@ -30,7 +31,13 @@ describe('HeaderFilterTemplateDirective', () => { }); fixture = TestBed.createComponent(DataTableComponent); dataTable = fixture.componentInstance; - directive = new HeaderFilterTemplateDirective(dataTable); + + const injector = Injector.create({ + providers: [{ provide: DataTableComponent, useValue: dataTable }], + parent: TestBed.inject(Injector) + }); + + directive = runInInjectionContext(injector, () => new HeaderFilterTemplateDirective()); }); afterEach(() => { diff --git a/lib/core/src/lib/datatable/directives/header-filter-template.directive.ts b/lib/core/src/lib/datatable/directives/header-filter-template.directive.ts index 07c8c01583..ab3d0321c8 100644 --- a/lib/core/src/lib/datatable/directives/header-filter-template.directive.ts +++ b/lib/core/src/lib/datatable/directives/header-filter-template.directive.ts @@ -15,18 +15,18 @@ * limitations under the License. */ -import { AfterContentInit, ContentChild, Directive, TemplateRef } from '@angular/core'; +import { AfterContentInit, ContentChild, Directive, TemplateRef, inject } from '@angular/core'; import { DataTableComponent } from '../components/datatable/datatable.component'; @Directive({ selector: 'adf-header-filter-template' }) export class HeaderFilterTemplateDirective implements AfterContentInit { + private readonly dataTable = inject(DataTableComponent, { optional: true }); + @ContentChild(TemplateRef) template: any; - constructor(private dataTable: DataTableComponent) {} - ngAfterContentInit() { if (this.dataTable) { this.dataTable.headerFilterTemplate = this.template; diff --git a/lib/core/src/lib/datatable/directives/loading-template.directive.spec.ts b/lib/core/src/lib/datatable/directives/loading-template.directive.spec.ts index c5befb3d37..7c7ba4f19f 100644 --- a/lib/core/src/lib/datatable/directives/loading-template.directive.spec.ts +++ b/lib/core/src/lib/datatable/directives/loading-template.directive.spec.ts @@ -18,6 +18,7 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { DataTableComponent } from '../components/datatable/datatable.component'; import { LoadingContentTemplateDirective } from './loading-template.directive'; +import { Injector, runInInjectionContext } from '@angular/core'; describe('LoadingContentTemplateDirective', () => { let fixture: ComponentFixture<DataTableComponent>; @@ -30,7 +31,13 @@ describe('LoadingContentTemplateDirective', () => { }); fixture = TestBed.createComponent(DataTableComponent); dataTable = fixture.componentInstance; - directive = new LoadingContentTemplateDirective(dataTable); + + const injector = Injector.create({ + providers: [{ provide: DataTableComponent, useValue: dataTable }], + parent: TestBed.inject(Injector) + }); + + directive = runInInjectionContext(injector, () => new LoadingContentTemplateDirective()); }); afterEach(() => { diff --git a/lib/core/src/lib/datatable/directives/loading-template.directive.ts b/lib/core/src/lib/datatable/directives/loading-template.directive.ts index 4d564ee41c..e78b223440 100644 --- a/lib/core/src/lib/datatable/directives/loading-template.directive.ts +++ b/lib/core/src/lib/datatable/directives/loading-template.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { AfterContentInit, ContentChild, Directive, TemplateRef } from '@angular/core'; +import { AfterContentInit, ContentChild, Directive, TemplateRef, inject } from '@angular/core'; import { DataTableComponent } from '../components/datatable/datatable.component'; /** @@ -25,11 +25,11 @@ import { DataTableComponent } from '../components/datatable/datatable.component' selector: 'adf-loading-content-template, loading-content-template' }) export class LoadingContentTemplateDirective implements AfterContentInit { + private readonly dataTable = inject(DataTableComponent); + @ContentChild(TemplateRef) template: any; - constructor(private dataTable: DataTableComponent) {} - ngAfterContentInit() { if (this.dataTable) { this.dataTable.loadingTemplate = this.template; diff --git a/lib/core/src/lib/datatable/directives/main-data-table-action-template.directive.spec.ts b/lib/core/src/lib/datatable/directives/main-data-table-action-template.directive.spec.ts index 44ab6463bd..101f885b57 100644 --- a/lib/core/src/lib/datatable/directives/main-data-table-action-template.directive.spec.ts +++ b/lib/core/src/lib/datatable/directives/main-data-table-action-template.directive.spec.ts @@ -18,9 +18,9 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { DataTableComponent } from '../components/datatable/datatable.component'; import { MainMenuDataTableTemplateDirective } from './main-data-table-action-template.directive'; +import { Injector, runInInjectionContext } from '@angular/core'; describe('MainMenuDataTableTemplateDirective', () => { - let fixture: ComponentFixture<DataTableComponent>; let dataTable: DataTableComponent; let directive: MainMenuDataTableTemplateDirective; @@ -28,7 +28,13 @@ describe('MainMenuDataTableTemplateDirective', () => { beforeEach(() => { fixture = TestBed.createComponent(DataTableComponent); dataTable = fixture.componentInstance; - directive = new MainMenuDataTableTemplateDirective(dataTable); + + const injector = Injector.create({ + providers: [{ provide: DataTableComponent, useValue: dataTable }], + parent: TestBed.inject(Injector) + }); + + directive = runInInjectionContext(injector, () => new MainMenuDataTableTemplateDirective()); }); afterEach(() => { diff --git a/lib/core/src/lib/datatable/directives/main-data-table-action-template.directive.ts b/lib/core/src/lib/datatable/directives/main-data-table-action-template.directive.ts index 0ee41ae0df..cc6de1bd68 100644 --- a/lib/core/src/lib/datatable/directives/main-data-table-action-template.directive.ts +++ b/lib/core/src/lib/datatable/directives/main-data-table-action-template.directive.ts @@ -15,18 +15,18 @@ * limitations under the License. */ -import { AfterContentInit, ContentChild, Directive, TemplateRef } from '@angular/core'; +import { AfterContentInit, ContentChild, Directive, TemplateRef, inject } from '@angular/core'; import { DataTableComponent } from '../components/datatable/datatable.component'; @Directive({ selector: 'adf-main-menu-datatable-template' }) export class MainMenuDataTableTemplateDirective implements AfterContentInit { + private readonly dataTable = inject(DataTableComponent); + @ContentChild(TemplateRef) template: any; - constructor(private dataTable: DataTableComponent) {} - ngAfterContentInit() { if (this.dataTable) { this.dataTable.mainActionTemplate = this.template; diff --git a/lib/core/src/lib/datatable/directives/no-content-template.directive.spec.ts b/lib/core/src/lib/datatable/directives/no-content-template.directive.spec.ts index 64961df313..9370c8d87d 100644 --- a/lib/core/src/lib/datatable/directives/no-content-template.directive.spec.ts +++ b/lib/core/src/lib/datatable/directives/no-content-template.directive.spec.ts @@ -18,6 +18,7 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { DataTableComponent } from '../components/datatable/datatable.component'; import { NoContentTemplateDirective } from './no-content-template.directive'; +import { Injector, runInInjectionContext } from '@angular/core'; describe('NoContentTemplateDirective', () => { let fixture: ComponentFixture<DataTableComponent>; @@ -30,7 +31,13 @@ describe('NoContentTemplateDirective', () => { }); fixture = TestBed.createComponent(DataTableComponent); dataTable = fixture.componentInstance; - directive = new NoContentTemplateDirective(dataTable); + + const injector = Injector.create({ + providers: [{ provide: DataTableComponent, useValue: dataTable }], + parent: TestBed.inject(Injector) + }); + + directive = runInInjectionContext(injector, () => new NoContentTemplateDirective()); }); afterEach(() => { diff --git a/lib/core/src/lib/datatable/directives/no-content-template.directive.ts b/lib/core/src/lib/datatable/directives/no-content-template.directive.ts index fdc551ad2d..e805d1a5b5 100644 --- a/lib/core/src/lib/datatable/directives/no-content-template.directive.ts +++ b/lib/core/src/lib/datatable/directives/no-content-template.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { AfterContentInit, ContentChild, Directive, TemplateRef } from '@angular/core'; +import { AfterContentInit, ContentChild, Directive, TemplateRef, inject } from '@angular/core'; import { DataTableComponent } from '../components/datatable/datatable.component'; /** @@ -25,11 +25,11 @@ import { DataTableComponent } from '../components/datatable/datatable.component' selector: 'adf-no-content-template, no-content-template' }) export class NoContentTemplateDirective implements AfterContentInit { + private readonly dataTable = inject(DataTableComponent); + @ContentChild(TemplateRef) template: any; - constructor(private dataTable: DataTableComponent) {} - ngAfterContentInit() { if (this.dataTable) { this.dataTable.noContentTemplate = this.template; diff --git a/lib/core/src/lib/datatable/directives/no-permission-template.directive.spec.ts b/lib/core/src/lib/datatable/directives/no-permission-template.directive.spec.ts index 1b5d32a55e..29f6bbd790 100644 --- a/lib/core/src/lib/datatable/directives/no-permission-template.directive.spec.ts +++ b/lib/core/src/lib/datatable/directives/no-permission-template.directive.spec.ts @@ -18,6 +18,7 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { DataTableComponent } from '../components/datatable/datatable.component'; import { NoPermissionTemplateDirective } from './no-permission-template.directive'; +import { Injector, runInInjectionContext } from '@angular/core'; describe('NoPermissionTemplateDirective', () => { let fixture: ComponentFixture<DataTableComponent>; @@ -30,7 +31,13 @@ describe('NoPermissionTemplateDirective', () => { }); fixture = TestBed.createComponent(DataTableComponent); dataTable = fixture.componentInstance; - directive = new NoPermissionTemplateDirective(dataTable); + + const injector = Injector.create({ + providers: [{ provide: DataTableComponent, useValue: dataTable }], + parent: TestBed.inject(Injector) + }); + + directive = runInInjectionContext(injector, () => new NoPermissionTemplateDirective()); }); afterEach(() => { diff --git a/lib/core/src/lib/datatable/directives/no-permission-template.directive.ts b/lib/core/src/lib/datatable/directives/no-permission-template.directive.ts index 7508d4fb3c..2a4f6d8b36 100644 --- a/lib/core/src/lib/datatable/directives/no-permission-template.directive.ts +++ b/lib/core/src/lib/datatable/directives/no-permission-template.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { AfterContentInit, ContentChild, Directive, TemplateRef } from '@angular/core'; +import { AfterContentInit, ContentChild, Directive, TemplateRef, inject } from '@angular/core'; import { DataTableComponent } from '../components/datatable/datatable.component'; /** @@ -25,11 +25,11 @@ import { DataTableComponent } from '../components/datatable/datatable.component' selector: 'adf-no-permission-template, no-permission-template' }) export class NoPermissionTemplateDirective implements AfterContentInit { + private readonly dataTable = inject(DataTableComponent); + @ContentChild(TemplateRef) template: any; - constructor(private dataTable: DataTableComponent) {} - ngAfterContentInit() { if (this.dataTable) { this.dataTable.noPermissionTemplate = this.template; diff --git a/lib/core/src/lib/datatable/directives/resizable/resizable.directive.spec.ts b/lib/core/src/lib/datatable/directives/resizable/resizable.directive.spec.ts index f0bb7d910a..1251b855c0 100644 --- a/lib/core/src/lib/datatable/directives/resizable/resizable.directive.spec.ts +++ b/lib/core/src/lib/datatable/directives/resizable/resizable.directive.spec.ts @@ -67,10 +67,18 @@ describe('ResizableDirective', () => { const injector = TestBed.inject(Injector); spyOn(ngZone, 'runOutsideAngular').and.callFake((fn) => fn()); spyOn(ngZone, 'run').and.callFake((fn) => fn()); - runInInjectionContext(injector, () => { - directive = new ResizableDirective(renderer, element, ngZone); + + const testInjector = Injector.create({ + providers: [ + { provide: Renderer2, useValue: renderer }, + { provide: ElementRef, useValue: element }, + { provide: NgZone, useValue: ngZone } + ], + parent: injector }); + directive = runInInjectionContext(testInjector, () => new ResizableDirective()); + directive.ngOnInit(); }); diff --git a/lib/core/src/lib/datatable/directives/resizable/resizable.directive.ts b/lib/core/src/lib/datatable/directives/resizable/resizable.directive.ts index 712e7b41e0..c002acdd72 100644 --- a/lib/core/src/lib/datatable/directives/resizable/resizable.directive.ts +++ b/lib/core/src/lib/datatable/directives/resizable/resizable.directive.ts @@ -26,6 +26,10 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; exportAs: 'adf-resizable' }) export class ResizableDirective implements OnInit, OnDestroy { + private readonly renderer = inject(Renderer2); + private readonly element = inject<ElementRef<HTMLElement>>(ElementRef); + private readonly zone = inject(NgZone); + /** * Emitted when the mouse is pressed and a resize event is about to begin. */ @@ -71,7 +75,10 @@ export class ResizableDirective implements OnInit, OnDestroy { private readonly destroyRef = inject(DestroyRef); - constructor(private readonly renderer: Renderer2, private readonly element: ElementRef<HTMLElement>, private readonly zone: NgZone) { + constructor() { + const renderer = this.renderer; + const zone = this.zone; + this.pointerDown = new Observable((observer: Observer<IResizeMouseEvent>) => { zone.runOutsideAngular(() => { this.unsubscribeMouseDown = renderer.listen('document', 'mousedown', (event: MouseEvent) => { diff --git a/lib/core/src/lib/datatable/directives/resizable/resize-handle.directive.spec.ts b/lib/core/src/lib/datatable/directives/resizable/resize-handle.directive.spec.ts index b830693722..3ff0540549 100644 --- a/lib/core/src/lib/datatable/directives/resizable/resize-handle.directive.spec.ts +++ b/lib/core/src/lib/datatable/directives/resizable/resize-handle.directive.spec.ts @@ -15,7 +15,8 @@ * limitations under the License. */ -import { ElementRef, NgZone, Renderer2 } from '@angular/core'; +import { ElementRef, NgZone, Renderer2, Injector, runInInjectionContext } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; import { ResizeHandleDirective } from './resize-handle.directive'; import { ResizableDirective } from './resizable.directive'; import { Subject } from 'rxjs'; @@ -38,7 +39,16 @@ describe('ResizeHandleDirective', () => { mousemove: new Subject() }); - directive = new ResizeHandleDirective(renderer, element, ngZone); + const injector = Injector.create({ + providers: [ + { provide: Renderer2, useValue: renderer }, + { provide: ElementRef, useValue: element }, + { provide: NgZone, useValue: ngZone } + ], + parent: TestBed.inject(Injector) + }); + + directive = runInInjectionContext(injector, () => new ResizeHandleDirective()); directive.resizableContainer = resizableContainer; }); diff --git a/lib/core/src/lib/datatable/directives/resizable/resize-handle.directive.ts b/lib/core/src/lib/datatable/directives/resizable/resize-handle.directive.ts index 32bb92a2bc..3fe46ce707 100644 --- a/lib/core/src/lib/datatable/directives/resizable/resize-handle.directive.ts +++ b/lib/core/src/lib/datatable/directives/resizable/resize-handle.directive.ts @@ -16,12 +16,16 @@ */ import { ResizableDirective } from './resizable.directive'; -import { Directive, ElementRef, HostListener, Input, NgZone, OnDestroy, OnInit, Renderer2 } from '@angular/core'; +import { Directive, ElementRef, HostListener, Input, NgZone, OnDestroy, OnInit, Renderer2, inject } from '@angular/core'; @Directive({ selector: '[adf-resize-handle]' }) export class ResizeHandleDirective implements OnInit, OnDestroy { + private readonly renderer = inject(Renderer2); + private readonly element = inject(ElementRef); + private readonly zone = inject(NgZone); + /** * Reference to ResizableDirective */ @@ -30,7 +34,6 @@ export class ResizeHandleDirective implements OnInit, OnDestroy { private unlistenMouseDown?: () => void; private unlistenMouseMove?: () => void; private unlistenMouseUp?: () => void; - constructor(private readonly renderer: Renderer2, private readonly element: ElementRef, private readonly zone: NgZone) {} @HostListener('keydown', ['$event']) onKeydown(event: KeyboardEvent): void { diff --git a/lib/core/src/lib/dialogs/confirm-dialog/confirm.dialog.ts b/lib/core/src/lib/dialogs/confirm-dialog/confirm.dialog.ts index be995d6872..fc6f781c71 100644 --- a/lib/core/src/lib/dialogs/confirm-dialog/confirm.dialog.ts +++ b/lib/core/src/lib/dialogs/confirm-dialog/confirm.dialog.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject, SecurityContext, ViewEncapsulation } from '@angular/core'; +import { Component, SecurityContext, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { DomSanitizer } from '@angular/platform-browser'; import { TranslatePipe } from '@ngx-translate/core'; @@ -41,6 +41,9 @@ export interface ConfirmDialogComponentProps { imports: [TranslatePipe, MatDialogModule, NgIf, MatButtonModule] }) export class ConfirmDialogComponent { + private readonly sanitizer = inject(DomSanitizer); + private readonly data = inject<ConfirmDialogComponentProps>(MAT_DIALOG_DATA) ?? {}; + title: string; message: string; yesLabel: string; @@ -48,14 +51,13 @@ export class ConfirmDialogComponent { thirdOptionLabel: string; htmlContent: string; - constructor(@Inject(MAT_DIALOG_DATA) data: ConfirmDialogComponentProps, private sanitizer: DomSanitizer) { - data = data || {}; - this.title = data.title || 'ADF_CONFIRM_DIALOG.TITLE'; - this.message = data.message || 'ADF_CONFIRM_DIALOG.MESSAGE'; - this.yesLabel = data.yesLabel || 'ADF_CONFIRM_DIALOG.YES_LABEL'; - this.thirdOptionLabel = data.thirdOptionLabel; - this.noLabel = data.noLabel || 'ADF_CONFIRM_DIALOG.NO_LABEL'; - this.htmlContent = data.htmlContent; + constructor() { + this.title = this.data.title || 'ADF_CONFIRM_DIALOG.TITLE'; + this.message = this.data.message || 'ADF_CONFIRM_DIALOG.MESSAGE'; + this.yesLabel = this.data.yesLabel || 'ADF_CONFIRM_DIALOG.YES_LABEL'; + this.thirdOptionLabel = this.data.thirdOptionLabel; + this.noLabel = this.data.noLabel || 'ADF_CONFIRM_DIALOG.NO_LABEL'; + this.htmlContent = this.data.htmlContent; } sanitizedHtmlContent(): string { diff --git a/lib/core/src/lib/dialogs/dialog/dialog.component.ts b/lib/core/src/lib/dialogs/dialog/dialog.component.ts index 98826bb0d1..fe49b0995e 100644 --- a/lib/core/src/lib/dialogs/dialog/dialog.component.ts +++ b/lib/core/src/lib/dialogs/dialog/dialog.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject, InjectionToken, Injector, ViewEncapsulation } from '@angular/core'; +import { Component, InjectionToken, Injector, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { AdditionalDialogActionButton, DialogData } from './dialog-data.interface'; import { BehaviorSubject } from 'rxjs'; @@ -36,6 +36,9 @@ export const DIALOG_COMPONENT_DATA = new InjectionToken<any>('dialog component d encapsulation: ViewEncapsulation.None }) export class DialogComponent { + data = inject<DialogData>(MAT_DIALOG_DATA); + dialogRef = inject<MatDialogRef<DialogComponent>>(MatDialogRef); + isConfirmButtonDisabled$ = new BehaviorSubject<boolean>(false); isCloseButtonHidden: boolean; isCancelButtonHidden: boolean; @@ -47,11 +50,9 @@ export class DialogComponent { dataInjector: Injector; - constructor( - @Inject(MAT_DIALOG_DATA) - public data: DialogData, - public dialogRef: MatDialogRef<DialogComponent> - ) { + constructor() { + const data = this.data; + if (data) { this.isCancelButtonHidden = data.isCancelButtonHidden || false; this.isCloseButtonHidden = data.isCloseButtonHidden || false; diff --git a/lib/core/src/lib/dialogs/edit-json/edit-json.dialog.ts b/lib/core/src/lib/dialogs/edit-json/edit-json.dialog.ts index b6f7f443c6..07adca4ef8 100644 --- a/lib/core/src/lib/dialogs/edit-json/edit-json.dialog.ts +++ b/lib/core/src/lib/dialogs/edit-json/edit-json.dialog.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject, OnInit, Input, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, Input, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; @@ -36,14 +36,14 @@ export interface EditJsonDialogSettings { host: { class: 'adf-edit-json-dialog' } }) export class EditJsonDialogComponent implements OnInit { + private readonly settings = inject<EditJsonDialogSettings>(MAT_DIALOG_DATA); + editable: boolean = false; title: string = 'JSON'; @Input() value: string = ''; - constructor(@Inject(MAT_DIALOG_DATA) private settings: EditJsonDialogSettings) {} - ngOnInit() { if (this.settings) { this.editable = this.settings.editable; diff --git a/lib/core/src/lib/dialogs/unsaved-changes-dialog/unsaved-changes-dialog.component.ts b/lib/core/src/lib/dialogs/unsaved-changes-dialog/unsaved-changes-dialog.component.ts index ead9c73c12..618dbd6cc0 100644 --- a/lib/core/src/lib/dialogs/unsaved-changes-dialog/unsaved-changes-dialog.component.ts +++ b/lib/core/src/lib/dialogs/unsaved-changes-dialog/unsaved-changes-dialog.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { UnsavedChangesDialogData } from './unsaved-changes-dialog.model'; import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox'; @@ -44,12 +44,10 @@ import { IconModule } from '../../icon/icon.module'; imports: [MatDialogModule, TranslatePipe, MatButtonModule, IconModule, CommonModule, MatCheckboxModule, ReactiveFormsModule] }) export class UnsavedChangesDialogComponent implements OnInit { - dialogData: UnsavedChangesDialogData; + data = inject<UnsavedChangesDialogData>(MAT_DIALOG_DATA); + private readonly userPreferencesService = inject(UserPreferencesService); - constructor( - @Inject(MAT_DIALOG_DATA) public data: UnsavedChangesDialogData, - private userPreferencesService: UserPreferencesService - ) {} + dialogData: UnsavedChangesDialogData; ngOnInit() { this.dialogData = { diff --git a/lib/core/src/lib/dialogs/unsaved-changes-dialog/unsaved-changes.guard.ts b/lib/core/src/lib/dialogs/unsaved-changes-dialog/unsaved-changes.guard.ts index d8af9f6634..4997793e87 100644 --- a/lib/core/src/lib/dialogs/unsaved-changes-dialog/unsaved-changes.guard.ts +++ b/lib/core/src/lib/dialogs/unsaved-changes-dialog/unsaved-changes.guard.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { CanDeactivate } from '@angular/router'; import { Observable, of } from 'rxjs'; import { MatDialog } from '@angular/material/dialog'; @@ -31,11 +31,13 @@ import { AuthenticationService, AuthGuardService } from '../../auth'; providedIn: 'root' }) export class UnsavedChangesGuard implements CanDeactivate<any> { + private readonly dialog = inject(MatDialog); + private readonly authenticationService = inject(AuthenticationService); + private readonly authGuardBaseService = inject(AuthGuardService); + unsaved = false; data: UnsavedChangesDialogData; - constructor(private dialog: MatDialog, private authenticationService: AuthenticationService, private authGuardBaseService: AuthGuardService) {} - /** * Allows to deactivate route when there is no unsaved changes, otherwise displays dialog to confirm discarding changes. * diff --git a/lib/core/src/lib/directives/highlight.directive.ts b/lib/core/src/lib/directives/highlight.directive.ts index c0fd080cae..71e47a3bf5 100644 --- a/lib/core/src/lib/directives/highlight.directive.ts +++ b/lib/core/src/lib/directives/highlight.directive.ts @@ -17,13 +17,17 @@ /* eslint-disable @angular-eslint/no-input-rename */ -import { Directive, ElementRef, Input, Renderer2, AfterViewChecked } from '@angular/core'; +import { Directive, ElementRef, Input, Renderer2, AfterViewChecked, inject } from '@angular/core'; import { HighlightTransformService, HighlightTransformResult } from '../common/services/highlight-transform.service'; @Directive({ selector: '[adf-highlight]' }) export class HighlightDirective implements AfterViewChecked { + private readonly el = inject(ElementRef); + private readonly renderer = inject(Renderer2); + private readonly highlightTransformService = inject(HighlightTransformService); + /** Class selector for highlightable elements. */ @Input('adf-highlight-selector') selector: string = ''; @@ -36,8 +40,6 @@ export class HighlightDirective implements AfterViewChecked { @Input('adf-highlight-class') classToApply: string = 'adf-highlight'; - constructor(private el: ElementRef, private renderer: Renderer2, private highlightTransformService: HighlightTransformService) {} - ngAfterViewChecked() { this.highlight(); } diff --git a/lib/core/src/lib/directives/infinite-select-scroll.directive.ts b/lib/core/src/lib/directives/infinite-select-scroll.directive.ts index 6ae3829dca..353fd5a1ca 100644 --- a/lib/core/src/lib/directives/infinite-select-scroll.directive.ts +++ b/lib/core/src/lib/directives/infinite-select-scroll.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { AfterViewInit, DestroyRef, Directive, EventEmitter, inject, Inject, Output } from '@angular/core'; +import { AfterViewInit, DestroyRef, Directive, EventEmitter, inject, Output } from '@angular/core'; import { MatSelect } from '@angular/material/select'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -25,6 +25,8 @@ const SELECT_ITEM_HEIGHT_EM = 3; selector: '[adf-infinite-select-scroll]' }) export class InfiniteSelectScrollDirective implements AfterViewInit { + private readonly matSelect = inject<MatSelect>(MatSelect); + static readonly MAX_ITEMS = 50; /** Emitted when scroll reaches the last item. */ @@ -33,8 +35,6 @@ export class InfiniteSelectScrollDirective implements AfterViewInit { private readonly destroyRef = inject(DestroyRef); private itemHeightToWaitBeforeLoadNext = 0; - constructor(@Inject(MatSelect) private matSelect: MatSelect) {} - ngAfterViewInit() { this.matSelect.openedChange.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((opened: boolean) => { if (opened) { diff --git a/lib/core/src/lib/directives/logout.directive.ts b/lib/core/src/lib/directives/logout.directive.ts index 09a8c60f67..195a6aa032 100644 --- a/lib/core/src/lib/directives/logout.directive.ts +++ b/lib/core/src/lib/directives/logout.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Input, Directive, ElementRef, OnInit, Renderer2 } from '@angular/core'; +import { Input, Directive, ElementRef, OnInit, Renderer2, inject } from '@angular/core'; import { Router } from '@angular/router'; import { AppConfigService } from '../app-config/app-config.service'; import { AuthenticationService } from '../auth/services/authentication.service'; @@ -24,6 +24,12 @@ import { AuthenticationService } from '../auth/services/authentication.service'; selector: '[adf-logout]' }) export class LogoutDirective implements OnInit { + private readonly elementRef = inject(ElementRef); + private readonly renderer = inject(Renderer2); + private readonly router = inject(Router); + private readonly appConfig = inject(AppConfigService); + private readonly authenticationService = inject(AuthenticationService); + /** URI to redirect to after logging out. */ @Input() redirectUri: string; @@ -32,14 +38,6 @@ export class LogoutDirective implements OnInit { @Input() enableRedirect: boolean = true; - constructor( - private elementRef: ElementRef, - private renderer: Renderer2, - private router: Router, - private appConfig: AppConfigService, - private authenticationService: AuthenticationService - ) {} - ngOnInit() { if (this.elementRef.nativeElement) { this.renderer.listen(this.elementRef.nativeElement, 'click', (evt) => { diff --git a/lib/core/src/lib/directives/tooltip-card/tooltip-card.component.ts b/lib/core/src/lib/directives/tooltip-card/tooltip-card.component.ts index 0588fcbcde..5172d9f0c6 100644 --- a/lib/core/src/lib/directives/tooltip-card/tooltip-card.component.ts +++ b/lib/core/src/lib/directives/tooltip-card/tooltip-card.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Input, SecurityContext } from '@angular/core'; +import { Component, Input, SecurityContext, inject } from '@angular/core'; import { animate, style, transition, trigger } from '@angular/animations'; import { DomSanitizer } from '@angular/platform-browser'; import { CommonModule } from '@angular/common'; @@ -33,13 +33,13 @@ import { CommonModule } from '@angular/common'; ] }) export class TooltipCardComponent { + private readonly sanitizer = inject(DomSanitizer); + @Input() image = ''; @Input() text = ''; @Input() htmlContent = ''; @Input() width = '300'; - constructor(private sanitizer: DomSanitizer) {} - sanitizedHtmlContent(): string { return this.sanitizer.sanitize(SecurityContext.HTML, this.htmlContent); } diff --git a/lib/core/src/lib/directives/tooltip-card/tooltip-card.directive.ts b/lib/core/src/lib/directives/tooltip-card/tooltip-card.directive.ts index 228b695572..9f2a5fd455 100644 --- a/lib/core/src/lib/directives/tooltip-card/tooltip-card.directive.ts +++ b/lib/core/src/lib/directives/tooltip-card/tooltip-card.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ComponentRef, Directive, ElementRef, HostListener, Input, OnDestroy, OnInit } from '@angular/core'; +import { ComponentRef, Directive, ElementRef, HostListener, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { Overlay, OverlayPositionBuilder, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; import { TooltipCardComponent } from './tooltip-card.component'; @@ -24,6 +24,10 @@ import { TooltipCardComponent } from './tooltip-card.component'; selector: '[adf-tooltip-card]' }) export class TooltipCardDirective implements OnInit, OnDestroy { + private readonly overlay = inject(Overlay); + private readonly overlayPositionBuilder = inject(OverlayPositionBuilder); + private readonly elementRef = inject(ElementRef); + @Input('adf-tooltip-card') text = ''; @Input() image = ''; @Input() width = '300'; @@ -37,12 +41,6 @@ export class TooltipCardDirective implements OnInit, OnDestroy { private overlayRef: OverlayRef; - constructor( - private overlay: Overlay, - private overlayPositionBuilder: OverlayPositionBuilder, - private elementRef: ElementRef - ) {} - ngOnDestroy(): void { if (this.overlayRef) { this.hide(); diff --git a/lib/core/src/lib/directives/upload.directive.spec.ts b/lib/core/src/lib/directives/upload.directive.spec.ts index ebe4816ce4..32e0b4c534 100644 --- a/lib/core/src/lib/directives/upload.directive.spec.ts +++ b/lib/core/src/lib/directives/upload.directive.spec.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import { ElementRef } from '@angular/core'; -import { fakeAsync, tick } from '@angular/core/testing'; +import { ElementRef, Renderer2, NgZone, Injector, runInInjectionContext } from '@angular/core'; +import { fakeAsync, tick, TestBed } from '@angular/core/testing'; import { UploadDirective } from './upload.directive'; describe('UploadDirective', () => { @@ -26,9 +26,26 @@ describe('UploadDirective', () => { beforeEach(() => { nativeElement = { classList: jasmine.createSpyObj('classList', ['add', 'remove']), - dispatchEvent: () => {} + dispatchEvent: () => {}, + parentElement: { + appendChild: jasmine.createSpy('appendChild') + } }; - directive = new UploadDirective(new ElementRef(nativeElement), null, null); + + const mockRenderer = jasmine.createSpyObj('Renderer2', ['createElement']); + const mockNgZone = jasmine.createSpyObj('NgZone', ['runOutsideAngular']); + mockNgZone.runOutsideAngular.and.callFake((fn: () => void) => fn()); + + const injector = Injector.create({ + providers: [ + { provide: ElementRef, useValue: new ElementRef(nativeElement) }, + { provide: Renderer2, useValue: mockRenderer }, + { provide: NgZone, useValue: mockNgZone } + ], + parent: TestBed.inject(Injector) + }); + + directive = runInInjectionContext(injector, () => new UploadDirective()); }); it('should be enabled by default', () => { diff --git a/lib/core/src/lib/directives/upload.directive.ts b/lib/core/src/lib/directives/upload.directive.ts index 4921d41e12..33aa32768c 100644 --- a/lib/core/src/lib/directives/upload.directive.ts +++ b/lib/core/src/lib/directives/upload.directive.ts @@ -17,13 +17,17 @@ /* eslint-disable @angular-eslint/no-input-rename */ -import { Directive, ElementRef, HostListener, Input, NgZone, OnDestroy, OnInit, Renderer2 } from '@angular/core'; +import { Directive, ElementRef, HostListener, Input, NgZone, OnDestroy, OnInit, Renderer2, inject } from '@angular/core'; import { FileInfo, FileUtils } from '../common/utils/file-utils'; @Directive({ selector: '[adf-upload]' }) export class UploadDirective implements OnInit, OnDestroy { + private readonly el = inject(ElementRef); + private readonly renderer = inject(Renderer2); + private readonly ngZone = inject(NgZone); + /** Enables/disables uploading. */ @Input('adf-upload') enabled: boolean = true; @@ -53,11 +57,13 @@ export class UploadDirective implements OnInit, OnDestroy { isDragging: boolean = false; - private cssClassName: string = 'adf-upload__dragging'; + private readonly cssClassName: string = 'adf-upload__dragging'; private upload: HTMLInputElement; - private element: HTMLElement; + private readonly element: HTMLElement; + + constructor() { + const el = this.el; - constructor(private el: ElementRef, private renderer: Renderer2, private ngZone: NgZone) { this.element = el.nativeElement; } diff --git a/lib/core/src/lib/dynamic-chip-list/dynamic-chip-list.component.ts b/lib/core/src/lib/dynamic-chip-list/dynamic-chip-list.component.ts index 3313cd4ba9..a37ac3e046 100644 --- a/lib/core/src/lib/dynamic-chip-list/dynamic-chip-list.component.ts +++ b/lib/core/src/lib/dynamic-chip-list/dynamic-chip-list.component.ts @@ -32,7 +32,8 @@ import { SimpleChanges, ViewChild, ViewChildren, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatChip, MatChipsModule } from '@angular/material/chips'; @@ -51,6 +52,8 @@ import { IconModule } from '../icon/icon.module'; encapsulation: ViewEncapsulation.None }) export class DynamicChipListComponent implements OnChanges, OnInit, AfterViewInit, OnDestroy { + private readonly changeDetectorRef = inject(ChangeDetectorRef); + /* eslint no-underscore-dangle: ["error", { "allow": ["_elementRef"] }]*/ /** Provide if you want to use paginated chips. */ @Input() @@ -103,15 +106,13 @@ export class DynamicChipListComponent implements OnChanges, OnInit, AfterViewIni private initialLimitChipsDisplayed: boolean; private viewMoreButtonLeftOffsetBeforeFlexDirection: number; private requestedDisplayingAllChips = false; - private resizeObserver = new ResizeObserver(() => { + private readonly resizeObserver = new ResizeObserver(() => { if (this.initialLimitChipsDisplayed && this.chipsToDisplay.length) { this.calculateChipsToDisplay(); this.changeDetectorRef.detectChanges(); } }); - constructor(private changeDetectorRef: ChangeDetectorRef) {} - ngOnChanges(changes: SimpleChanges): void { if (changes.pagination) { this.limitChipsDisplayed = this.pagination?.hasMoreItems; diff --git a/lib/core/src/lib/form/components/form-renderer.component.ts b/lib/core/src/lib/form/components/form-renderer.component.ts index e532cd5c29..31803611d5 100644 --- a/lib/core/src/lib/form/components/form-renderer.component.ts +++ b/lib/core/src/lib/form/components/form-renderer.component.ts @@ -16,7 +16,7 @@ */ import { NgClass, NgForOf, NgIf, NgStyle, NgTemplateOutlet } from '@angular/common'; -import { ChangeDetectorRef, Component, Inject, inject, Injector, Input, OnDestroy, OnInit, Optional, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectorRef, Component, inject, Injector, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatTabsModule } from '@angular/material/tabs'; @@ -70,10 +70,12 @@ import { IconModule } from '../../icon/icon.module'; encapsulation: ViewEncapsulation.None }) export class FormRendererComponent<T> implements OnInit, OnDestroy { + private readonly middlewareServices = inject<FormFieldModelRenderMiddleware[]>(FORM_FIELD_MODEL_RENDER_MIDDLEWARE, { optional: true }) ?? []; + public readonly formService = inject(FormService); private readonly formRulesManager = inject(FormRulesManager<T>); private readonly dialog = inject(MatDialog); - private cdr = inject(ChangeDetectorRef); + private readonly cdr = inject(ChangeDetectorRef); @Input({ required: true }) formDefinition: FormModel; @@ -85,12 +87,6 @@ export class FormRendererComponent<T> implements OnInit, OnDestroy { fields: FormFieldModel[]; - constructor( - @Optional() - @Inject(FORM_FIELD_MODEL_RENDER_MIDDLEWARE) - private middlewareServices?: FormFieldModelRenderMiddleware[] - ) {} - ngOnInit(): void { this.runMiddlewareServices(); if (!this.readOnly) { diff --git a/lib/core/src/lib/form/components/widgets/amount/amount.widget.ts b/lib/core/src/lib/form/components/widgets/amount/amount.widget.ts index 861e5e352d..3ba65caae7 100644 --- a/lib/core/src/lib/form/components/widgets/amount/amount.widget.ts +++ b/lib/core/src/lib/form/components/widgets/amount/amount.widget.ts @@ -18,7 +18,7 @@ /* eslint-disable @angular-eslint/component-selector */ import { CurrencyPipe, NgIf } from '@angular/common'; -import { Component, OnInit, ViewEncapsulation, InjectionToken, Inject, Optional, inject, DestroyRef } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, InjectionToken, inject, DestroyRef } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; @@ -57,6 +57,9 @@ export const ADF_AMOUNT_SETTINGS = new InjectionToken<Observable<AmountWidgetSet encapsulation: ViewEncapsulation.None }) export class AmountWidgetComponent extends WidgetComponent implements OnInit { + private readonly currencyPipe = inject(CurrencyPipe); + private readonly translationService = inject(TranslationService); + static DEFAULT_CURRENCY: string = '$'; private showPlaceholder = true; private readonly destroyRef = inject(DestroyRef); @@ -85,11 +88,9 @@ export class AmountWidgetComponent extends WidgetComponent implements OnInit { return this.currencyPipe.transform(this.field.placeholder, this.currency, this.currencyDisplay, this.decimalProperty, this.locale); } - constructor( - @Optional() @Inject(ADF_AMOUNT_SETTINGS) settings: Observable<AmountWidgetSettings> | AmountWidgetSettings, - private currencyPipe: CurrencyPipe, - private translationService: TranslationService - ) { + constructor() { + const settings = inject<Observable<AmountWidgetSettings> | AmountWidgetSettings>(ADF_AMOUNT_SETTINGS, { optional: true }); + super(); if (isObservable(settings)) { settings.pipe(takeUntilDestroyed()).subscribe((data: AmountWidgetSettings) => { diff --git a/lib/core/src/lib/form/components/widgets/base-display-text/base-display-text.widget.ts b/lib/core/src/lib/form/components/widgets/base-display-text/base-display-text.widget.ts index b6b8287c83..0871f39f15 100644 --- a/lib/core/src/lib/form/components/widgets/base-display-text/base-display-text.widget.ts +++ b/lib/core/src/lib/form/components/widgets/base-display-text/base-display-text.widget.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ChangeDetectorRef, Component, inject, AfterViewInit, DestroyRef, InjectionToken, Optional, Inject } from '@angular/core'; +import { ChangeDetectorRef, Component, inject, AfterViewInit, DestroyRef, InjectionToken } from '@angular/core'; import { debounceTime, filter, isObservable, Observable } from 'rxjs'; import { FormRulesEvent } from '../../../events'; import { FormExpressionService } from '../../../services/form-expression.service'; @@ -39,14 +39,18 @@ export abstract class BaseDisplayTextWidgetComponent extends WidgetComponent imp private enableExpressionEvaluation: boolean = false; protected originalFieldValue?: string; - constructor(@Optional() @Inject(ADF_DISPLAY_TEXT_SETTINGS) settings: Observable<DisplayTextWidgetSettings> | DisplayTextWidgetSettings) { + private readonly settings = inject<Observable<DisplayTextWidgetSettings> | DisplayTextWidgetSettings>(ADF_DISPLAY_TEXT_SETTINGS, { + optional: true + }); + + constructor() { super(); - if (isObservable(settings)) { - settings.pipe(takeUntilDestroyed()).subscribe((data: DisplayTextWidgetSettings) => { + if (isObservable(this.settings)) { + this.settings.pipe(takeUntilDestroyed()).subscribe((data: DisplayTextWidgetSettings) => { this.updateSettingsBasedProperties(data); }); } else { - this.updateSettingsBasedProperties(settings); + this.updateSettingsBasedProperties(this.settings); } } diff --git a/lib/core/src/lib/form/components/widgets/core/form-field-validator.ts b/lib/core/src/lib/form/components/widgets/core/form-field-validator.ts index 7f26286f69..4fb03766cd 100644 --- a/lib/core/src/lib/form/components/widgets/core/form-field-validator.ts +++ b/lib/core/src/lib/form/components/widgets/core/form-field-validator.ts @@ -27,7 +27,7 @@ export interface FormFieldValidator { } export class RequiredFieldValidator implements FormFieldValidator { - private supportedTypes = [ + private readonly supportedTypes = [ FormFieldTypes.TEXT, FormFieldTypes.MULTILINE_TEXT, FormFieldTypes.NUMBER, @@ -78,7 +78,7 @@ export class RequiredFieldValidator implements FormFieldValidator { } export class NumberFieldValidator implements FormFieldValidator { - private supportedTypes = [FormFieldTypes.NUMBER, FormFieldTypes.AMOUNT]; + private readonly supportedTypes = [FormFieldTypes.NUMBER, FormFieldTypes.AMOUNT]; static isNumber(value: any): boolean { return isNumberValue(value); @@ -109,7 +109,7 @@ export class NumberFieldValidator implements FormFieldValidator { } export class MinLengthFieldValidator implements FormFieldValidator { - private supportedTypes = [FormFieldTypes.TEXT, FormFieldTypes.MULTILINE_TEXT]; + private readonly supportedTypes = [FormFieldTypes.TEXT, FormFieldTypes.MULTILINE_TEXT]; isSupported(field: FormFieldModel): boolean { return field && this.supportedTypes.indexOf(field.type) > -1 && field.minLength > 0; @@ -130,8 +130,8 @@ export class MinLengthFieldValidator implements FormFieldValidator { export class MaxLengthFieldValidator implements FormFieldValidator { constructor( - private supportedTypes: FormFieldTypes[] = [FormFieldTypes.TEXT, FormFieldTypes.MULTILINE_TEXT], - private maxLength?: number + private readonly supportedTypes: FormFieldTypes[] = [FormFieldTypes.TEXT, FormFieldTypes.MULTILINE_TEXT], + private readonly maxLength?: number ) {} isSupported(field: FormFieldModel): boolean { @@ -159,7 +159,7 @@ export class MaxLengthFieldValidator implements FormFieldValidator { } export class MinValueFieldValidator implements FormFieldValidator { - private supportedTypes = [FormFieldTypes.NUMBER, FormFieldTypes.DECIMAL, FormFieldTypes.AMOUNT]; + private readonly supportedTypes = [FormFieldTypes.NUMBER, FormFieldTypes.DECIMAL, FormFieldTypes.AMOUNT]; isSupported(field: FormFieldModel): boolean { return field && this.supportedTypes.indexOf(field.type) > -1 && NumberFieldValidator.isNumber(field.minValue); @@ -183,7 +183,7 @@ export class MinValueFieldValidator implements FormFieldValidator { } export class MaxValueFieldValidator implements FormFieldValidator { - private supportedTypes = [FormFieldTypes.NUMBER, FormFieldTypes.DECIMAL, FormFieldTypes.AMOUNT]; + private readonly supportedTypes = [FormFieldTypes.NUMBER, FormFieldTypes.DECIMAL, FormFieldTypes.AMOUNT]; isSupported(field: FormFieldModel): boolean { return field && this.supportedTypes.indexOf(field.type) > -1 && NumberFieldValidator.isNumber(field.maxValue); @@ -207,7 +207,7 @@ export class MaxValueFieldValidator implements FormFieldValidator { } export class RegExFieldValidator implements FormFieldValidator { - private supportedTypes = [FormFieldTypes.TEXT, FormFieldTypes.MULTILINE_TEXT]; + private readonly supportedTypes = [FormFieldTypes.TEXT, FormFieldTypes.MULTILINE_TEXT]; isSupported(field: FormFieldModel): boolean { return field && this.supportedTypes.indexOf(field.type) > -1 && !!field.regexPattern; @@ -226,7 +226,7 @@ export class RegExFieldValidator implements FormFieldValidator { } export class FixedValueFieldValidator implements FormFieldValidator { - private supportedTypes = [FormFieldTypes.TYPEAHEAD]; + private readonly supportedTypes = [FormFieldTypes.TYPEAHEAD]; isSupported(field: FormFieldModel): boolean { return field && this.supportedTypes.indexOf(field.type) > -1; @@ -264,7 +264,7 @@ export class FixedValueFieldValidator implements FormFieldValidator { } export class DecimalFieldValidator implements FormFieldValidator { - private supportedTypes = [FormFieldTypes.DECIMAL]; + private readonly supportedTypes = [FormFieldTypes.DECIMAL]; isSupported(field: FormFieldModel): boolean { return field && this.supportedTypes.indexOf(field.type) > -1 && !!field.value; diff --git a/lib/core/src/lib/form/components/widgets/core/form.model.spec.ts b/lib/core/src/lib/form/components/widgets/core/form.model.spec.ts index 0bc180995a..6883a2e65b 100644 --- a/lib/core/src/lib/form/components/widgets/core/form.model.spec.ts +++ b/lib/core/src/lib/form/components/widgets/core/form.model.spec.ts @@ -25,12 +25,16 @@ import { FormOutcomeModel } from './form-outcome.model'; import { FormModel } from './form.model'; import { TabModel } from './tab.model'; import { fakeMetadataForm, mockDisplayExternalPropertyForm, mockFormWithSections, fakeValidatorMock } from '../../mock/form.mock'; +import { TestBed } from '@angular/core/testing'; describe('FormModel', () => { let formService: FormService; beforeEach(() => { - formService = new FormService(); + TestBed.configureTestingModule({ + providers: [FormService] + }); + formService = TestBed.inject(FormService); }); it('should store original json', () => { diff --git a/lib/core/src/lib/form/components/widgets/json/json.widget.ts b/lib/core/src/lib/form/components/widgets/json/json.widget.ts index add918be27..1eb6870120 100644 --- a/lib/core/src/lib/form/components/widgets/json/json.widget.ts +++ b/lib/core/src/lib/form/components/widgets/json/json.widget.ts @@ -41,7 +41,7 @@ import { TranslatePipe } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None }) export class JsonWidgetComponent extends WidgetComponent { - private dialog = inject(MatDialog); + private readonly dialog = inject(MatDialog); view() { const rawValue = this.field.value; diff --git a/lib/core/src/lib/form/components/widgets/number/number.widget.ts b/lib/core/src/lib/form/components/widgets/number/number.widget.ts index 0a6e818dcc..8cabe2a308 100644 --- a/lib/core/src/lib/form/components/widgets/number/number.widget.ts +++ b/lib/core/src/lib/form/components/widgets/number/number.widget.ts @@ -49,7 +49,7 @@ import { WidgetComponent } from '../widget.component'; export class NumberWidgetComponent extends WidgetComponent implements OnInit { displayValue: number; - private decimalNumberPipe = inject(DecimalNumberPipe); + private readonly decimalNumberPipe = inject(DecimalNumberPipe); ngOnInit() { if (this.field.readOnly) { diff --git a/lib/core/src/lib/form/components/widgets/text/text-mask.component.ts b/lib/core/src/lib/form/components/widgets/text/text-mask.component.ts index 469759268f..a016e05069 100644 --- a/lib/core/src/lib/form/components/widgets/text/text-mask.component.ts +++ b/lib/core/src/lib/form/components/widgets/text/text-mask.component.ts @@ -17,7 +17,7 @@ /* eslint-disable @angular-eslint/component-selector, @typescript-eslint/no-use-before-define, @angular-eslint/no-input-rename */ -import { Directive, ElementRef, forwardRef, HostListener, Input, OnChanges, Renderer2, SimpleChanges } from '@angular/core'; +import { Directive, ElementRef, forwardRef, HostListener, Input, OnChanges, Renderer2, SimpleChanges, inject } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = { @@ -34,13 +34,16 @@ export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = { providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR] }) export class InputMaskDirective implements OnChanges, ControlValueAccessor { + private readonly el = inject(ElementRef); + private readonly render = inject(Renderer2); + /** Object defining mask and "reversed" status. */ @Input('textMask') inputMask: { mask: string; isReversed: boolean; }; - private translationMask = { + private readonly translationMask = { '0': { pattern: /\d/ }, '9': { pattern: /\d/, optional: true }, '#': { pattern: /\d/, recursive: true }, @@ -48,11 +51,9 @@ export class InputMaskDirective implements OnChanges, ControlValueAccessor { S: { pattern: /[a-zA-Z]/ } }; - private byPassKeys = [9, 16, 17, 18, 36, 37, 38, 39, 40, 91]; + private readonly byPassKeys = [9, 16, 17, 18, 36, 37, 38, 39, 40, 91]; private value; - private invalidCharacters = []; - - constructor(private el: ElementRef, private render: Renderer2) {} + private readonly invalidCharacters = []; _onChange = (_: any) => {}; diff --git a/lib/core/src/lib/form/models/widget-visibility.model.ts b/lib/core/src/lib/form/models/widget-visibility.model.ts index e8ccc1b0af..47a3079973 100644 --- a/lib/core/src/lib/form/models/widget-visibility.model.ts +++ b/lib/core/src/lib/form/models/widget-visibility.model.ts @@ -24,7 +24,7 @@ export class WidgetVisibilityModel { nextCondition?: WidgetVisibilityModel; nextConditionOperator: string; - constructor(private json?: any) { + constructor(private readonly json?: any) { if (json) { this.operator = json.operator; this.nextCondition = new WidgetVisibilityModel(json.nextCondition); diff --git a/lib/core/src/lib/form/services/form.service.ts b/lib/core/src/lib/form/services/form.service.ts index d562e3973c..0632e51829 100644 --- a/lib/core/src/lib/form/services/form.service.ts +++ b/lib/core/src/lib/form/services/form.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Inject, Injectable, InjectionToken, Optional } from '@angular/core'; +import { Injectable, InjectionToken, inject } from '@angular/core'; import { Subject } from 'rxjs'; import { ContentLinkModel } from '../components/widgets/core/content-link.model'; import { FormOutcomeEvent } from '../components/widgets/core/form-outcome-event.model'; @@ -38,7 +38,7 @@ export const FORM_SERVICE_FIELD_VALIDATORS_TOKEN = new InjectionToken<FormFieldV providedIn: 'root' }) export class FormService implements FormValidationService { - private fieldValidators: FormFieldValidator[]; + private readonly fieldValidators: FormFieldValidator[]; formLoaded = new Subject<FormEvent>(); formDataRefreshed = new Subject<FormEvent>(); formFieldValueChanged = new Subject<FormFieldEvent>(); @@ -62,7 +62,9 @@ export class FormService implements FormValidationService { formRulesEvent = new Subject<FormRulesEvent>(); - constructor(@Optional() @Inject(FORM_SERVICE_FIELD_VALIDATORS_TOKEN) injectedFieldValidators?: FormFieldValidator[]) { + constructor() { + const injectedFieldValidators = inject(FORM_SERVICE_FIELD_VALIDATORS_TOKEN, { optional: true }); + this.fieldValidators = injectedFieldValidators || []; } diff --git a/lib/core/src/lib/language-menu/language-menu.component.ts b/lib/core/src/lib/language-menu/language-menu.component.ts index e6d169dbe3..8ab2a4a262 100644 --- a/lib/core/src/lib/language-menu/language-menu.component.ts +++ b/lib/core/src/lib/language-menu/language-menu.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, Output, QueryList, ViewChildren } from '@angular/core'; +import { Component, EventEmitter, Output, QueryList, ViewChildren, inject } from '@angular/core'; import { LanguageService } from './service/language.service'; import { Observable } from 'rxjs'; import { LanguageItem } from '../common/services/language-item.interface'; @@ -32,6 +32,8 @@ import { MatMenuItem, MatMenuModule } from '@angular/material/menu'; ` }) export class LanguageMenuComponent { + private readonly languageService = inject(LanguageService); + /** Emitted when the language change */ @Output() changedLanguage: EventEmitter<LanguageItem> = new EventEmitter<LanguageItem>(); @@ -41,7 +43,9 @@ export class LanguageMenuComponent { languages$: Observable<LanguageItem[]>; - constructor(private languageService: LanguageService) { + constructor() { + const languageService = this.languageService; + this.languages$ = languageService.languages$; } diff --git a/lib/core/src/lib/language-menu/service/language.service.spec.ts b/lib/core/src/lib/language-menu/service/language.service.spec.ts index 2e54d61b3f..6b9eb76c97 100644 --- a/lib/core/src/lib/language-menu/service/language.service.spec.ts +++ b/lib/core/src/lib/language-menu/service/language.service.spec.ts @@ -21,6 +21,7 @@ import { AppConfigService } from '../../app-config/app-config.service'; import { UserPreferencesService } from '../../common/services/user-preferences.service'; import { DEFAULT_LANGUAGE_LIST } from '../../common/models/default-languages.model'; import { LanguageItem } from '../../common/services/language-item.interface'; +import { Injector, runInInjectionContext } from '@angular/core'; describe('LanguageService', () => { let service: LanguageService; @@ -54,7 +55,16 @@ describe('LanguageService', () => { it('should initialize with default languages when no custom languages are provided', (done) => { appConfigService.get.and.returnValue(null); - service = new LanguageService(appConfigService, userPreferencesService); + + const injector = Injector.create({ + providers: [ + { provide: AppConfigService, useValue: appConfigService }, + { provide: UserPreferencesService, useValue: userPreferencesService } + ], + parent: TestBed.inject(Injector) + }); + + service = runInInjectionContext(injector, () => new LanguageService()); service.languages$.subscribe((languages) => { expect(languages).toEqual(DEFAULT_LANGUAGE_LIST); @@ -64,7 +74,16 @@ describe('LanguageService', () => { it('should initialize with custom languages when provided', (done) => { appConfigService.get.and.returnValue(customLanguages); - service = new LanguageService(appConfigService, userPreferencesService); + + const injector = Injector.create({ + providers: [ + { provide: AppConfigService, useValue: appConfigService }, + { provide: UserPreferencesService, useValue: userPreferencesService } + ], + parent: TestBed.inject(Injector) + }); + + service = runInInjectionContext(injector, () => new LanguageService()); service.languages$.subscribe((languages) => { expect(languages).toEqual(customLanguages); diff --git a/lib/core/src/lib/language-menu/service/language.service.ts b/lib/core/src/lib/language-menu/service/language.service.ts index a9c3fe8573..32f36fd484 100644 --- a/lib/core/src/lib/language-menu/service/language.service.ts +++ b/lib/core/src/lib/language-menu/service/language.service.ts @@ -16,7 +16,7 @@ */ import { LanguageServiceInterface } from './language.service.interface'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service'; import { LanguageItem } from '../../common/services/language-item.interface'; @@ -25,11 +25,15 @@ import { DEFAULT_LANGUAGE_LIST } from '../../common/models/default-languages.mod @Injectable({ providedIn: 'root' }) export class LanguageService implements LanguageServiceInterface { - private languages = new BehaviorSubject<LanguageItem[]>(DEFAULT_LANGUAGE_LIST); + private readonly userPreferencesService = inject(UserPreferencesService); + + private readonly languages = new BehaviorSubject<LanguageItem[]>(DEFAULT_LANGUAGE_LIST); languages$ = this.languages.asObservable(); - constructor(appConfigService: AppConfigService, private readonly userPreferencesService: UserPreferencesService) { + constructor() { + const appConfigService = inject(AppConfigService); + const customLanguages = appConfigService.get<Array<LanguageItem>>(AppConfigValues.APP_CONFIG_LANGUAGES_KEY); this.setLanguages(customLanguages); } diff --git a/lib/core/src/lib/layout/components/header/header.component.ts b/lib/core/src/lib/layout/components/header/header.component.ts index cd2524292d..89d9ec554f 100644 --- a/lib/core/src/lib/layout/components/header/header.component.ts +++ b/lib/core/src/lib/layout/components/header/header.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Input, Output, EventEmitter, ViewEncapsulation, OnInit } from '@angular/core'; +import { Component, Input, Output, EventEmitter, ViewEncapsulation, OnInit, inject } from '@angular/core'; import { ThemePalette } from '@angular/material/core'; import { AppConfigService } from '../../../app-config/app-config.service'; import { CommonModule } from '@angular/common'; @@ -34,6 +34,8 @@ import { IconModule } from '../../../icon/icon.module'; host: { class: 'adf-layout-header' } }) export class HeaderLayoutComponent implements OnInit { + private readonly appConfigService = inject(AppConfigService); + /** Title of the application. */ @Input() title: string; @@ -76,8 +78,6 @@ export class HeaderLayoutComponent implements OnInit { /** The side of the page that the drawer is attached to (can be 'start' or 'end') */ @Input() position = 'start'; - constructor(private appConfigService: AppConfigService) {} - toggleMenu() { this.clicked.emit(true); this.expandedSidenav = !this.expandedSidenav; diff --git a/lib/core/src/lib/layout/components/sidenav-layout/sidenav-layout.component.ts b/lib/core/src/lib/layout/components/sidenav-layout/sidenav-layout.component.ts index f6060dc26d..4c785ec75a 100644 --- a/lib/core/src/lib/layout/components/sidenav-layout/sidenav-layout.component.ts +++ b/lib/core/src/lib/layout/components/sidenav-layout/sidenav-layout.component.ts @@ -51,6 +51,10 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; host: { class: 'adf-sidenav-layout' } }) export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy { + private readonly mediaMatcher = inject(MediaMatcher); + private readonly userPreferencesService = inject(UserPreferencesService); + private readonly changeDetectorRef = inject(ChangeDetectorRef); + static STEP_OVER = 600; /** The direction of the layout. 'ltr' or 'rtl' */ @@ -86,7 +90,7 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy @ContentChild(SidenavLayoutContentDirective) contentDirective: SidenavLayoutContentDirective; - private menuOpenStateSubject = new BehaviorSubject<boolean>(false); + private readonly menuOpenStateSubject = new BehaviorSubject<boolean>(false); public menuOpenState$ = this.menuOpenStateSubject.asObservable(); @ViewChild('container', { static: true }) container: any; @@ -102,11 +106,7 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy private readonly destroyRef = inject(DestroyRef); - constructor( - private readonly mediaMatcher: MediaMatcher, - private readonly userPreferencesService: UserPreferencesService, - private readonly changeDetectorRef: ChangeDetectorRef - ) { + constructor() { this.onMediaQueryChange = this.onMediaQueryChange.bind(this); } diff --git a/lib/core/src/lib/login/components/login/login.component.ts b/lib/core/src/lib/login/components/login/login.component.ts index 8c0fa303f1..372d6d8e8a 100644 --- a/lib/core/src/lib/login/components/login/login.component.ts +++ b/lib/core/src/lib/login/components/login/login.component.ts @@ -75,6 +75,16 @@ interface LoginFormValues { host: { class: 'adf-login' } }) export class LoginComponent implements OnInit { + private readonly _fb = inject(UntypedFormBuilder); + private readonly authService = inject(AuthenticationService); + private readonly basicAlfrescoAuthService = inject(BasicAlfrescoAuthService); + private readonly oidcAuthenticationService = inject(OidcAuthenticationService); + private readonly translateService = inject(TranslationService); + private readonly router = inject(Router); + private readonly appConfig = inject(AppConfigService); + private readonly userPreferences = inject(UserPreferencesService); + private readonly route = inject(ActivatedRoute); + isPasswordShow: boolean = false; /** @@ -147,18 +157,6 @@ export class LoginComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private _fb: UntypedFormBuilder, - private authService: AuthenticationService, - private basicAlfrescoAuthService: BasicAlfrescoAuthService, - private oidcAuthenticationService: OidcAuthenticationService, - private translateService: TranslationService, - private router: Router, - private appConfig: AppConfigService, - private userPreferences: UserPreferencesService, - private route: ActivatedRoute - ) {} - ngOnInit() { this.initFormError(); this.initFormFieldsDefault(); diff --git a/lib/core/src/lib/login/directives/login-footer.directive.spec.ts b/lib/core/src/lib/login/directives/login-footer.directive.spec.ts index f043f44769..1e9217e8f5 100644 --- a/lib/core/src/lib/login/directives/login-footer.directive.spec.ts +++ b/lib/core/src/lib/login/directives/login-footer.directive.spec.ts @@ -20,6 +20,7 @@ import { OidcAuthenticationService } from '../../auth/oidc/oidc-authentication.s import { LoginComponent } from '../components/login/login.component'; import { LoginFooterDirective } from './login-footer.directive'; import { provideCoreAuthTesting } from '../../testing'; +import { Injector, runInInjectionContext } from '@angular/core'; describe('LoginFooterDirective', () => { let fixture: ComponentFixture<LoginComponent>; @@ -39,7 +40,13 @@ describe('LoginFooterDirective', () => { }); fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; - directive = new LoginFooterDirective(component); + + const injector = Injector.create({ + providers: [{ provide: LoginComponent, useValue: component }], + parent: TestBed.inject(Injector) + }); + + directive = runInInjectionContext(injector, () => new LoginFooterDirective()); }); afterEach(() => { diff --git a/lib/core/src/lib/login/directives/login-footer.directive.ts b/lib/core/src/lib/login/directives/login-footer.directive.ts index dd9ca7e1ca..7ff6427cac 100644 --- a/lib/core/src/lib/login/directives/login-footer.directive.ts +++ b/lib/core/src/lib/login/directives/login-footer.directive.ts @@ -15,18 +15,18 @@ * limitations under the License. */ -import { AfterContentInit, ContentChild, Directive, TemplateRef } from '@angular/core'; +import { AfterContentInit, ContentChild, Directive, TemplateRef, inject } from '@angular/core'; import { LoginComponent } from '../components/login/login.component'; @Directive({ selector: 'adf-login-footer' }) export class LoginFooterDirective implements AfterContentInit { + private readonly alfrescoLoginComponent = inject(LoginComponent); + @ContentChild(TemplateRef) template: any; - constructor(private alfrescoLoginComponent: LoginComponent) {} - ngAfterContentInit() { this.alfrescoLoginComponent.footerTemplate = this.template; } diff --git a/lib/core/src/lib/login/directives/login-header.directive.spec.ts b/lib/core/src/lib/login/directives/login-header.directive.spec.ts index 607a17e1c2..36c3ee18e2 100644 --- a/lib/core/src/lib/login/directives/login-header.directive.spec.ts +++ b/lib/core/src/lib/login/directives/login-header.directive.spec.ts @@ -20,6 +20,7 @@ import { OidcAuthenticationService } from '../../auth/oidc/oidc-authentication.s import { LoginComponent } from '../components/login/login.component'; import { LoginHeaderDirective } from './login-header.directive'; import { provideCoreAuthTesting } from '../../testing'; +import { Injector, runInInjectionContext } from '@angular/core'; describe('LoginHeaderDirective', () => { let fixture: ComponentFixture<LoginComponent>; @@ -33,7 +34,13 @@ describe('LoginHeaderDirective', () => { }); fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; - directive = new LoginHeaderDirective(component); + + const injector = Injector.create({ + providers: [{ provide: LoginComponent, useValue: component }], + parent: TestBed.inject(Injector) + }); + + directive = runInInjectionContext(injector, () => new LoginHeaderDirective()); }); afterEach(() => { diff --git a/lib/core/src/lib/login/directives/login-header.directive.ts b/lib/core/src/lib/login/directives/login-header.directive.ts index 1ca48d171e..462a6bcdd6 100644 --- a/lib/core/src/lib/login/directives/login-header.directive.ts +++ b/lib/core/src/lib/login/directives/login-header.directive.ts @@ -15,18 +15,18 @@ * limitations under the License. */ -import { AfterContentInit, ContentChild, Directive, TemplateRef } from '@angular/core'; +import { AfterContentInit, ContentChild, Directive, TemplateRef, inject } from '@angular/core'; import { LoginComponent } from '../components/login/login.component'; @Directive({ selector: 'adf-login-header' }) export class LoginHeaderDirective implements AfterContentInit { + private readonly alfrescoLoginComponent = inject(LoginComponent); + @ContentChild(TemplateRef) template: any; - constructor(private alfrescoLoginComponent: LoginComponent) {} - ngAfterContentInit() { this.alfrescoLoginComponent.headerTemplate = this.template; } diff --git a/lib/core/src/lib/mock/language.service.mock.ts b/lib/core/src/lib/mock/language.service.mock.ts index acf43eddd2..c6e7376bb5 100644 --- a/lib/core/src/lib/mock/language.service.mock.ts +++ b/lib/core/src/lib/mock/language.service.mock.ts @@ -22,7 +22,7 @@ import { LanguageItem } from '../common/services/language-item.interface'; @Injectable() export class LanguageServiceMock implements LanguageServiceInterface { - private languages = new BehaviorSubject<LanguageItem[]>([ + private readonly languages = new BehaviorSubject<LanguageItem[]>([ { key: 'de', label: 'Deutsch' }, { key: 'en', label: 'English' }, { key: 'es', label: 'Español' }, diff --git a/lib/core/src/lib/notifications/components/add-notification-component.mock.ts b/lib/core/src/lib/notifications/components/add-notification-component.mock.ts index fcf1981748..d2ddd8f6ae 100644 --- a/lib/core/src/lib/notifications/components/add-notification-component.mock.ts +++ b/lib/core/src/lib/notifications/components/add-notification-component.mock.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { NotificationService } from '../services/notification.service'; import { MatButtonModule } from '@angular/material/button'; @@ -25,9 +25,9 @@ import { MatButtonModule } from '@angular/material/button'; template: ` <button mat-raised-button (click)="showInfo()">Add Notification</button>` }) export class AddNotificationStorybookComponent { - infoCounter: number = 1; + private readonly notificationService = inject(NotificationService); - constructor(private notificationService: NotificationService) {} + infoCounter: number = 1; showInfo() { this.notificationService.showInfo(`Example notification ${this.infoCounter}`); diff --git a/lib/core/src/lib/notifications/components/notification-history.component.ts b/lib/core/src/lib/notifications/components/notification-history.component.ts index 6031b9abde..7652203aa9 100644 --- a/lib/core/src/lib/notifications/components/notification-history.component.ts +++ b/lib/core/src/lib/notifications/components/notification-history.component.ts @@ -64,6 +64,10 @@ import { IconModule } from '../../icon/icon.module'; encapsulation: ViewEncapsulation.None }) export class NotificationHistoryComponent implements OnInit, AfterViewInit { + private readonly notificationService = inject(NotificationService); + private readonly storageService = inject(StorageService); + private readonly cd = inject(ChangeDetectorRef); + public static MAX_NOTIFICATION_STACK_LENGTH = 100; public static NOTIFICATION_STORAGE = 'notification-history'; @@ -99,12 +103,6 @@ export class NotificationHistoryComponent implements OnInit, AfterViewInit { private focusKeyManager: FocusKeyManager<MatIconButton | MatMenuItem | MatButton>; - constructor( - private readonly notificationService: NotificationService, - private readonly storageService: StorageService, - private readonly cd: ChangeDetectorRef - ) {} - ngOnInit() { this.notifications = JSON.parse(this.storageService.getItem(NotificationHistoryComponent.NOTIFICATION_STORAGE)) || []; } diff --git a/lib/core/src/lib/notifications/services/notification.service.spec.ts b/lib/core/src/lib/notifications/services/notification.service.spec.ts index 0408ff5432..7453098852 100644 --- a/lib/core/src/lib/notifications/services/notification.service.spec.ts +++ b/lib/core/src/lib/notifications/services/notification.service.spec.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component } from '@angular/core'; +import { Component, inject as inject_1 } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatSnackBarConfig, MatSnackBarModule } from '@angular/material/snack-bar'; import { NotificationService } from './notification.service'; @@ -29,7 +29,7 @@ import { UnitTestingUtils } from '../../testing/unit-testing-utils'; providers: [NotificationService] }) class ProvidesNotificationServiceComponent { - constructor(public notificationService: NotificationService) {} + notificationService = inject_1(NotificationService); sendMessageWithoutConfig() { return this.notificationService.openSnackMessage('Test notification'); diff --git a/lib/core/src/lib/notifications/services/notification.service.ts b/lib/core/src/lib/notifications/services/notification.service.ts index d899f85a9d..5295ecff6f 100644 --- a/lib/core/src/lib/notifications/services/notification.service.ts +++ b/lib/core/src/lib/notifications/services/notification.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { MatSnackBar, MatSnackBarRef, MatSnackBarConfig } from '@angular/material/snack-bar'; import { TranslationService } from '../../translation/translation.service'; import { Subject } from 'rxjs'; @@ -32,9 +32,10 @@ const ERROR_SNACK_CLASS = 'adf-error-snackbar'; providedIn: 'root' }) export class NotificationService { - notifications$: Subject<NotificationModel> = new Subject(); + private readonly snackBar = inject(MatSnackBar); + private readonly translationService = inject(TranslationService); - constructor(private snackBar: MatSnackBar, private translationService: TranslationService) {} + notifications$: Subject<NotificationModel> = new Subject(); /** * Opens a SnackBar notification to show a message. diff --git a/lib/core/src/lib/pagination/infinite-pagination.component.ts b/lib/core/src/lib/pagination/infinite-pagination.component.ts index 009c80f95d..02314b70a5 100644 --- a/lib/core/src/lib/pagination/infinite-pagination.component.ts +++ b/lib/core/src/lib/pagination/infinite-pagination.component.ts @@ -52,6 +52,9 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; imports: [CommonModule, MatButtonModule, MatProgressBarModule, TranslatePipe] }) export class InfinitePaginationComponent implements OnInit, PaginationComponentInterface { + private readonly cdr = inject(ChangeDetectorRef); + private readonly userPreferencesService = inject(UserPreferencesService); + static DEFAULT_PAGINATION: PaginationModel = new PaginationModel({ skipCount: 0, maxItems: 25, @@ -103,8 +106,6 @@ export class InfinitePaginationComponent implements OnInit, PaginationComponentI private readonly destroyRef = inject(DestroyRef); - constructor(private cdr: ChangeDetectorRef, private userPreferencesService: UserPreferencesService) {} - ngOnInit() { this.userPreferencesService .select(UserPreferenceValues.PaginationSize) diff --git a/lib/core/src/lib/pagination/pagination.component.ts b/lib/core/src/lib/pagination/pagination.component.ts index 73785d38e2..fa106a0f42 100644 --- a/lib/core/src/lib/pagination/pagination.component.ts +++ b/lib/core/src/lib/pagination/pagination.component.ts @@ -60,6 +60,12 @@ export const DEFAULT_PAGINATION: PaginationModel = { imports: [CommonModule, TranslatePipe, MatButtonModule, IconModule, MatMenuModule] }) export class PaginationComponent implements OnInit, PaginationComponentInterface { + private readonly elementRef = inject(ElementRef); + private readonly renderer = inject(Renderer2); + private readonly cdr = inject(ChangeDetectorRef); + private readonly userPreferencesService = inject(UserPreferencesService); + private readonly translate = inject(TranslateService); + private _pagination: PaginationModel; private _isEmpty = true; private _hasItems = false; @@ -118,14 +124,6 @@ export class PaginationComponent implements OnInit, PaginationComponentInterface private readonly destroyRef = inject(DestroyRef); - constructor( - private elementRef: ElementRef, - private renderer: Renderer2, - private cdr: ChangeDetectorRef, - private userPreferencesService: UserPreferencesService, - private translate: TranslateService - ) {} - ngOnInit() { this.userPreferencesService .select(UserPreferenceValues.PaginationSize) diff --git a/lib/core/src/lib/pipes/decimal-number.pipe.spec.ts b/lib/core/src/lib/pipes/decimal-number.pipe.spec.ts index 62ece4f0ec..4938ebf08e 100644 --- a/lib/core/src/lib/pipes/decimal-number.pipe.spec.ts +++ b/lib/core/src/lib/pipes/decimal-number.pipe.spec.ts @@ -19,7 +19,6 @@ import { TestBed } from '@angular/core/testing'; import { UserPreferencesService } from '../common/services/user-preferences.service'; import { of } from 'rxjs'; import { DecimalNumberPipe } from './decimal-number.pipe'; -import { Injector, runInInjectionContext } from '@angular/core'; import { AppConfigService } from '@alfresco/adf-core'; describe('DecimalNumberPipe', () => { @@ -27,12 +26,13 @@ describe('DecimalNumberPipe', () => { let userPreferences: UserPreferencesService; beforeEach(() => { - userPreferences = TestBed.inject(UserPreferencesService); - const injector = TestBed.inject(Injector); - spyOn(userPreferences, 'select').and.returnValue(of('')); - runInInjectionContext(injector, () => { - pipe = new DecimalNumberPipe(userPreferences, TestBed.inject(AppConfigService)); + TestBed.configureTestingModule({ + providers: [DecimalNumberPipe, UserPreferencesService, AppConfigService] }); + + userPreferences = TestBed.inject(UserPreferencesService); + spyOn(userPreferences, 'select').and.returnValue(of('')); + pipe = TestBed.inject(DecimalNumberPipe); }); it('should return number localized and rounded following the default config', () => { diff --git a/lib/core/src/lib/pipes/decimal-number.pipe.ts b/lib/core/src/lib/pipes/decimal-number.pipe.ts index e4c68aabd6..ff40324b2b 100644 --- a/lib/core/src/lib/pipes/decimal-number.pipe.ts +++ b/lib/core/src/lib/pipes/decimal-number.pipe.ts @@ -16,7 +16,7 @@ */ import { DecimalPipe } from '@angular/common'; -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { AppConfigService } from '../app-config/app-config.service'; import { UserPreferencesService } from '../common/services/user-preferences.service'; import { DecimalNumberModel } from '../models/decimal-number.model'; @@ -26,6 +26,9 @@ import { DecimalNumberModel } from '../models/decimal-number.model'; pure: false }) export class DecimalNumberPipe implements PipeTransform { + userPreferenceService? = inject(UserPreferencesService); + appConfig? = inject(AppConfigService); + static DEFAULT_LOCALE = 'en-US'; static DEFAULT_MIN_INTEGER_DIGITS = 1; static DEFAULT_MIN_FRACTION_DIGITS = 0; @@ -35,10 +38,7 @@ export class DecimalNumberPipe implements PipeTransform { defaultMinFractionDigits: number = DecimalNumberPipe.DEFAULT_MIN_FRACTION_DIGITS; defaultMaxFractionDigits: number = DecimalNumberPipe.DEFAULT_MAX_FRACTION_DIGITS; - constructor( - public userPreferenceService?: UserPreferencesService, - public appConfig?: AppConfigService - ) { + constructor() { if (this.appConfig) { this.defaultMinIntegerDigits = this.appConfig.get<number>('decimalValues.minIntegerDigits', DecimalNumberPipe.DEFAULT_MIN_INTEGER_DIGITS); this.defaultMinFractionDigits = this.appConfig.get<number>( diff --git a/lib/core/src/lib/pipes/file-size.pipe.spec.ts b/lib/core/src/lib/pipes/file-size.pipe.spec.ts index 0e1a66a14a..665047edec 100644 --- a/lib/core/src/lib/pipes/file-size.pipe.spec.ts +++ b/lib/core/src/lib/pipes/file-size.pipe.spec.ts @@ -16,6 +16,8 @@ */ import { FileSizePipe } from './file-size.pipe'; +import { TestBed } from '@angular/core/testing'; +import { TranslationService } from '../translation/translation.service'; describe('FileSizePipe', () => { let pipe: FileSizePipe; @@ -37,7 +39,12 @@ describe('FileSizePipe', () => { return enUs[key]; } }; - pipe = new FileSizePipe(translation); + + TestBed.configureTestingModule({ + providers: [FileSizePipe, { provide: TranslationService, useValue: translation }] + }); + + pipe = TestBed.inject(FileSizePipe); }); it('returns empty string with invalid input', () => { diff --git a/lib/core/src/lib/pipes/file-size.pipe.ts b/lib/core/src/lib/pipes/file-size.pipe.ts index 379fbe52c9..60f63d6712 100644 --- a/lib/core/src/lib/pipes/file-size.pipe.ts +++ b/lib/core/src/lib/pipes/file-size.pipe.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { TranslationService } from '../translation/translation.service'; @Pipe({ @@ -23,7 +23,7 @@ import { TranslationService } from '../translation/translation.service'; pure: false }) export class FileSizePipe implements PipeTransform { - constructor(private translation: TranslationService) {} + private readonly translation = inject(TranslationService); transform(paramByte: any, decimals: number = 2): string { if (paramByte == null) { diff --git a/lib/core/src/lib/pipes/localized-date.pipe.spec.ts b/lib/core/src/lib/pipes/localized-date.pipe.spec.ts index 6ff68d2030..a27059ca08 100644 --- a/lib/core/src/lib/pipes/localized-date.pipe.spec.ts +++ b/lib/core/src/lib/pipes/localized-date.pipe.spec.ts @@ -29,9 +29,13 @@ describe('LocalizedDatePipe', () => { let userPreferences: UserPreferencesService; beforeEach(() => { + TestBed.configureTestingModule({ + providers: [LocalizedDatePipe, UserPreferencesService, AppConfigService] + }); + userPreferences = TestBed.inject(UserPreferencesService); spyOn(userPreferences, 'select').and.returnValue(of('')); - pipe = new LocalizedDatePipe(userPreferences, TestBed.inject(AppConfigService)); + pipe = TestBed.inject(LocalizedDatePipe); }); it('should return time with locale en-US', () => { diff --git a/lib/core/src/lib/pipes/localized-date.pipe.ts b/lib/core/src/lib/pipes/localized-date.pipe.ts index 03f265b3f7..0aa8884e8a 100644 --- a/lib/core/src/lib/pipes/localized-date.pipe.ts +++ b/lib/core/src/lib/pipes/localized-date.pipe.ts @@ -16,7 +16,7 @@ */ import { DatePipe } from '@angular/common'; -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { AppConfigService } from '../app-config/app-config.service'; import { UserPreferencesService } from '../common/services/user-preferences.service'; @@ -26,15 +26,15 @@ import { UserPreferencesService } from '../common/services/user-preferences.serv pure: false }) export class LocalizedDatePipe implements PipeTransform { + userPreferenceService? = inject(UserPreferencesService); + appConfig? = inject(AppConfigService); + static DEFAULT_LOCALE = 'en-US'; static DEFAULT_DATE_FORMAT = 'mediumDate'; defaultFormat: string = LocalizedDatePipe.DEFAULT_DATE_FORMAT; - constructor( - public userPreferenceService?: UserPreferencesService, - public appConfig?: AppConfigService - ) { + constructor() { if (this.appConfig) { this.defaultFormat = this.appConfig.get<string>('dateValues.defaultDateFormat', LocalizedDatePipe.DEFAULT_DATE_FORMAT); } diff --git a/lib/core/src/lib/pipes/text-highlight.pipe.ts b/lib/core/src/lib/pipes/text-highlight.pipe.ts index 9474aa216e..3e209bf85e 100644 --- a/lib/core/src/lib/pipes/text-highlight.pipe.ts +++ b/lib/core/src/lib/pipes/text-highlight.pipe.ts @@ -15,14 +15,14 @@ * limitations under the License. */ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { HighlightTransformService, HighlightTransformResult } from '../common/services/highlight-transform.service'; @Pipe({ name: 'highlight' }) export class HighlightPipe implements PipeTransform { - constructor(private highlightTransformService: HighlightTransformService) {} + private readonly highlightTransformService = inject(HighlightTransformService); transform(text: string, search: string): string { const highlightTransformResult: HighlightTransformResult = this.highlightTransformService.highlight(text, search); diff --git a/lib/core/src/lib/pipes/time-ago.pipe.spec.ts b/lib/core/src/lib/pipes/time-ago.pipe.spec.ts index 8b7fe09744..f08c384bff 100644 --- a/lib/core/src/lib/pipes/time-ago.pipe.spec.ts +++ b/lib/core/src/lib/pipes/time-ago.pipe.spec.ts @@ -20,19 +20,19 @@ import { TestBed } from '@angular/core/testing'; import { AppConfigService } from '../app-config/app-config.service'; import { UserPreferencesService } from '../common/services/user-preferences.service'; import { of } from 'rxjs'; -import { Injector, runInInjectionContext } from '@angular/core'; describe('TimeAgoPipe', () => { let pipe: TimeAgoPipe; let userPreferences: UserPreferencesService; beforeEach(() => { - userPreferences = TestBed.inject(UserPreferencesService); - const injector = TestBed.inject(Injector); - spyOn(userPreferences, 'select').and.returnValue(of('')); - runInInjectionContext(injector, () => { - pipe = new TimeAgoPipe(userPreferences, TestBed.inject(AppConfigService)); + TestBed.configureTestingModule({ + providers: [TimeAgoPipe, UserPreferencesService, AppConfigService] }); + + userPreferences = TestBed.inject(UserPreferencesService); + spyOn(userPreferences, 'select').and.returnValue(of('')); + pipe = TestBed.inject(TimeAgoPipe); }); it('should return time difference for a given date', () => { diff --git a/lib/core/src/lib/pipes/time-ago.pipe.ts b/lib/core/src/lib/pipes/time-ago.pipe.ts index cd35ddcbab..ee3850c639 100644 --- a/lib/core/src/lib/pipes/time-ago.pipe.ts +++ b/lib/core/src/lib/pipes/time-ago.pipe.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { AppConfigService } from '../app-config/app-config.service'; import { UserPreferencesService } from '../common/services/user-preferences.service'; import { DatePipe } from '@angular/common'; @@ -28,15 +28,15 @@ import { DateFnsUtils } from '../common/utils/date-fns-utils'; pure: false }) export class TimeAgoPipe implements PipeTransform { + userPreferenceService = inject(UserPreferencesService); + appConfig = inject(AppConfigService); + static DEFAULT_LOCALE = 'en-US'; static DEFAULT_DATE_TIME_FORMAT = 'dd/MM/yyyy HH:mm'; defaultDateTimeFormat: string; - constructor( - public userPreferenceService: UserPreferencesService, - public appConfig: AppConfigService - ) { + constructor() { this.defaultDateTimeFormat = this.appConfig.get<string>('dateValues.defaultDateTimeFormat', TimeAgoPipe.DEFAULT_DATE_TIME_FORMAT); } diff --git a/lib/core/src/lib/pipes/user-initial.pipe.spec.ts b/lib/core/src/lib/pipes/user-initial.pipe.spec.ts index 3ba3abcc68..857c76e124 100644 --- a/lib/core/src/lib/pipes/user-initial.pipe.spec.ts +++ b/lib/core/src/lib/pipes/user-initial.pipe.spec.ts @@ -18,6 +18,7 @@ import { DomSanitizer } from '@angular/platform-browser'; import { User } from '../models/general-user.model'; import { InitialUsernamePipe } from './user-initial.pipe'; +import { TestBed } from '@angular/core/testing'; class FakeSanitizer extends DomSanitizer { constructor() { @@ -54,7 +55,10 @@ describe('UserInitialPipe', () => { let fakeUser: User; beforeEach(() => { - pipe = new InitialUsernamePipe(new FakeSanitizer()); + TestBed.configureTestingModule({ + providers: [InitialUsernamePipe, { provide: DomSanitizer, useClass: FakeSanitizer }] + }); + pipe = TestBed.inject(InitialUsernamePipe); fakeUser = { username: '' } as User; }); diff --git a/lib/core/src/lib/pipes/user-initial.pipe.ts b/lib/core/src/lib/pipes/user-initial.pipe.ts index 6adb4d3d0d..cd52046f5e 100644 --- a/lib/core/src/lib/pipes/user-initial.pipe.ts +++ b/lib/core/src/lib/pipes/user-initial.pipe.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Pipe, PipeTransform } from '@angular/core'; +import { Pipe, PipeTransform, inject } from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { UserLike } from './user-like.interface'; @@ -23,7 +23,7 @@ import { UserLike } from './user-like.interface'; name: 'usernameInitials' }) export class InitialUsernamePipe implements PipeTransform { - constructor(private sanitized: DomSanitizer) {} + private readonly sanitized = inject(DomSanitizer); transform(user: UserLike & { displayName?: string }, className: string = '', delimiter: string = ''): SafeHtml { let safeHtml: SafeHtml = ''; diff --git a/lib/core/src/lib/search-text/search-text-input.component.ts b/lib/core/src/lib/search-text/search-text-input.component.ts index bead0e3baa..d6764591ab 100644 --- a/lib/core/src/lib/search-text/search-text-input.component.ts +++ b/lib/core/src/lib/search-text/search-text-input.component.ts @@ -56,6 +56,8 @@ import { IconModule } from '../icon/icon.module'; } }) export class SearchTextInputComponent implements OnInit, OnDestroy { + private readonly userPreferencesService = inject(UserPreferencesService); + /** Toggles auto-completion of the search input field. */ @Input() autocomplete: boolean = false; @@ -166,14 +168,14 @@ export class SearchTextInputComponent implements OnInit, OnDestroy { private dir = 'ltr'; private toggleSearch = new Subject<any>(); private focusSubscription: Subscription; - private valueChange = new Subject<string>(); - private toggleSubscription: Subscription; + private readonly valueChange = new Subject<string>(); + private readonly toggleSubscription: Subscription; toggle$ = this.toggleSearch.asObservable(); private readonly destroyRef = inject(DestroyRef); - constructor(private userPreferencesService: UserPreferencesService) { + constructor() { this.toggleSubscription = this.toggle$.pipe(debounceTime(200), takeUntilDestroyed()).subscribe(() => { if (this.expandable) { this.subscriptAnimationState = this.toggleAnimation(); diff --git a/lib/core/src/lib/search-text/search-trigger.directive.ts b/lib/core/src/lib/search-text/search-trigger.directive.ts index 982edd13fa..81e6c4cd52 100644 --- a/lib/core/src/lib/search-text/search-trigger.directive.ts +++ b/lib/core/src/lib/search-text/search-trigger.directive.ts @@ -18,7 +18,7 @@ /* eslint-disable @angular-eslint/no-input-rename, @typescript-eslint/no-use-before-define, @angular-eslint/no-input-rename */ import { ENTER, ESCAPE } from '@angular/cdk/keycodes'; -import { ChangeDetectorRef, DestroyRef, Directive, ElementRef, forwardRef, inject, Inject, Input, NgZone, OnDestroy, Optional } from '@angular/core'; +import { ChangeDetectorRef, DestroyRef, Directive, ElementRef, forwardRef, inject, Input, NgZone, OnDestroy } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { DOCUMENT } from '@angular/common'; import { fromEvent, merge, Observable, of, Subject, Subscription } from 'rxjs'; @@ -50,6 +50,11 @@ export const SEARCH_AUTOCOMPLETE_VALUE_ACCESSOR: any = { providers: [SEARCH_AUTOCOMPLETE_VALUE_ACCESSOR] }) export class SearchTriggerDirective implements ControlValueAccessor, OnDestroy { + private readonly element = inject(ElementRef); + private readonly ngZone = inject(NgZone); + private readonly changeDetectorRef = inject(ChangeDetectorRef); + private readonly document = inject(DOCUMENT, { optional: true }); + @Input('searchAutocomplete') searchPanel: SearchComponentInterface; @@ -66,13 +71,6 @@ export class SearchTriggerDirective implements ControlValueAccessor, OnDestroy { private readonly destroyRef = inject(DestroyRef); - constructor( - private element: ElementRef, - private ngZone: NgZone, - private changeDetectorRef: ChangeDetectorRef, - @Optional() @Inject(DOCUMENT) private document: any - ) {} - ngOnDestroy() { if (this.escapeEventStream) { this.escapeEventStream = null; diff --git a/lib/core/src/lib/snackbar-content/snackbar-content.component.ts b/lib/core/src/lib/snackbar-content/snackbar-content.component.ts index 32e8b97bc6..c6065326a4 100644 --- a/lib/core/src/lib/snackbar-content/snackbar-content.component.ts +++ b/lib/core/src/lib/snackbar-content/snackbar-content.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { MAT_SNACK_BAR_DATA, MatSnackBarRef } from '@angular/material/snack-bar'; import { SnackBarData } from './snack-bar-data'; import { TranslatePipe } from '@ngx-translate/core'; @@ -32,10 +32,12 @@ import { IconModule } from '../icon/icon.module'; } }) export class SnackbarContentComponent { - constructor( - public snackBarRef: MatSnackBarRef<SnackbarContentComponent>, - @Inject(MAT_SNACK_BAR_DATA) public data: SnackBarData - ) { + snackBarRef = inject<MatSnackBarRef<SnackbarContentComponent>>(MatSnackBarRef); + data = inject<SnackBarData>(MAT_SNACK_BAR_DATA); + + constructor() { + const data = this.data; + if (!data) { this.data = { message: '' }; } diff --git a/lib/core/src/lib/templates/error-content/error-content.component.ts b/lib/core/src/lib/templates/error-content/error-content.component.ts index abb4397d45..038dc56a12 100644 --- a/lib/core/src/lib/templates/error-content/error-content.component.ts +++ b/lib/core/src/lib/templates/error-content/error-content.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, ChangeDetectionStrategy, Input, ViewEncapsulation, OnInit } from '@angular/core'; +import { Component, ChangeDetectionStrategy, Input, ViewEncapsulation, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslationService } from '../../translation/translation.service'; import { Observable } from 'rxjs'; @@ -34,6 +34,10 @@ import { TranslatePipe } from '@ngx-translate/core'; host: { class: 'adf-error-content' } }) export class ErrorContentComponent implements OnInit { + private readonly route = inject(ActivatedRoute); + private readonly translateService = inject(TranslationService); + private readonly breakpointObserver = inject(BreakpointObserver); + static UNKNOWN_ERROR = 'UNKNOWN'; /** Error code associated with this error. */ @@ -43,8 +47,6 @@ export class ErrorContentComponent implements OnInit { errorCodeTranslated: string; isSmallScreen$: Observable<boolean>; - constructor(private route: ActivatedRoute, private translateService: TranslationService, private breakpointObserver: BreakpointObserver) {} - ngOnInit() { if (this.route) { this.route.params.subscribe((params) => { diff --git a/lib/core/src/lib/translation/translate-loader.service.ts b/lib/core/src/lib/translation/translate-loader.service.ts index 37c6bc787e..3b1939480b 100644 --- a/lib/core/src/lib/translation/translate-loader.service.ts +++ b/lib/core/src/lib/translation/translate-loader.service.ts @@ -16,7 +16,7 @@ */ import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { TranslateLoader } from '@ngx-translate/core'; import { Observable, forkJoin, throwError, of } from 'rxjs'; import { ComponentTranslationModel } from '../models/component.model'; @@ -27,9 +27,11 @@ import { map, catchError, retry } from 'rxjs/operators'; providedIn: 'root' }) export class TranslateLoaderService implements TranslateLoader { - private prefix: string = 'i18n'; - private suffix: string = '.json'; - private providers: ComponentTranslationModel[] = [ + private readonly http = inject(HttpClient); + + private readonly prefix: string = 'i18n'; + private readonly suffix: string = '.json'; + private readonly providers: ComponentTranslationModel[] = [ new ComponentTranslationModel({ name: 'adf-core', path: 'assets/adf-core' @@ -38,8 +40,6 @@ export class TranslateLoaderService implements TranslateLoader { private queue: string[][] = []; private defaultLang: string = 'en'; - constructor(private http: HttpClient) {} - setDefaultLang(value: string) { this.defaultLang = value || 'en'; } diff --git a/lib/core/src/lib/translation/translation.service.ts b/lib/core/src/lib/translation/translation.service.ts index c8c877e280..f0f64ca270 100644 --- a/lib/core/src/lib/translation/translation.service.ts +++ b/lib/core/src/lib/translation/translation.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { effect, Inject, Injectable, InjectionToken, Optional } from '@angular/core'; +import { effect, Injectable, InjectionToken, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { Observable } from 'rxjs'; import { TranslateLoaderService } from './translate-loader.service'; @@ -50,29 +50,29 @@ export function provideTranslations(id: string, path: string) { providedIn: 'root' }) export class TranslationService { + translate = inject(TranslateService); + private readonly userPreferencesService = inject(UserPreferencesService); + private readonly providers = inject<TranslationProvider[]>(TRANSLATION_PROVIDER, { optional: true }) ?? []; + defaultLang: string; userLang: string; customLoader: TranslateLoaderService; - constructor( - public translate: TranslateService, - userPreferencesService: UserPreferencesService, - @Optional() @Inject(TRANSLATION_PROVIDER) providers: TranslationProvider[] - ) { + constructor() { this.customLoader = this.translate.currentLoader as TranslateLoaderService; this.defaultLang = 'en'; - translate.setDefaultLang(this.defaultLang); + this.translate.setDefaultLang(this.defaultLang); this.customLoader.setDefaultLang(this.defaultLang); - if (providers && providers.length > 0) { - for (const provider of providers) { + if (this.providers && this.providers.length > 0) { + for (const provider of this.providers) { this.addTranslationFolder(provider.name, provider.source); } } // Try to read locale from storage synchronously to apply it before components render - const storedLocale = userPreferencesService.get(UserPreferenceValues.Locale); + const storedLocale = this.userPreferencesService.get(UserPreferenceValues.Locale); if (storedLocale) { // Apply stored locale immediately during construction @@ -81,7 +81,7 @@ export class TranslationService { } effect(() => { - const locale = userPreferencesService.localeSignal(); + const locale = this.userPreferencesService.localeSignal(); if (locale && locale !== this.userLang) { this.userLang = locale; diff --git a/lib/core/src/lib/viewer/components/img-viewer/img-viewer.component.ts b/lib/core/src/lib/viewer/components/img-viewer/img-viewer.component.ts index f0682ed0c9..e3dcc77819 100644 --- a/lib/core/src/lib/viewer/components/img-viewer/img-viewer.component.ts +++ b/lib/core/src/lib/viewer/components/img-viewer/img-viewer.component.ts @@ -28,7 +28,8 @@ import { Output, SimpleChanges, ViewChild, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { TranslatePipe } from '@ngx-translate/core'; @@ -47,6 +48,9 @@ import { IconModule } from '../../../icon/icon.module'; encapsulation: ViewEncapsulation.None }) export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy { + private readonly appConfigService = inject(AppConfigService); + private readonly urlService = inject(UrlService); + @Input() showToolbar = true; @@ -139,10 +143,7 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy { return Math.round(this.scale * 100) + '%'; } - constructor( - private readonly appConfigService: AppConfigService, - private readonly urlService: UrlService - ) { + constructor() { this.initializeScaling(); } diff --git a/lib/core/src/lib/viewer/components/media-player/media-player.component.ts b/lib/core/src/lib/viewer/components/media-player/media-player.component.ts index 5c3f88ab35..3b30f9a591 100644 --- a/lib/core/src/lib/viewer/components/media-player/media-player.component.ts +++ b/lib/core/src/lib/viewer/components/media-player/media-player.component.ts @@ -16,7 +16,7 @@ */ import { NgClass, NgForOf } from '@angular/common'; -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewEncapsulation, inject } from '@angular/core'; import { UrlService } from '../../../common'; import { Track } from '../../models/viewer.model'; @@ -29,6 +29,8 @@ import { Track } from '../../models/viewer.model'; encapsulation: ViewEncapsulation.None }) export class MediaPlayerComponent implements OnChanges { + private readonly urlService = inject(UrlService); + @Input() urlFile: string; @@ -51,8 +53,6 @@ export class MediaPlayerComponent implements OnChanges { @Output() canPlay = new EventEmitter<void>(); - constructor(private urlService: UrlService) {} - ngOnChanges(changes: SimpleChanges) { const blobFile = changes['blobFile']; diff --git a/lib/core/src/lib/viewer/components/pdf-viewer-password-dialog/pdf-viewer-password-dialog.ts b/lib/core/src/lib/viewer/components/pdf-viewer-password-dialog/pdf-viewer-password-dialog.ts index 79fb615d35..b7c90c3b00 100644 --- a/lib/core/src/lib/viewer/components/pdf-viewer-password-dialog/pdf-viewer-password-dialog.ts +++ b/lib/core/src/lib/viewer/components/pdf-viewer-password-dialog/pdf-viewer-password-dialog.ts @@ -16,7 +16,7 @@ */ import { NgIf } from '@angular/common'; -import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { ReactiveFormsModule, UntypedFormControl, Validators } from '@angular/forms'; @@ -35,12 +35,10 @@ declare const pdfjsLib: any; encapsulation: ViewEncapsulation.None }) export class PdfPasswordDialogComponent implements OnInit { - passwordFormControl: UntypedFormControl; + private readonly dialogRef = inject<MatDialogRef<PdfPasswordDialogComponent>>(MatDialogRef); + data = inject(MAT_DIALOG_DATA); - constructor( - private dialogRef: MatDialogRef<PdfPasswordDialogComponent>, - @Inject(MAT_DIALOG_DATA) public data: any - ) {} + passwordFormControl: UntypedFormControl; ngOnInit() { this.passwordFormControl = new UntypedFormControl('', [Validators.required]); diff --git a/lib/core/src/lib/viewer/components/pdf-viewer-thumb/pdf-viewer-thumb.component.ts b/lib/core/src/lib/viewer/components/pdf-viewer-thumb/pdf-viewer-thumb.component.ts index 9a6ee8a60d..66abfc588b 100644 --- a/lib/core/src/lib/viewer/components/pdf-viewer-thumb/pdf-viewer-thumb.component.ts +++ b/lib/core/src/lib/viewer/components/pdf-viewer-thumb/pdf-viewer-thumb.component.ts @@ -17,7 +17,7 @@ import { FocusableOption } from '@angular/cdk/a11y'; import { AsyncPipe, NgIf } from '@angular/common'; -import { Component, ElementRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, ElementRef, Input, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { TranslatePipe } from '@ngx-translate/core'; @@ -29,13 +29,14 @@ import { TranslatePipe } from '@ngx-translate/core'; host: { tabindex: '0' } }) export class PdfThumbComponent implements OnInit, FocusableOption { + private readonly sanitizer = inject(DomSanitizer); + private readonly element = inject(ElementRef); + @Input() page: any = null; image$: Promise<string>; - constructor(private sanitizer: DomSanitizer, private element: ElementRef) {} - ngOnInit() { this.image$ = this.page.getPage().then((page) => this.getThumb(page)); } diff --git a/lib/core/src/lib/viewer/components/pdf-viewer-thumbnails/pdf-viewer-thumbnails.component.ts b/lib/core/src/lib/viewer/components/pdf-viewer-thumbnails/pdf-viewer-thumbnails.component.ts index 32aa6f082a..15d8aaf53c 100644 --- a/lib/core/src/lib/viewer/components/pdf-viewer-thumbnails/pdf-viewer-thumbnails.component.ts +++ b/lib/core/src/lib/viewer/components/pdf-viewer-thumbnails/pdf-viewer-thumbnails.component.ts @@ -25,7 +25,6 @@ import { ElementRef, EventEmitter, HostListener, - Inject, Input, OnDestroy, OnInit, @@ -33,7 +32,8 @@ import { QueryList, TemplateRef, ViewChildren, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { delay } from 'rxjs/operators'; import { PdfThumbComponent } from '../pdf-viewer-thumb/pdf-viewer-thumb.component'; @@ -47,6 +47,9 @@ import { PdfThumbComponent } from '../pdf-viewer-thumb/pdf-viewer-thumb.componen encapsulation: ViewEncapsulation.None }) export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy { + private readonly element = inject(ElementRef); + private readonly document = inject(DOCUMENT); + @Input({ required: true }) pdfViewer: any; @Output() @@ -59,7 +62,7 @@ export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy { currentHeight: number = 0; private items = []; - private margin: number = 15; + private readonly margin: number = 15; private itemHeight: number = 114 + this.margin; private previouslyFocusedElement: HTMLElement | null = null; private keyManager: FocusKeyManager<PdfThumbComponent>; @@ -103,7 +106,7 @@ export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy { this.calculateItems(); } - constructor(private element: ElementRef, @Inject(DOCUMENT) private document: any) { + constructor() { this.calculateItems = this.calculateItems.bind(this); this.onPageChange = this.onPageChange.bind(this); } diff --git a/lib/core/src/lib/viewer/components/pdf-viewer/pdf-viewer.component.ts b/lib/core/src/lib/viewer/components/pdf-viewer/pdf-viewer.component.ts index 9f4a028581..591a28369d 100644 --- a/lib/core/src/lib/viewer/components/pdf-viewer/pdf-viewer.component.ts +++ b/lib/core/src/lib/viewer/components/pdf-viewer/pdf-viewer.component.ts @@ -140,22 +140,22 @@ export class PdfViewerComponent implements OnChanges, OnDestroy { return currentScaleText; } - private pdfjsLib = inject(PDFJS_MODULE); - private pdfjsViewer = inject(PDFJS_VIEWER_MODULE); + private readonly pdfjsLib = inject(PDFJS_MODULE); + private readonly pdfjsViewer = inject(PDFJS_VIEWER_MODULE); - private eventBus = new EventBus(); - private pdfjsDefaultOptions = { + private readonly eventBus = new EventBus(); + private readonly pdfjsDefaultOptions = { disableAutoFetch: true, disableStream: true, cMapUrl: './cmaps/', cMapPacked: true, wasmUrl: './wasm/' }; - private pdfjsWorkerDestroy$ = new Subject<boolean>(); + private readonly pdfjsWorkerDestroy$ = new Subject<boolean>(); - private dialog = inject(MatDialog); - private renderingQueueServices = inject(RenderingQueueServices); - private appConfigService = inject(AppConfigService); + private readonly dialog = inject(MatDialog); + private readonly renderingQueueServices = inject(RenderingQueueServices); + private readonly appConfigService = inject(AppConfigService); private readonly translateService = inject(TranslateService); diff --git a/lib/core/src/lib/viewer/components/txt-viewer/txt-viewer.component.ts b/lib/core/src/lib/viewer/components/txt-viewer/txt-viewer.component.ts index 82b2fde1eb..54ffb8acc9 100644 --- a/lib/core/src/lib/viewer/components/txt-viewer/txt-viewer.component.ts +++ b/lib/core/src/lib/viewer/components/txt-viewer/txt-viewer.component.ts @@ -16,7 +16,7 @@ */ import { HttpClient } from '@angular/common/http'; -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewEncapsulation, inject } from '@angular/core'; import { AppConfigService } from '../../../app-config'; @Component({ @@ -27,6 +27,9 @@ import { AppConfigService } from '../../../app-config'; encapsulation: ViewEncapsulation.None }) export class TxtViewerComponent implements OnChanges { + private readonly http = inject(HttpClient); + private readonly appConfigService = inject(AppConfigService); + @Input() urlFile: any; @@ -38,8 +41,6 @@ export class TxtViewerComponent implements OnChanges { content: string | ArrayBuffer; - constructor(private http: HttpClient, private appConfigService: AppConfigService) {} - ngOnChanges(changes: SimpleChanges): Promise<void> { const blobFile = changes['blobFile']; if (blobFile?.currentValue) { diff --git a/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.ts b/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.ts index e1d7fa977f..211a6eee74 100644 --- a/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.ts +++ b/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.ts @@ -17,7 +17,19 @@ import { AppExtensionService, ExtensionsModule, ViewerExtensionRef, PreviewExtensionComponent } from '@alfresco/adf-extensions'; import { NgForOf, NgTemplateOutlet } from '@angular/common'; -import { Component, EventEmitter, Injector, Input, OnChanges, OnInit, Output, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; +import { + Component, + EventEmitter, + Injector, + Input, + OnChanges, + OnInit, + Output, + TemplateRef, + ViewChild, + ViewEncapsulation, + inject +} from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { TranslatePipe } from '@ngx-translate/core'; @@ -51,6 +63,11 @@ import { UnknownFormatComponent } from '../unknown-format/unknown-format.compone providers: [ViewUtilService] }) export class ViewerRenderComponent implements OnChanges, OnInit { + private readonly viewUtilService = inject(ViewUtilService); + private readonly extensionService = inject(AppExtensionService); + dialog = inject(MatDialog); + readonly injector = inject(Injector); + /** * If you want to load an external file that does not come from ACS you * can use this URL to specify where to load the file from. @@ -168,13 +185,6 @@ export class ViewerRenderComponent implements OnChanges, OnInit { cacheTypeForContent = 'no-cache'; - constructor( - private viewUtilService: ViewUtilService, - private extensionService: AppExtensionService, - public dialog: MatDialog, - public readonly injector: Injector - ) {} - ngOnInit() { this.cacheTypeForContent = 'no-cache'; this.isLoading = true; diff --git a/lib/core/src/lib/viewer/components/viewer.component.ts b/lib/core/src/lib/viewer/components/viewer.component.ts index 950f37de60..10ac3a6367 100644 --- a/lib/core/src/lib/viewer/components/viewer.component.ts +++ b/lib/core/src/lib/viewer/components/viewer.component.ts @@ -92,7 +92,12 @@ const DEFAULT_NON_PREVIEW_CONFIG = { providers: [ViewUtilService] }) export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges { - private thumbnailService = inject(ThumbnailService); + private readonly el = inject(ElementRef); + dialog = inject(MatDialog); + private readonly viewUtilsService = inject(ViewUtilService); + private readonly appConfigService = inject(AppConfigService); + + private readonly thumbnailService = inject(ThumbnailService); @HostBinding('class.adf-viewer-inline') get isInline() { @@ -302,7 +307,7 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges { viewerRenderer: ViewerRenderComponent; private closeViewer = true; - private keyDown$ = fromEvent<KeyboardEvent>(document, 'keydown'); + private readonly keyDown$ = fromEvent<KeyboardEvent>(document, 'keydown'); private isDialogVisible = false; private _fileName: string; private _fileNameWithoutExtension: string; @@ -344,13 +349,6 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges { return this._fileNameWithoutExtension; } - constructor( - private el: ElementRef, - public dialog: MatDialog, - private viewUtilsService: ViewUtilService, - private appConfigService: AppConfigService - ) {} - ngOnChanges(changes: SimpleChanges) { const { blobFile, urlFile, mimeType, nodeMimeType } = changes; diff --git a/lib/core/src/lib/viewer/directives/viewer-extension.directive.ts b/lib/core/src/lib/viewer/directives/viewer-extension.directive.ts index 56606884a2..69de461f69 100644 --- a/lib/core/src/lib/viewer/directives/viewer-extension.directive.ts +++ b/lib/core/src/lib/viewer/directives/viewer-extension.directive.ts @@ -23,6 +23,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; selector: 'adf-viewer-extension' }) export class ViewerExtensionDirective implements AfterContentInit { + private readonly viewerComponent = inject(ViewerRenderComponent); + @ContentChild(TemplateRef) template: any; @@ -39,8 +41,6 @@ export class ViewerExtensionDirective implements AfterContentInit { private readonly destroyRef = inject(DestroyRef); - constructor(private viewerComponent: ViewerRenderComponent) {} - ngAfterContentInit() { this.templateModel = { template: this.template, isVisible: false }; this.viewerComponent.extensionsSupportedByTemplates.push(...this.supportedExtensions); diff --git a/lib/core/src/lib/viewer/services/view-util.service.spec.ts b/lib/core/src/lib/viewer/services/view-util.service.spec.ts index 63610314a5..89a246a8c9 100644 --- a/lib/core/src/lib/viewer/services/view-util.service.spec.ts +++ b/lib/core/src/lib/viewer/services/view-util.service.spec.ts @@ -16,7 +16,8 @@ */ import { AppExtensionService } from '@alfresco/adf-extensions'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { ViewUtilService } from './view-util.service'; @@ -27,8 +28,7 @@ describe('ViewUtilService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [AppExtensionService] + providers: [AppExtensionService, provideHttpClient(), provideHttpClientTesting()] }); viewUtilService = TestBed.inject(ViewUtilService); diff --git a/lib/core/src/lib/viewer/services/view-util.service.ts b/lib/core/src/lib/viewer/services/view-util.service.ts index 421437c26d..97da06d264 100644 --- a/lib/core/src/lib/viewer/services/view-util.service.ts +++ b/lib/core/src/lib/viewer/services/view-util.service.ts @@ -15,15 +15,17 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions'; @Injectable({ providedIn: 'root' }) export class ViewUtilService { + private readonly extensionService = inject(AppExtensionService); + // Extensions that are supported by the Viewer without conversion - private extensions = { + private readonly extensions = { image: ['png', 'jpg', 'jpeg', 'gif', 'bpm', 'svg'], media: ['wav', 'mp4', 'mp3', 'webm', 'ogg'], text: ['txt', 'xml', 'html', 'json', 'ts', 'css', 'md'], @@ -31,7 +33,7 @@ export class ViewUtilService { }; // Mime types that are supported by the Viewer without conversion - private mimeTypes = { + private readonly mimeTypes = { text: ['text/plain', 'text/csv', 'text/xml', 'text/html', 'application/x-javascript'], pdf: ['application/pdf'], image: ['image/png', 'image/jpeg', 'image/gif', 'image/bmp', 'image/svg+xml'], @@ -56,8 +58,6 @@ export class ViewUtilService { return this.viewerExtensions.map((extension) => extension.fileExtension); } - constructor(private extensionService: AppExtensionService) {} - /** * get File name from url * diff --git a/lib/extensions/src/lib/components/dynamic-column/dynamic-column.component.ts b/lib/extensions/src/lib/components/dynamic-column/dynamic-column.component.ts index 7798e015c5..5ca99f2857 100644 --- a/lib/extensions/src/lib/components/dynamic-column/dynamic-column.component.ts +++ b/lib/extensions/src/lib/components/dynamic-column/dynamic-column.component.ts @@ -26,7 +26,8 @@ import { OnChanges, SimpleChanges, ViewEncapsulation, - ChangeDetectionStrategy + ChangeDetectionStrategy, + inject } from '@angular/core'; import { ExtensionService } from '../../services/extension.service'; @@ -47,6 +48,8 @@ import { ExtensionService } from '../../services/extension.service'; ] }) export class DynamicColumnComponent implements OnInit, OnChanges, OnDestroy { + private readonly extensions = inject(ExtensionService); + @ViewChild('content', { read: ViewContainerRef, static: true }) content: ViewContainerRef; @@ -58,8 +61,6 @@ export class DynamicColumnComponent implements OnInit, OnChanges, OnDestroy { private componentRef: ComponentRef<any>; - constructor(private extensions: ExtensionService) {} - ngOnInit() { const componentType = this.extensions.getComponentById(this.id); if (componentType) { diff --git a/lib/extensions/src/lib/components/dynamic-component/dynamic.component.ts b/lib/extensions/src/lib/components/dynamic-component/dynamic.component.ts index 0c2399e2d8..28f5863344 100644 --- a/lib/extensions/src/lib/components/dynamic-component/dynamic.component.ts +++ b/lib/extensions/src/lib/components/dynamic-component/dynamic.component.ts @@ -15,7 +15,18 @@ * limitations under the License. */ -import { Component, Input, ComponentRef, ViewChild, ViewContainerRef, OnDestroy, OnChanges, SimpleChanges, AfterViewInit } from '@angular/core'; +import { + Component, + Input, + ComponentRef, + ViewChild, + ViewContainerRef, + OnDestroy, + OnChanges, + SimpleChanges, + AfterViewInit, + inject +} from '@angular/core'; import { ExtensionService } from '../../services/extension.service'; import { ExtensionComponent } from '../../services/component-register.service'; import { MatMenuItem } from '@angular/material/menu'; @@ -26,6 +37,8 @@ import { MatMenuItem } from '@angular/material/menu'; template: `<div #content></div>` }) export class DynamicExtensionComponent implements OnChanges, OnDestroy, AfterViewInit { + private readonly extensions = inject(ExtensionService); + @ViewChild('content', { read: ViewContainerRef, static: true }) content: ViewContainerRef; @@ -41,8 +54,6 @@ export class DynamicExtensionComponent implements OnChanges, OnDestroy, AfterVie private componentRef: ComponentRef<ExtensionComponent>; private loaded: boolean = false; - constructor(private extensions: ExtensionService) {} - ngOnChanges(changes: SimpleChanges) { if (!this.loaded) { this.loadComponent(); diff --git a/lib/extensions/src/lib/components/dynamic-tab/dynamic-tab.component.ts b/lib/extensions/src/lib/components/dynamic-tab/dynamic-tab.component.ts index edd036b45e..45a84a9792 100644 --- a/lib/extensions/src/lib/components/dynamic-tab/dynamic-tab.component.ts +++ b/lib/extensions/src/lib/components/dynamic-tab/dynamic-tab.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Input, OnInit, OnDestroy, ViewChild, ViewContainerRef, ComponentRef, OnChanges, SimpleChanges } from '@angular/core'; +import { Component, Input, OnInit, OnDestroy, ViewChild, ViewContainerRef, ComponentRef, OnChanges, SimpleChanges, inject } from '@angular/core'; import { Node } from '@alfresco/js-api'; import { ExtensionService } from '../../services/extension.service'; @@ -24,6 +24,8 @@ import { ExtensionService } from '../../services/extension.service'; template: `<div #content></div>` }) export class DynamicTabComponent implements OnInit, OnChanges, OnDestroy { + private readonly extensions = inject(ExtensionService); + @ViewChild('content', { read: ViewContainerRef, static: true }) content: ViewContainerRef; @@ -36,8 +38,6 @@ export class DynamicTabComponent implements OnInit, OnChanges, OnDestroy { private componentRef: ComponentRef<any>; - constructor(private extensions: ExtensionService) {} - ngOnInit() { const componentType = this.extensions.getComponentById(this.id); if (componentType) { diff --git a/lib/extensions/src/lib/components/viewer/preview-extension.component.ts b/lib/extensions/src/lib/components/viewer/preview-extension.component.ts index 9fc4f6b72d..007c3b5041 100644 --- a/lib/extensions/src/lib/components/viewer/preview-extension.component.ts +++ b/lib/extensions/src/lib/components/viewer/preview-extension.component.ts @@ -37,6 +37,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; template: `<div #content></div>` }) export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy { + private readonly extensionService = inject(ExtensionService); + @ViewChild('content', { read: ViewContainerRef, static: true }) content: ViewContainerRef; @@ -63,8 +65,6 @@ export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy { private componentRef: ComponentRef<any>; - constructor(private extensionService: ExtensionService) {} - ngOnInit() { if (!this.id) { return; diff --git a/lib/extensions/src/lib/services/app-extension.service.ts b/lib/extensions/src/lib/services/app-extension.service.ts index 3b26d6994d..04eae72b07 100644 --- a/lib/extensions/src/lib/services/app-extension.service.ts +++ b/lib/extensions/src/lib/services/app-extension.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ExtensionConfig, ExtensionRef } from '../config/extension.config'; import { ExtensionService } from '../services/extension.service'; import { Observable, BehaviorSubject } from 'rxjs'; @@ -26,10 +26,12 @@ import { DocumentListPresetRef } from '../config/document-list.extensions'; providedIn: 'root' }) export class AppExtensionService { - references$: Observable<ExtensionRef[]>; - private _references = new BehaviorSubject<ExtensionRef[]>([]); + protected extensionService = inject(ExtensionService); - constructor(protected extensionService: ExtensionService) { + references$: Observable<ExtensionRef[]>; + private readonly _references = new BehaviorSubject<ExtensionRef[]>([]); + + constructor() { this.references$ = this._references.asObservable(); } diff --git a/lib/extensions/src/lib/services/extension-loader.service.spec.ts b/lib/extensions/src/lib/services/extension-loader.service.spec.ts index 569fa14a45..174ea2e4d8 100644 --- a/lib/extensions/src/lib/services/extension-loader.service.spec.ts +++ b/lib/extensions/src/lib/services/extension-loader.service.spec.ts @@ -16,7 +16,8 @@ */ import { TestBed, fakeAsync, flushMicrotasks, tick } from '@angular/core/testing'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { ExtensionConfig } from '../config/extension.config'; import { ExtensionLoaderService } from './extension-loader.service'; @@ -35,10 +36,7 @@ describe('ExtensionLoaderService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - ExtensionLoaderService - ] + providers: [ExtensionLoaderService, provideHttpClient(), provideHttpClientTesting()] }); extensionLoaderService = TestBed.inject(ExtensionLoaderService); httpMock = TestBed.inject(HttpTestingController); @@ -85,10 +83,12 @@ describe('ExtensionLoaderService', () => { it('should load only extensions defined by $references', fakeAsync(() => { appExtensionsConfig.$references = ['test.extension.1.json']; - extensionLoaderService.load('assets/app.extensions.json', 'assets/plugins', ['test.extension.2.json, test.extension.3.json']).then((config: ExtensionConfig) => { - const pluginsReference = config.$references.map((entry: ExtensionConfig) => entry.$name); - expect(pluginsReference).toEqual(['test.extension.1']); - }); + extensionLoaderService + .load('assets/app.extensions.json', 'assets/plugins', ['test.extension.2.json, test.extension.3.json']) + .then((config: ExtensionConfig) => { + const pluginsReference = config.$references.map((entry: ExtensionConfig) => entry.$name); + expect(pluginsReference).toEqual(['test.extension.1']); + }); httpMock.expectOne('assets/app.extensions.json').flush(appExtensionsConfig); tick(); @@ -98,20 +98,20 @@ describe('ExtensionLoaderService', () => { flushMicrotasks(); })); - it('should load extensions from passed extension value',fakeAsync(() => { + it('should load extensions from passed extension value', fakeAsync(() => { appExtensionsConfig.$references = ['test.extension.1.json']; - extensionLoaderService.load( - 'assets/app.extensions.json', - 'assets/plugins', - undefined, - [{ - $id: 'extension-value-id', - $license: 'license', - $name: 'name', - $version:'version', - $vendor: 'vendor' - }]).then((config: ExtensionConfig) => { + extensionLoaderService + .load('assets/app.extensions.json', 'assets/plugins', undefined, [ + { + $id: 'extension-value-id', + $license: 'license', + $name: 'name', + $version: 'version', + $vendor: 'vendor' + } + ]) + .then((config: ExtensionConfig) => { const hasExtensionValue = config.$references.some((entry: ExtensionConfig) => entry.$id === 'extension-value-id'); expect(hasExtensionValue).toBe(true); }); @@ -122,17 +122,17 @@ describe('ExtensionLoaderService', () => { })); it('should load extensions if only extension value was passed', fakeAsync(() => { - extensionLoaderService.load( - 'assets/app.extensions.json', - 'assets/plugins', - undefined, - [{ - $id: 'extension-value-id', - $license: 'license', - $name: 'name', - $version:'version', - $vendor: 'vendor' - }]).then((config: ExtensionConfig) => { + extensionLoaderService + .load('assets/app.extensions.json', 'assets/plugins', undefined, [ + { + $id: 'extension-value-id', + $license: 'license', + $name: 'name', + $version: 'version', + $vendor: 'vendor' + } + ]) + .then((config: ExtensionConfig) => { const hasExtensionValue = config.$references.some((entry: ExtensionConfig) => entry.$id === 'extension-value-id'); expect(hasExtensionValue).toBe(true); }); @@ -144,23 +144,24 @@ describe('ExtensionLoaderService', () => { it('should load extensions with multiple extension values', fakeAsync(() => { appExtensionsConfig.$references = ['test.extension.1.json']; - extensionLoaderService.load( - 'assets/app.extensions.json', - 'assets/plugins', - undefined, - [{ - $id: 'extension-value-id-1', - $license: 'license', - $name: 'name', - $version:'version', - $vendor: 'vendor' - },{ - $id: 'extension-value-id-2', - $license: 'license', - $name: 'name', - $version:'version', - $vendor: 'vendor' - }]).then((config: ExtensionConfig) => { + extensionLoaderService + .load('assets/app.extensions.json', 'assets/plugins', undefined, [ + { + $id: 'extension-value-id-1', + $license: 'license', + $name: 'name', + $version: 'version', + $vendor: 'vendor' + }, + { + $id: 'extension-value-id-2', + $license: 'license', + $name: 'name', + $version: 'version', + $vendor: 'vendor' + } + ]) + .then((config: ExtensionConfig) => { const hasFirstExtensionValue = config.$references.some((entry: ExtensionConfig) => entry.$id === 'extension-value-id-1'); expect(hasFirstExtensionValue).toBe(true); const hasSecondExtensionValue = config.$references.some((entry: ExtensionConfig) => entry.$id === 'extension-value-id-2'); diff --git a/lib/extensions/src/lib/services/extension-loader.service.ts b/lib/extensions/src/lib/services/extension-loader.service.ts index 6a8c1f6dfd..38b1b6e504 100644 --- a/lib/extensions/src/lib/services/extension-loader.service.ts +++ b/lib/extensions/src/lib/services/extension-loader.service.ts @@ -16,7 +16,7 @@ */ import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ActionRef, ContentActionRef, ContentActionType } from '../config/action.extensions'; import { ExtensionElement } from '../config/extension-element'; import { filterEnabled, getValue, mergeObjects, sortByOrder } from '../config/extension-utils'; @@ -28,7 +28,7 @@ import { RuleRef } from '../config/rule.extensions'; providedIn: 'root' }) export class ExtensionLoaderService { - constructor(private http: HttpClient) {} + private readonly http = inject(HttpClient); load(configPath: string, pluginsPath: string, extensions?: string[], extensionValues?: ExtensionConfig[]): Promise<ExtensionConfig> { return new Promise<any>((resolve) => { diff --git a/lib/extensions/src/lib/services/extension.service.spec.ts b/lib/extensions/src/lib/services/extension.service.spec.ts index 30d792b96a..fd388c6317 100644 --- a/lib/extensions/src/lib/services/extension.service.spec.ts +++ b/lib/extensions/src/lib/services/extension.service.spec.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ExtensionService } from './extension.service'; +import { ExtensionService, EXTENSION_JSONS, EXTENSION_JSON_VALUES } from './extension.service'; import { ExtensionLoaderService } from './extension-loader.service'; import { ExtensionConfig } from '../config/extension.config'; import { RuleRef } from '../config/rule.extensions'; @@ -23,6 +23,9 @@ import { RouteRef } from '../config/routing.extensions'; import { ActionRef } from '../config/action.extensions'; import { ComponentRegisterService } from './component-register.service'; import { RuleService } from './rule.service'; +import { TestBed } from '@angular/core/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; describe('ExtensionService', () => { const blankConfig: ExtensionConfig = { @@ -35,15 +38,24 @@ describe('ExtensionService', () => { }; let loader: ExtensionLoaderService; - let componentRegister: ComponentRegisterService; let service: ExtensionService; - let ruleService: RuleService; beforeEach(() => { - loader = new ExtensionLoaderService(null); - componentRegister = new ComponentRegisterService(); - ruleService = new RuleService(loader); - service = new ExtensionService(loader, componentRegister, ruleService, [], []); + TestBed.configureTestingModule({ + providers: [ + provideHttpClient(), + provideHttpClientTesting(), + ExtensionService, + ExtensionLoaderService, + ComponentRegisterService, + RuleService, + { provide: EXTENSION_JSONS, useValue: [] }, + { provide: EXTENSION_JSON_VALUES, useValue: [] } + ] + }); + + loader = TestBed.inject(ExtensionLoaderService); + service = TestBed.inject(ExtensionService); }); it('should load and setup a config', async () => { diff --git a/lib/extensions/src/lib/services/extension.service.ts b/lib/extensions/src/lib/services/extension.service.ts index 45c3feaad8..0a25dc0876 100644 --- a/lib/extensions/src/lib/services/extension.service.ts +++ b/lib/extensions/src/lib/services/extension.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable, Type, InjectionToken, Inject } from '@angular/core'; +import { Injectable, Type, InjectionToken, inject } from '@angular/core'; import { RuleEvaluator, RuleRef, RuleContext } from '../config/rule.extensions'; import { ExtensionConfig } from '../config/extension.config'; import { ExtensionLoaderService } from './extension-loader.service'; @@ -75,6 +75,12 @@ export function provideExtensionConfigValues(extensionConfigValue: ExtensionConf providedIn: 'root' }) export class ExtensionService { + protected loader = inject(ExtensionLoaderService); + protected componentRegister = inject(ComponentRegisterService); + protected ruleService = inject(RuleService); + protected extensionJsons = inject(EXTENSION_JSONS); + protected extensionJsonValues = inject(EXTENSION_JSON_VALUES); + configPath = 'assets/app.extensions.json'; pluginsPath = 'assets/plugins'; @@ -88,13 +94,7 @@ export class ExtensionService { protected config: ExtensionConfig = null; protected onSetup$ = new BehaviorSubject<ExtensionConfig>(this.config); - constructor( - protected loader: ExtensionLoaderService, - protected componentRegister: ComponentRegisterService, - protected ruleService: RuleService, - @Inject(EXTENSION_JSONS) protected extensionJsons: string[], - @Inject(EXTENSION_JSON_VALUES) protected extensionJsonValues: ExtensionConfig[] - ) { + constructor() { this.setup$ = this.onSetup$.asObservable(); } @@ -103,7 +103,7 @@ export class ExtensionService { * @returns The loaded config data */ async load(): Promise<ExtensionConfig> { - const config = await this.loader.load(this.configPath, this.pluginsPath, this.extensionJsons.flat(), this.extensionJsonValues.flat()); + const config = await this.loader.load(this.configPath, this.pluginsPath, this.extensionJsons.flat(), this.extensionJsonValues.flat() as any); this.setup(config); return config; diff --git a/lib/extensions/src/lib/services/rule.service.ts b/lib/extensions/src/lib/services/rule.service.ts index fc12ad3e94..44acfce362 100644 --- a/lib/extensions/src/lib/services/rule.service.ts +++ b/lib/extensions/src/lib/services/rule.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { RuleRef, RuleContext, RuleEvaluator, RuleParameter } from '../config/rule.extensions'; import { ExtensionConfig } from '../config/extension.config'; import { ExtensionLoaderService } from './extension-loader.service'; @@ -24,12 +24,12 @@ import { ExtensionLoaderService } from './extension-loader.service'; providedIn: 'root' }) export class RuleService { + protected loader = inject(ExtensionLoaderService); + context: RuleContext = null; rules: Array<RuleRef> = []; evaluators: { [key: string]: RuleEvaluator } = {}; - constructor(protected loader: ExtensionLoaderService) {} - setup(config: ExtensionConfig) { this.rules = this.loader.getRules(config); } diff --git a/lib/insights/src/lib/analytics-process/components/analytics-generator.component.ts b/lib/insights/src/lib/analytics-process/components/analytics-generator.component.ts index 977ab60d1d..f76a62565e 100644 --- a/lib/insights/src/lib/analytics-process/components/analytics-generator.component.ts +++ b/lib/insights/src/lib/analytics-process/components/analytics-generator.component.ts @@ -45,7 +45,7 @@ import { AnalyticsReportHeatMapComponent } from './analytics-report-heat-map.com encapsulation: ViewEncapsulation.None }) export class AnalyticsGeneratorComponent implements OnChanges { - private analyticsService = inject(AnalyticsService); + private readonly analyticsService = inject(AnalyticsService); /** reportId. */ @Input() diff --git a/lib/insights/src/lib/analytics-process/components/analytics-report-heat-map.component.ts b/lib/insights/src/lib/analytics-process/components/analytics-report-heat-map.component.ts index 60a85729e4..661c78c355 100644 --- a/lib/insights/src/lib/analytics-process/components/analytics-report-heat-map.component.ts +++ b/lib/insights/src/lib/analytics-process/components/analytics-report-heat-map.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { AnalyticsService } from '../services/analytics.service'; import { ParameterValueModel } from '../../diagram/models/report/parameter-value.model'; @@ -29,6 +29,9 @@ import { DiagramComponent } from '../../diagram/components/diagram.component'; templateUrl: './analytics-report-heat-map.component.html' }) export class AnalyticsReportHeatMapComponent implements OnInit { + private readonly analyticsService = inject(AnalyticsService); + private readonly formBuilder = inject(UntypedFormBuilder); + /** reportId. */ @Input() report: any; @@ -48,8 +51,6 @@ export class AnalyticsReportHeatMapComponent implements OnInit { currentMetricColors: any; metricType: string; - constructor(private analyticsService: AnalyticsService, private formBuilder: UntypedFormBuilder) {} - ngOnInit() { this.initForm(); this.field.id = 'metrics'; diff --git a/lib/insights/src/lib/analytics-process/components/analytics-report-list.component.ts b/lib/insights/src/lib/analytics-process/components/analytics-report-list.component.ts index 71aa009c0d..de47daacad 100644 --- a/lib/insights/src/lib/analytics-process/components/analytics-report-list.component.ts +++ b/lib/insights/src/lib/analytics-process/components/analytics-report-list.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, inject } from '@angular/core'; import { Observable, Observer } from 'rxjs'; import { ReportParametersModel } from '../../diagram/models/report/report-parameters.model'; import { AnalyticsService } from '../services/analytics.service'; @@ -36,6 +36,8 @@ export const LAYOUT_GRID = 'GRID'; encapsulation: ViewEncapsulation.None }) export class AnalyticsReportListComponent implements OnInit { + private readonly analyticsService = inject(AnalyticsService); + /** layout Type LIST or GRID. */ @Input() layoutType: string = LAYOUT_LIST; @@ -66,7 +68,7 @@ export class AnalyticsReportListComponent implements OnInit { private reportObserver: Observer<any>; - constructor(private analyticsService: AnalyticsService) { + constructor() { this.report$ = new Observable<ReportParametersModel>((observer) => (this.reportObserver = observer)).pipe(share()); } diff --git a/lib/insights/src/lib/analytics-process/components/analytics-report-parameters.component.ts b/lib/insights/src/lib/analytics-process/components/analytics-report-parameters.component.ts index 997d85a7a3..6439fda60c 100644 --- a/lib/insights/src/lib/analytics-process/components/analytics-report-parameters.component.ts +++ b/lib/insights/src/lib/analytics-process/components/analytics-report-parameters.component.ts @@ -133,6 +133,12 @@ export interface ReportFormValues { encapsulation: ViewEncapsulation.None }) export class AnalyticsReportParametersComponent implements OnInit, OnChanges, AfterContentChecked { + private readonly analyticsService = inject(AnalyticsService); + private readonly formBuilder = inject(FormBuilder); + private readonly downloadService = inject(DownloadService); + private readonly dialog = inject(MatDialog); + private readonly dateAdapter = inject(AdfDateFnsAdapter); + /** appId ID of the target app. */ @Input() appId: number; @@ -188,14 +194,6 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, Af private readonly destroyRef = inject(DestroyRef); - constructor( - private analyticsService: AnalyticsService, - private formBuilder: FormBuilder, - private downloadService: DownloadService, - private dialog: MatDialog, - private dateAdapter: AdfDateFnsAdapter - ) {} - ngOnInit() { this.onDropdownChanged.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((field: any) => { const paramDependOn = this.reportParameters.definition.parameters.find((param) => param.dependsOn === field.id); diff --git a/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.ts b/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.ts index 9282dfe0ed..f0265bd683 100644 --- a/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.ts +++ b/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.ts @@ -18,7 +18,7 @@ /* eslint-disable @angular-eslint/no-input-rename */ import { ADF_DATE_FORMATS, AdfDateFnsAdapter } from '@alfresco/adf-core'; -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, inject } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { ReportParameterDetailsModel } from '../../../../diagram/models/report/report-parameter-details.model'; @@ -50,6 +50,8 @@ interface DateRangeProps { encapsulation: ViewEncapsulation.None }) export class DateRangeWidgetComponent implements OnInit { + private readonly dateAdapter = inject<DateAdapter<Date>>(DateAdapter); + @Input('group') dateRange: FormGroup<DateRangeProps>; @@ -64,8 +66,6 @@ export class DateRangeWidgetComponent implements OnInit { startDateValue = new Date(); endDateValue = new Date(); - constructor(private dateAdapter: DateAdapter<Date>) {} - ngOnInit() { const dateAdapter = this.dateAdapter as AdfDateFnsAdapter; dateAdapter.displayFormat = DISPLAY_FORMAT; diff --git a/lib/insights/src/lib/analytics-process/services/analytics.service.ts b/lib/insights/src/lib/analytics-process/services/analytics.service.ts index 2bf98ccd89..be78a37c7c 100644 --- a/lib/insights/src/lib/analytics-process/services/analytics.service.ts +++ b/lib/insights/src/lib/analytics-process/services/analytics.service.ts @@ -16,7 +16,7 @@ */ import { AlfrescoApiService } from '@alfresco/adf-content-services'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from, of } from 'rxjs'; import { ParameterValueModel } from '../../diagram/models/report/parameter-value.model'; import { ReportParametersModel } from '../../diagram/models/report/report-parameters.model'; @@ -34,6 +34,8 @@ import { LineChart } from '../../diagram/models/chart/line-chart.model'; @Injectable({ providedIn: 'root' }) export class AnalyticsService { + private readonly apiService = inject(AlfrescoApiService); + private _reportApi: ReportApi; get reportApi(): ReportApi { this._reportApi = this._reportApi ?? new ReportApi(this.apiService.getInstance()); @@ -46,8 +48,6 @@ export class AnalyticsService { return this._processDefinitionsApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Retrieve all the Deployed app * @param appId application id diff --git a/lib/insights/src/lib/diagram/components/diagram.component.ts b/lib/insights/src/lib/diagram/components/diagram.component.ts index 58c8801000..70679586d8 100644 --- a/lib/insights/src/lib/diagram/components/diagram.component.ts +++ b/lib/insights/src/lib/diagram/components/diagram.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, inject } from '@angular/core'; import { DiagramElementModel } from '../models/diagram/diagram-element.model'; import { DiagramModel } from '../models/diagram/diagram.model'; import { DiagramColorService } from '../services/diagram-color.service'; @@ -73,6 +73,10 @@ const PADDING_HEIGHT: number = 60; templateUrl: './diagram.component.html' }) export class DiagramComponent implements OnChanges { + private readonly diagramColorService = inject(DiagramColorService); + private readonly raphaelService = inject(RaphaelService); + private readonly diagramsService = inject(DiagramsService); + /** processDefinitionId. */ @Input() processDefinitionId: any; @@ -111,8 +115,6 @@ export class DiagramComponent implements OnChanges { diagram: DiagramModel; - constructor(private diagramColorService: DiagramColorService, private raphaelService: RaphaelService, private diagramsService: DiagramsService) {} - ngOnChanges() { this.reset(); this.diagramColorService.setTotalColors(this.metricColor); diff --git a/lib/insights/src/lib/diagram/components/raphael/raphael-base.ts b/lib/insights/src/lib/diagram/components/raphael/raphael-base.ts index 557701db99..618b6f6e86 100644 --- a/lib/insights/src/lib/diagram/components/raphael/raphael-base.ts +++ b/lib/insights/src/lib/diagram/components/raphael/raphael-base.ts @@ -20,8 +20,8 @@ import { RaphaelService } from './raphael.service'; @Directive() export class RaphaelBase { - private raphaelService = inject(RaphaelService); - private element = inject(ElementRef); + private readonly raphaelService = inject(RaphaelService); + private readonly element = inject(ElementRef); paper: any; diff --git a/lib/insights/src/lib/diagram/services/diagrams.service.ts b/lib/insights/src/lib/diagram/services/diagrams.service.ts index 523e5eb81a..2906b48ad1 100644 --- a/lib/insights/src/lib/diagram/services/diagrams.service.ts +++ b/lib/insights/src/lib/diagram/services/diagrams.service.ts @@ -16,20 +16,20 @@ */ import { AlfrescoApiService } from '@alfresco/adf-content-services'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from } from 'rxjs'; import { ModelJsonBpmnApi } from '@alfresco/js-api'; @Injectable({ providedIn: 'root' }) export class DiagramsService { + private readonly apiService = inject(AlfrescoApiService); + private _modelJsonBpmnApi: ModelJsonBpmnApi; get modelJsonBpmnApi(): ModelJsonBpmnApi { this._modelJsonBpmnApi = this._modelJsonBpmnApi ?? new ModelJsonBpmnApi(this.apiService.getInstance()); return this._modelJsonBpmnApi; } - constructor(private apiService: AlfrescoApiService) {} - getProcessDefinitionModel(processDefinitionId: string): Observable<any> { return from(this.modelJsonBpmnApi.getModelJSON(processDefinitionId)); } diff --git a/lib/js-api/src/alfrescoApiClient.ts b/lib/js-api/src/alfrescoApiClient.ts index 69b316891a..5b456ec39d 100644 --- a/lib/js-api/src/alfrescoApiClient.ts +++ b/lib/js-api/src/alfrescoApiClient.ts @@ -64,7 +64,7 @@ export function buildCollectionParam(param: string[], collectionFormat: string): } export class AlfrescoApiClient implements LegacyHttpClient { - private eventEmitter = new EventEmitter(); + private readonly eventEmitter = new EventEmitter(); storage: Storage; host: string; diff --git a/lib/process-services-cloud/.storybook/main.ts b/lib/process-services-cloud/.storybook/main.ts index a378293848..d38c0fcb84 100644 --- a/lib/process-services-cloud/.storybook/main.ts +++ b/lib/process-services-cloud/.storybook/main.ts @@ -14,6 +14,9 @@ const config: StorybookConfig = { framework: { name: getAbsolutePath('@storybook/angular'), options: {} + }, + core: { + disableTelemetry: true } }; diff --git a/lib/process-services-cloud/src/lib/app/services/apps-process-cloud.service.ts b/lib/process-services-cloud/src/lib/app/services/apps-process-cloud.service.ts index a239e5be6d..814472cf17 100644 --- a/lib/process-services-cloud/src/lib/app/services/apps-process-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/app/services/apps-process-cloud.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from, of } from 'rxjs'; import { map } from 'rxjs/operators'; import { AppConfigService } from '@alfresco/adf-core'; @@ -26,12 +26,12 @@ import { RequestOptions } from '@alfresco/js-api'; @Injectable({ providedIn: 'root' }) export class AppsProcessCloudService { + private readonly adfHttpClient = inject(AdfHttpClient); + private readonly appConfigService = inject(AppConfigService); + deployedApps: ApplicationInstanceModel[]; - constructor( - private readonly adfHttpClient: AdfHttpClient, - private readonly appConfigService: AppConfigService - ) { + constructor() { this.loadApps(); } diff --git a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts index 53122af365..3c299e0af1 100644 --- a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts @@ -21,13 +21,11 @@ import { DestroyRef, EventEmitter, HostListener, - Inject, inject, InjectionToken, Input, OnChanges, OnInit, - Optional, Output, SimpleChanges } from '@angular/core'; @@ -190,7 +188,9 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, private readonly destroyRef = inject(DestroyRef); - constructor(@Optional() @Inject(FORM_CLOUD_FIELD_VALIDATORS_TOKEN) injectedFieldValidators?: FormFieldValidator[]) { + constructor() { + const injectedFieldValidators = inject(FORM_CLOUD_FIELD_VALIDATORS_TOKEN, { optional: true }); + super(); this.loadInjectedFieldValidators(injectedFieldValidators); this.spinnerService.initSpinnerHandling(this.destroyRef); diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.ts b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.ts index c321546b2a..e55019f68b 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.ts @@ -58,11 +58,11 @@ const VALID_ALIAS = [ALIAS_ROOT_FOLDER, ALIAS_USER_FOLDER, '-shared-']; encapsulation: ViewEncapsulation.None }) export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent implements OnInit, OnDestroy { - private contentNodeSelectorService = inject(ContentCloudNodeSelectorService); - private appConfigService = inject(AppConfigService); - private apiService = inject(AlfrescoApiService); - private contentNodeSelectorPanelService = inject(ContentNodeSelectorPanelService); - private newVersionUploaderService = inject(NewVersionUploaderService); + private readonly contentNodeSelectorService = inject(ContentCloudNodeSelectorService); + private readonly appConfigService = inject(AppConfigService); + private readonly apiService = inject(AlfrescoApiService); + private readonly contentNodeSelectorPanelService = inject(ContentNodeSelectorPanelService); + private readonly newVersionUploaderService = inject(NewVersionUploaderService); typeId = 'AttachFileCloudWidgetComponent'; rootNodeId = ALIAS_USER_FOLDER; diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/file-properties-table/file-properties-table-cloud.component.ts b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/file-properties-table/file-properties-table-cloud.component.ts index 166eca49c9..193dd89b6e 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/file-properties-table/file-properties-table-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/file-properties-table/file-properties-table-cloud.component.ts @@ -39,8 +39,8 @@ const RETRIEVE_METADATA_OPTION = 'retrieveMetadata'; styleUrls: ['./file-properties-table-cloud.component.scss'] }) export class FilePropertiesTableCloudComponent { - private localizedDatePipe = inject(LocalizedDatePipe); - private thumbnailService = inject(ThumbnailService); + private readonly localizedDatePipe = inject(LocalizedDatePipe); + private readonly thumbnailService = inject(ThumbnailService); @Input() uploadedFiles; diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table-adapter.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table-adapter.widget.ts index cb0a2ca8c6..a3269088b7 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table-adapter.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table-adapter.widget.ts @@ -20,9 +20,9 @@ import { DataTablePathParserHelper } from './helpers/data-table-path-parser.help import { Subject } from 'rxjs'; export class WidgetDataTableAdapter implements DataTableAdapter { - private adapter: ObjectDataTableAdapter; - private columnKeys: string[] = []; - private helper = new DataTablePathParserHelper(); + private readonly adapter: ObjectDataTableAdapter; + private readonly columnKeys: string[] = []; + private readonly helper = new DataTablePathParserHelper(); get selectedRow(): DataRow { return this.adapter.selectedRow; diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.ts index 42f60a457d..59d89515d3 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.ts @@ -61,9 +61,9 @@ export class DataTableWidgetComponent extends WidgetComponent implements OnInit private rowsData: DataRow[]; private columnsSchema: DataColumn[]; private variableName: string; - private defaultResponseProperty = 'data'; - private pathParserHelper = new DataTablePathParserHelper(); - private formCloudService = inject(FormCloudService); + private readonly defaultResponseProperty = 'data'; + private readonly pathParserHelper = new DataTablePathParserHelper(); + private readonly formCloudService = inject(FormCloudService); ngOnInit(): void { this.init(); diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.ts index deaa1c126f..ee9eaa4d84 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/dropdown/dropdown-cloud.widget.ts @@ -76,7 +76,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI private readonly formCloudService = inject(FormCloudService); private readonly appConfig = inject(AppConfigService); private readonly formUtilsService = inject(FormUtilsService); - private destroyRef = inject(DestroyRef); + private readonly destroyRef = inject(DestroyRef); typeId = 'DropdownCloudWidgetComponent'; showInputFilter = false; @@ -93,7 +93,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI private readonly defaultVariableOptionLabel = 'name'; private readonly defaultVariableOptionPath = 'data'; - private debounceSetValue = new Subject<void>(); + private readonly debounceSetValue = new Subject<void>(); get showRequiredMessage(): boolean { return this.dropdownControl.touched && this.dropdownControl.errors?.required && !this.isRestApiFailed && !this.variableOptionsFailed; diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/people/people-cloud.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/people/people-cloud.widget.ts index a8bb73ff8a..892d65962e 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/people/people-cloud.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/people/people-cloud.widget.ts @@ -48,7 +48,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; encapsulation: ViewEncapsulation.None }) export class PeopleCloudWidgetComponent extends WidgetComponent implements OnInit { - private identityUserService = inject(IdentityUserService); + private readonly identityUserService = inject(IdentityUserService); typeId = 'PeopleCloudWidgetComponent'; appName: string; diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/properties-viewer/properties-viewer-wrapper/properties-viewer-wrapper.component.ts b/lib/process-services-cloud/src/lib/form/components/widgets/properties-viewer/properties-viewer-wrapper/properties-viewer-wrapper.component.ts index 0b3f605701..2f8989897a 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/properties-viewer/properties-viewer-wrapper/properties-viewer-wrapper.component.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/properties-viewer/properties-viewer-wrapper/properties-viewer-wrapper.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation, inject } from '@angular/core'; import { PresetConfig, NodesApiService, ContentMetadataComponent } from '@alfresco/adf-content-services'; import { Node } from '@alfresco/js-api'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; @@ -30,6 +30,8 @@ import { CommonModule } from '@angular/common'; encapsulation: ViewEncapsulation.None }) export class PropertiesViewerWrapperComponent implements OnInit, OnChanges { + private readonly nodesApiService = inject(NodesApiService); + node: Node; loading = true; @@ -78,8 +80,6 @@ export class PropertiesViewerWrapperComponent implements OnInit, OnChanges { @Output() nodeContentLoaded = new EventEmitter<Node>(); - constructor(private nodesApiService: NodesApiService) {} - ngOnChanges(changes: SimpleChanges): void { if (changes?.['nodeId']?.currentValue && !changes['nodeId'].isFirstChange()) { this.getNode(changes['nodeId'].currentValue); diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/radio-buttons/radio-buttons-cloud.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/radio-buttons/radio-buttons-cloud.widget.ts index 301cbedfc8..34ce3090aa 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/radio-buttons/radio-buttons-cloud.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/radio-buttons/radio-buttons-cloud.widget.ts @@ -46,9 +46,9 @@ import { FormsModule } from '@angular/forms'; encapsulation: ViewEncapsulation.None }) export class RadioButtonsCloudWidgetComponent extends WidgetComponent implements OnInit { - private formCloudService = inject(FormCloudService); - private translateService = inject(TranslateService); - private formUtilsService = inject(FormUtilsService); + private readonly formCloudService = inject(FormCloudService); + private readonly translateService = inject(TranslateService); + private readonly formUtilsService = inject(FormUtilsService); typeId = 'RadioButtonsCloudWidgetComponent'; restApiError: ErrorMessageModel; diff --git a/lib/process-services-cloud/src/lib/form/services/content-cloud-node-selector.service.ts b/lib/process-services-cloud/src/lib/form/services/content-cloud-node-selector.service.ts index 9d029c1a14..6e3d16c6d2 100644 --- a/lib/process-services-cloud/src/lib/form/services/content-cloud-node-selector.service.ts +++ b/lib/process-services-cloud/src/lib/form/services/content-cloud-node-selector.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { NotificationService } from '@alfresco/adf-core'; import { MatDialog } from '@angular/material/dialog'; import { ContentNodeSelectorComponent, ContentNodeSelectorComponentData, NodeAction, AlfrescoApiService } from '@alfresco/adf-content-services'; @@ -28,6 +28,10 @@ import { DestinationFolderPathModel } from '../models/form-cloud-representation. providedIn: 'root' }) export class ContentCloudNodeSelectorService { + private readonly apiService = inject(AlfrescoApiService); + private readonly notificationService = inject(NotificationService); + private readonly dialog = inject(MatDialog); + private _nodesApi: NodesApi; get nodesApi(): NodesApi { this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance()); @@ -36,12 +40,6 @@ export class ContentCloudNodeSelectorService { sourceNodeNotFound = false; - constructor( - private apiService: AlfrescoApiService, - private notificationService: NotificationService, - private dialog: MatDialog - ) {} - openUploadFileDialog( currentFolderId?: string, selectionMode?: string, diff --git a/lib/process-services-cloud/src/lib/form/services/form-cloud.service.ts b/lib/process-services-cloud/src/lib/form/services/form-cloud.service.ts index f08dd3ca54..7e8cb9785a 100644 --- a/lib/process-services-cloud/src/lib/form/services/form-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/form/services/form-cloud.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { inject, Inject, Injectable, InjectionToken, Optional } from '@angular/core'; +import { inject, Injectable, InjectionToken } from '@angular/core'; import { FormValues, FormModel, FormFieldOption, FormFieldValidator, FormService } from '@alfresco/adf-core'; import { Observable, from, EMPTY } from 'rxjs'; import { expand, map, reduce, switchMap } from 'rxjs/operators'; @@ -25,7 +25,6 @@ import { TaskVariableCloud } from '../models/task-variable-cloud.model'; import { BaseCloudService } from '../../services/base-cloud.service'; import { FormContent } from '../../services/form-fields.interfaces'; import { FormCloudServiceInterface } from './form-cloud.service.interface'; -import { AdfHttpClient } from '@alfresco/adf-core/api'; export const FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN = new InjectionToken<FormFieldValidator[]>('FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN'); @@ -34,21 +33,13 @@ export const FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN = new InjectionToken<Form }) export class FormCloudService extends BaseCloudService implements FormCloudServiceInterface { private _uploadApi: UploadApi; - private fieldValidators: FormFieldValidator[]; - private formService = inject(FormService); + private readonly fieldValidators: FormFieldValidator[] = inject(FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN, { optional: true }) ?? []; + private readonly formService = inject(FormService); get uploadApi(): UploadApi { this._uploadApi = this._uploadApi ?? new UploadApi(this.apiService.getInstance()); return this._uploadApi; } - constructor( - adfHttpClient: AdfHttpClient, - @Optional() @Inject(FORM_CLOUD_SERVICE_FIELD_VALIDATORS_TOKEN) injectedFieldValidators?: FormFieldValidator[] - ) { - super(adfHttpClient); - this.fieldValidators = injectedFieldValidators || []; - } - /** * Gets the form definition of a task. * diff --git a/lib/process-services-cloud/src/lib/form/services/process-cloud-content.service.ts b/lib/process-services-cloud/src/lib/form/services/process-cloud-content.service.ts index 35def80a1e..22cc4637bd 100644 --- a/lib/process-services-cloud/src/lib/form/services/process-cloud-content.service.ts +++ b/lib/process-services-cloud/src/lib/form/services/process-cloud-content.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from } from 'rxjs'; import { map } from 'rxjs/operators'; import { DownloadService } from '@alfresco/adf-core'; @@ -26,6 +26,11 @@ import { AuthenticationApi, Node, UploadApi } from '@alfresco/js-api'; providedIn: 'root' }) export class ProcessCloudContentService { + private readonly apiService = inject(AlfrescoApiService); + private readonly nodesApiService = inject(NodesApiService); + private readonly contentService = inject(ContentService); + private readonly downloadService = inject(DownloadService); + private _uploadApi: UploadApi; get uploadApi(): UploadApi { this._uploadApi = this._uploadApi ?? new UploadApi(this.apiService.getInstance()); @@ -38,13 +43,6 @@ export class ProcessCloudContentService { return this._authenticationApi; } - constructor( - private apiService: AlfrescoApiService, - private nodesApiService: NodesApiService, - private contentService: ContentService, - private downloadService: DownloadService - ) {} - createTemporaryRawRelatedContent(file: File, nodeId: string): Observable<Node> { return from(this.uploadApi.uploadFile(file, '', nodeId, null, { overwrite: true })).pipe( map((res: any) => ({ diff --git a/lib/process-services-cloud/src/lib/form/services/spinner/form-cloud-spinner.service.ts b/lib/process-services-cloud/src/lib/form/services/spinner/form-cloud-spinner.service.ts index e6cf92e986..15af2a7e8c 100644 --- a/lib/process-services-cloud/src/lib/form/services/spinner/form-cloud-spinner.service.ts +++ b/lib/process-services-cloud/src/lib/form/services/spinner/form-cloud-spinner.service.ts @@ -24,8 +24,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Injectable() export class FormCloudSpinnerService { - private formService = inject(FormService); - private overlay = inject(Overlay); + private readonly formService = inject(FormService); + private readonly overlay = inject(Overlay); private overlayRef?: OverlayRef = null; diff --git a/lib/process-services-cloud/src/lib/group/components/group-cloud.component.ts b/lib/process-services-cloud/src/lib/group/components/group-cloud.component.ts index 0cf25d3816..0cd2542b4c 100644 --- a/lib/process-services-cloud/src/lib/group/components/group-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/group/components/group-cloud.component.ts @@ -74,6 +74,8 @@ import { IconModule } from '@alfresco/adf-core'; encapsulation: ViewEncapsulation.None }) export class GroupCloudComponent implements OnInit, OnChanges { + private readonly identityGroupService = inject(IdentityGroupService); + /** Label for the user selection component. */ @Input() label: string; @@ -143,7 +145,7 @@ export class GroupCloudComponent implements OnInit, OnChanges { warning = new EventEmitter<any>(); @ViewChild('groupInput') - private groupInput: ElementRef<HTMLInputElement>; + private readonly groupInput: ElementRef<HTMLInputElement>; private searchGroups: IdentityGroupModel[] = []; @@ -165,8 +167,6 @@ export class GroupCloudComponent implements OnInit, OnChanges { private readonly destroyRef = inject(DestroyRef); - constructor(private identityGroupService: IdentityGroupService) {} - ngOnInit(): void { this.initSearch(); } diff --git a/lib/process-services-cloud/src/lib/group/services/identity-group.service.spec.ts b/lib/process-services-cloud/src/lib/group/services/identity-group.service.spec.ts index ece6fd6a58..4bdd9e6d6f 100644 --- a/lib/process-services-cloud/src/lib/group/services/identity-group.service.spec.ts +++ b/lib/process-services-cloud/src/lib/group/services/identity-group.service.spec.ts @@ -20,8 +20,8 @@ import { TranslateModule } from '@ngx-translate/core'; import { IdentityGroupService } from './identity-group.service'; import { mockFoodGroups } from '../mock/group-cloud.mock'; import { AdfHttpClient } from '@alfresco/adf-core/api'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { HttpErrorResponse } from '@angular/common/http'; +import { provideHttpClient, HttpErrorResponse } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; const mockHttpErrorResponse = new HttpErrorResponse({ error: 'Mock Error', @@ -36,8 +36,8 @@ describe('IdentityGroupService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), HttpClientTestingModule], - providers: [IdentityGroupService] + imports: [TranslateModule.forRoot()], + providers: [IdentityGroupService, provideHttpClient(), provideHttpClientTesting()] }); service = TestBed.inject(IdentityGroupService); adfHttpClient = TestBed.inject(AdfHttpClient); @@ -64,18 +64,18 @@ describe('IdentityGroupService', () => { const searchSpy = spyOn(service, 'search').and.callThrough(); - service.search('fake').subscribe( - () => { + service.search('fake').subscribe({ + next: () => { fail('expected an error, not groups'); }, - (error) => { + error: (error) => { expect(searchSpy).toHaveBeenCalled(); expect(error.status).toEqual(404); expect(error.statusText).toEqual('Not Found'); expect(error.error).toEqual('Mock Error'); done(); } - ); + }); }); it('should fetch groups by roles', (done) => { @@ -107,11 +107,11 @@ describe('IdentityGroupService', () => { roles: ['fake-role-1', 'fake-role-2'], withinApplication: '' }) - .subscribe( - () => { + .subscribe({ + next: () => { fail('expected an error, not groups'); }, - (error) => { + error: (error) => { expect(searchSpy).toHaveBeenCalled(); expect(service.queryParams).toEqual({ search: 'fake', @@ -122,7 +122,7 @@ describe('IdentityGroupService', () => { expect(error.error).toEqual('Mock Error'); done(); } - ); + }); }); it('should fetch groups within app', (done) => { @@ -171,11 +171,11 @@ describe('IdentityGroupService', () => { roles: [], withinApplication: 'fake-app-name' }) - .subscribe( - () => { + .subscribe({ + next: () => { fail('expected an error, not groups'); }, - (error) => { + error: (error) => { expect(searchSpy).toHaveBeenCalled(); expect(service.queryParams).toEqual({ search: 'fake', @@ -186,7 +186,7 @@ describe('IdentityGroupService', () => { expect(error.error).toEqual('Mock Error'); done(); } - ); + }); }); }); }); diff --git a/lib/process-services-cloud/src/lib/group/services/identity-group.service.ts b/lib/process-services-cloud/src/lib/group/services/identity-group.service.ts index 5b6fadf9d2..f6970490bc 100644 --- a/lib/process-services-cloud/src/lib/group/services/identity-group.service.ts +++ b/lib/process-services-cloud/src/lib/group/services/identity-group.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AppConfigService, OAuth2Service } from '@alfresco/adf-core'; import { EMPTY, Observable } from 'rxjs'; import { IdentityGroupModel } from '../models/identity-group.model'; @@ -29,9 +29,10 @@ export interface IdentityGroupFilterInterface { @Injectable({ providedIn: 'root' }) export class IdentityGroupService { - queryParams: { search: string; application?: string; roles?: string[] }; + private readonly oAuth2Service = inject(OAuth2Service); + private readonly appConfigService = inject(AppConfigService); - constructor(private oAuth2Service: OAuth2Service, private appConfigService: AppConfigService) {} + queryParams: { search: string; application?: string; roles?: string[] }; public search(name: string, filters?: IdentityGroupFilterInterface): Observable<IdentityGroupModel[]> { if (name.trim() === '') { diff --git a/lib/process-services-cloud/src/lib/people/components/people-cloud.component.ts b/lib/process-services-cloud/src/lib/people/components/people-cloud.component.ts index 6251db507c..54e4d38b0c 100644 --- a/lib/process-services-cloud/src/lib/people/components/people-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/people/components/people-cloud.component.ts @@ -78,6 +78,8 @@ import { MatTooltipModule } from '@angular/material/tooltip'; encapsulation: ViewEncapsulation.None }) export class PeopleCloudComponent implements OnInit, OnChanges, AfterViewInit { + private readonly identityUserService = inject(IdentityUserService); + /** Label for the user selection component. */ @Input() label: string; @@ -205,7 +207,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, AfterViewInit { warning = new EventEmitter<any>(); @ViewChild('userInput') - private userInput: ElementRef<HTMLInputElement>; + private readonly userInput: ElementRef<HTMLInputElement>; private searchUsers: IdentityUserModel[] = []; @@ -227,8 +229,6 @@ export class PeopleCloudComponent implements OnInit, OnChanges, AfterViewInit { private readonly destroyRef = inject(DestroyRef); - constructor(private identityUserService: IdentityUserService) {} - ngOnInit(): void { this.initSearch(); } diff --git a/lib/process-services-cloud/src/lib/people/services/identity-user.service.ts b/lib/process-services-cloud/src/lib/people/services/identity-user.service.ts index f288970292..08a367e2e4 100644 --- a/lib/process-services-cloud/src/lib/people/services/identity-user.service.ts +++ b/lib/process-services-cloud/src/lib/people/services/identity-user.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { AppConfigService, JwtHelperService, OAuth2Service } from '@alfresco/adf-core'; import { EMPTY, Observable } from 'rxjs'; import { IdentityUserModel } from '../models/identity-user.model'; @@ -32,9 +32,11 @@ export interface IdentityUserFilterInterface { providedIn: 'root' }) export class IdentityUserService { - queryParams: { search: string; application?: string; roles?: string[]; groups?: string[] }; + private readonly jwtHelperService = inject(JwtHelperService); + private readonly oAuth2Service = inject(OAuth2Service); + private readonly appConfigService = inject(AppConfigService); - constructor(private jwtHelperService: JwtHelperService, private oAuth2Service: OAuth2Service, private appConfigService: AppConfigService) {} + queryParams: { search: string; application?: string; roles?: string[]; groups?: string[] }; /** * Gets the name and other basic details of the current user. diff --git a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter/edit-process-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter/edit-process-filter-cloud.component.ts index e0660772a9..53510c0e62 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter/edit-process-filter-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter/edit-process-filter-cloud.component.ts @@ -100,6 +100,15 @@ interface ProcessFilterFormProps { encapsulation: ViewEncapsulation.None }) export class EditProcessFilterCloudComponent implements OnChanges { + private readonly formBuilder = inject(FormBuilder); + dialog = inject(MatDialog); + private readonly dateAdapter = inject<DateAdapter<Date>>(DateAdapter); + private readonly userPreferencesService = inject(UserPreferencesService); + private readonly translateService = inject(TranslationService); + private readonly processFilterCloudService = inject(ProcessFilterCloudService); + private readonly appsProcessCloudService = inject(AppsProcessCloudService); + private readonly processCloudService = inject(ProcessCloudService); + /** The name of the application. */ @Input() appName: string = ''; @@ -217,16 +226,7 @@ export class EditProcessFilterCloudComponent implements OnChanges { private readonly destroyRef = inject(DestroyRef); - constructor( - private formBuilder: FormBuilder, - public dialog: MatDialog, - private dateAdapter: DateAdapter<Date>, - private userPreferencesService: UserPreferencesService, - private translateService: TranslationService, - private processFilterCloudService: ProcessFilterCloudService, - private appsProcessCloudService: AppsProcessCloudService, - private processCloudService: ProcessCloudService - ) { + constructor() { // Use effect to react to locale signal changes (must be in injection context) effect(() => { const locale = this.userPreferencesService.localeSignal(); diff --git a/lib/process-services-cloud/src/lib/process/process-filters/models/process-filter-cloud.model.ts b/lib/process-services-cloud/src/lib/process/process-filters/models/process-filter-cloud.model.ts index ec20fede24..1067652561 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/models/process-filter-cloud.model.ts +++ b/lib/process-services-cloud/src/lib/process/process-filters/models/process-filter-cloud.model.ts @@ -64,7 +64,7 @@ export class ProcessFilterCloudModel { processVariableFilters?: ProcessVariableFilterModel[]; - private dateRangeFilterService = new DateRangeFilterService(); + private readonly dateRangeFilterService = new DateRangeFilterService(); private _completedFrom: string | null; private _completedTo: string | null; private _startFrom: string | null; diff --git a/lib/process-services-cloud/src/lib/process/process-filters/services/process-filter-cloud.service.ts b/lib/process-services-cloud/src/lib/process/process-filters/services/process-filter-cloud.service.ts index fc39105682..83eb292cbb 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/services/process-filter-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/process/process-filters/services/process-filter-cloud.service.ts @@ -45,9 +45,9 @@ const PROCESS_EVENT_SUBSCRIPTION_QUERY = ` providedIn: 'root' }) export class ProcessFilterCloudService { - private filtersSubject: BehaviorSubject<ProcessFilterCloudModel[]>; + private readonly filtersSubject: BehaviorSubject<ProcessFilterCloudModel[]>; filters$: Observable<ProcessFilterCloudModel[]>; - private filterKeyToBeRefreshedSource = new Subject<string>(); + private readonly filterKeyToBeRefreshedSource = new Subject<string>(); filterKeyToBeRefreshed$: Observable<string> = this.filterKeyToBeRefreshedSource.asObservable(); protected readonly preferenceService = inject<PreferenceCloudServiceInterface>(PROCESS_FILTERS_SERVICE_TOKEN); diff --git a/lib/process-services-cloud/src/lib/process/process-header/components/process-header-cloud.component.ts b/lib/process-services-cloud/src/lib/process/process-header/components/process-header-cloud.component.ts index ca99502599..d475b3fe71 100644 --- a/lib/process-services-cloud/src/lib/process/process-header/components/process-header-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/process-header/components/process-header-cloud.component.ts @@ -40,6 +40,10 @@ import { NgIf } from '@angular/common'; host: { class: 'adf-cloud-process-header' } }) export class ProcessHeaderCloudComponent implements OnChanges, OnInit { + private readonly processCloudService = inject(ProcessCloudService); + private readonly translationService = inject(TranslationService); + private readonly appConfig = inject(AppConfigService); + /** (Required) The name of the application. */ @Input({ required: true }) appName: string = ''; @@ -59,12 +63,6 @@ export class ProcessHeaderCloudComponent implements OnChanges, OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private processCloudService: ProcessCloudService, - private translationService: TranslationService, - private appConfig: AppConfigService - ) {} - ngOnInit() { this.dateFormat = this.appConfig.get('adf-cloud-process-header.defaultDateFormat'); this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale'); diff --git a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts index 5a86553067..b74734c00b 100644 --- a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts @@ -20,17 +20,16 @@ import { Component, ContentChild, EventEmitter, - Inject, input, Input, OnChanges, Output, SimpleChanges, ViewChild, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { - AppConfigService, ColumnsSelectorComponent, CustomEmptyContentTemplateDirective, CustomLoadingContentTemplateDirective, @@ -95,6 +94,11 @@ export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataColumnCustomData> implements OnChanges, AfterContentInit, PaginatedComponent { + private readonly processListCloudService = inject(ProcessListCloudService); + private readonly userPreferences = inject(UserPreferencesService); + private readonly cloudPreferenceService = inject<PreferenceCloudServiceInterface>(PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN); + private readonly variableMapperService = inject(VariableMapperService); + @ViewChild(DataTableComponent) dataTable: DataTableComponent; @ContentChild(CustomEmptyContentTemplateDirective) @@ -354,7 +358,7 @@ export class ProcessListCloudComponent processListRequestNode: ProcessListRequestModel; dataAdapter: ProcessListDatatableAdapter; - private defaultSorting = { key: 'startDate', direction: 'desc' }; + private readonly defaultSorting = { key: 'startDate', direction: 'desc' }; protected isLoadingPreferences$ = new BehaviorSubject<boolean>(true); private readonly isReloadingSubject$ = new BehaviorSubject<boolean>(false); @@ -363,20 +367,19 @@ export class ProcessListCloudComponent map(([isLoadingPreferences, isReloading]) => isLoadingPreferences || isReloading) ); - private fetchProcessesTrigger$ = new Subject<void>(); + private readonly fetchProcessesTrigger$ = new Subject<void>(); + + constructor() { + super(PRESET_KEY, processCloudPresetsDefaultModel); + const userPreferences = this.userPreferences; - constructor( - private processListCloudService: ProcessListCloudService, - appConfigService: AppConfigService, - private userPreferences: UserPreferencesService, - @Inject(PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN) private cloudPreferenceService: PreferenceCloudServiceInterface, - private variableMapperService: VariableMapperService - ) { - super(appConfigService, PRESET_KEY, processCloudPresetsDefaultModel); this.size = userPreferences.paginationSize; - this.userPreferences.select(UserPreferenceValues.PaginationSize).subscribe((pageSize) => { - this.size = pageSize; - }); + this.userPreferences + .select(UserPreferenceValues.PaginationSize) + .pipe(takeUntilDestroyed()) + .subscribe((pageSize) => { + this.size = pageSize; + }); this.pagination = new BehaviorSubject<PaginationModel>({ maxItems: this.size, skipCount: 0, diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts index 09c4c619e2..10a36504fe 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts @@ -96,6 +96,8 @@ const PROCESS_DEFINITION_IDENTIFIER_REG_EXP = /%{processdefinition}/i; encapsulation: ViewEncapsulation.None }) export class StartProcessCloudComponent implements OnChanges, OnInit { + private readonly translateService = inject(TranslationService); + @ViewChild(MatAutocompleteTrigger) inputAutocomplete: MatAutocompleteTrigger; @@ -263,7 +265,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit { return this.translateService.instant('ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.CANCEL').toUpperCase(); } - constructor(private readonly translateService: TranslationService) { + constructor() { this.startProcessButtonLabel = this.defaultStartProcessButtonLabel; this.cancelButtonLabel = this.defaultCancelProcessButtonLabel; } diff --git a/lib/process-services-cloud/src/lib/screen/services/screen-rendering.service.ts b/lib/process-services-cloud/src/lib/screen/services/screen-rendering.service.ts index 57498d1ee8..cbf6bf56a2 100644 --- a/lib/process-services-cloud/src/lib/screen/services/screen-rendering.service.ts +++ b/lib/process-services-cloud/src/lib/screen/services/screen-rendering.service.ts @@ -49,7 +49,7 @@ import { APP_CUSTOM_SCREEN_TOKEN, CustomScreen } from './provide-screen'; providedIn: 'root' }) export class ScreenRenderingService extends DynamicComponentMapper { - private customScreens = inject<CustomScreen[]>(APP_CUSTOM_SCREEN_TOKEN, { optional: true }) || []; + private readonly customScreens = inject<CustomScreen[]>(APP_CUSTOM_SCREEN_TOKEN, { optional: true }) || []; constructor() { super(); diff --git a/lib/process-services-cloud/src/lib/services/base-cloud.service.ts b/lib/process-services-cloud/src/lib/services/base-cloud.service.ts index f50d411391..ca0e6662d7 100644 --- a/lib/process-services-cloud/src/lib/services/base-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/services/base-cloud.service.ts @@ -24,6 +24,8 @@ import { RequestOptions } from '@alfresco/js-api'; @Injectable() export class BaseCloudService { + protected adfHttpClient = inject(AdfHttpClient); + protected apiService = inject(AlfrescoApiService); protected appConfigService = inject(AppConfigService); @@ -34,8 +36,6 @@ export class BaseCloudService { accepts: ['application/json'] }; - constructor(protected adfHttpClient: AdfHttpClient) {} - getBasePath(appName: string): string { return appName ? `${this.contextRoot}/${appName}` : this.contextRoot; } diff --git a/lib/process-services-cloud/src/lib/services/local-preference-cloud.service.ts b/lib/process-services-cloud/src/lib/services/local-preference-cloud.service.ts index 81db87c4b5..b367ac5165 100644 --- a/lib/process-services-cloud/src/lib/services/local-preference-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/services/local-preference-cloud.service.ts @@ -15,14 +15,14 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { PreferenceCloudServiceInterface } from './preference-cloud.interface'; import { StorageService } from '@alfresco/adf-core'; import { Observable, of } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class LocalPreferenceCloudService implements PreferenceCloudServiceInterface { - constructor(private storage: StorageService) {} + private readonly storage = inject(StorageService); /** * Gets local preferences diff --git a/lib/process-services-cloud/src/lib/services/notification-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/services/notification-cloud.service.spec.ts index 8327037fa7..75cf171410 100644 --- a/lib/process-services-cloud/src/lib/services/notification-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/services/notification-cloud.service.spec.ts @@ -19,7 +19,8 @@ import { TestBed } from '@angular/core/testing'; import { NotificationCloudService } from './notification-cloud.service'; import { WebSocketService } from './web-socket.service'; import { Apollo } from 'apollo-angular'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { AuthenticationService } from '@alfresco/adf-core'; import { of, Subject } from 'rxjs'; @@ -42,7 +43,6 @@ describe('NotificationCloudService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], providers: [ WebSocketService, { @@ -55,7 +55,9 @@ describe('NotificationCloudService', () => { getToken: () => 'testToken', onLogout: onLogoutSubject.asObservable() } - } + }, + provideHttpClient(), + provideHttpClientTesting() ] }); service = TestBed.inject(NotificationCloudService); diff --git a/lib/process-services-cloud/src/lib/services/notification-cloud.service.ts b/lib/process-services-cloud/src/lib/services/notification-cloud.service.ts index 6794848744..ad870d85ec 100644 --- a/lib/process-services-cloud/src/lib/services/notification-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/services/notification-cloud.service.ts @@ -16,13 +16,13 @@ */ import { gql } from '@apollo/client/core'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { WebSocketService } from './web-socket.service'; @Injectable({ providedIn: 'root' }) export class NotificationCloudService { - constructor(private readonly webSocketService: WebSocketService) {} + private readonly webSocketService = inject(WebSocketService); makeGQLQuery(appName: string, gqlQuery: string) { return this.webSocketService.getSubscription({ diff --git a/lib/process-services-cloud/src/lib/services/web-socket.service.ts b/lib/process-services-cloud/src/lib/services/web-socket.service.ts index f7c01282de..32039566c9 100644 --- a/lib/process-services-cloud/src/lib/services/web-socket.service.ts +++ b/lib/process-services-cloud/src/lib/services/web-socket.service.ts @@ -51,17 +51,15 @@ interface serviceOptions { providedIn: 'root' }) export class WebSocketService { - private appConfigService = inject(AppConfigService); - private subscriptionProtocol = 'graphql-ws'; + private readonly apollo = inject(Apollo); + private readonly httpLink = inject(HttpLink); + private readonly authService = inject(AuthenticationService); + + private readonly appConfigService = inject(AppConfigService); + private readonly subscriptionProtocol: 'graphql-ws' | 'transport-ws' = 'graphql-ws'; private wsLink: GraphQLWsLink | WebSocketLink; private httpLinkHandler: HttpLinkHandler; - constructor( - private readonly apollo: Apollo, - private readonly httpLink: HttpLink, - private readonly authService: AuthenticationService - ) {} - public getSubscription<T>(options: serviceOptions): Observable<FetchResult<T>> { const { apolloClientName, subscriptionOptions } = options; this.authService.onLogout.pipe(take(1)).subscribe(() => { diff --git a/lib/process-services-cloud/src/lib/task/mock/task-cloud.service.mock.ts b/lib/process-services-cloud/src/lib/task/mock/task-cloud.service.mock.ts index 029b1d73c5..a367b3e80c 100644 --- a/lib/process-services-cloud/src/lib/task/mock/task-cloud.service.mock.ts +++ b/lib/process-services-cloud/src/lib/task/mock/task-cloud.service.mock.ts @@ -20,17 +20,12 @@ import { Observable, of, Subject } from 'rxjs'; import { TaskDetailsCloudModel, TASK_ASSIGNED_STATE, TASK_CREATED_STATE } from '../models/task-details-cloud.model'; import { taskDetailsContainer } from '../task-header/mocks/task-details-cloud.mock'; import { TaskCloudService } from '../services/task-cloud.service'; -import { AdfHttpClient } from '@alfresco/adf-core/api'; @Injectable() export class TaskCloudServiceMock extends TaskCloudService { currentUserMock = 'AssignedTaskUser'; dataChangesDetected$ = new Subject(); - constructor(adfHttpClient: AdfHttpClient) { - super(adfHttpClient); - } - getTaskById(_appName: string, taskId: string): Observable<TaskDetailsCloudModel> { return of(taskDetailsContainer[taskId]); } diff --git a/lib/process-services-cloud/src/lib/task/services/task-cloud.service.ts b/lib/process-services-cloud/src/lib/task/services/task-cloud.service.ts index 767928e372..3eac1b86c8 100644 --- a/lib/process-services-cloud/src/lib/task/services/task-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/task/services/task-cloud.service.ts @@ -32,21 +32,16 @@ import { StartTaskCloudRequestModel } from '../models/start-task-cloud-request.m import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.model'; import { DEFAULT_TASK_PRIORITIES, TaskPriorityOption } from '../models/task.model'; import { IdentityUserService } from '../../people/services/identity-user.service'; -import { AdfHttpClient } from '@alfresco/adf-core/api'; @Injectable({ providedIn: 'root' }) export class TaskCloudService extends BaseCloudService { - private translateService = inject(TranslationService); - private identityUserService = inject(IdentityUserService); + private readonly translateService = inject(TranslationService); + private readonly identityUserService = inject(IdentityUserService); dataChangesDetected$ = new Subject(); - constructor(adfHttpClient: AdfHttpClient) { - super(adfHttpClient); - } - /** * Complete a task. * diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter/edit-service-task-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter/edit-service-task-filter-cloud.component.ts index 5c56353b60..3b0440a987 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter/edit-service-task-filter-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter/edit-service-task-filter-cloud.component.ts @@ -61,7 +61,7 @@ import { TaskAssignmentFilterCloudComponent } from '../../task-assignment-filter encapsulation: ViewEncapsulation.None }) export class EditServiceTaskFilterCloudComponent extends BaseEditTaskFilterCloudComponent<ServiceTaskFilterCloudModel> { - private serviceTaskFilterCloudService = inject(ServiceTaskFilterCloudService); + private readonly serviceTaskFilterCloudService = inject(ServiceTaskFilterCloudService); constructor() { super(); diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter/edit-task-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter/edit-task-filter-cloud.component.ts index ec3b359a29..455866e939 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter/edit-task-filter-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter/edit-task-filter-cloud.component.ts @@ -64,7 +64,7 @@ import { TaskAssignmentFilterCloudComponent } from '../../task-assignment-filter encapsulation: ViewEncapsulation.None }) export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudComponent<TaskFilterCloudModel> { - private taskFilterCloudService = inject(TaskFilterCloudService); + private readonly taskFilterCloudService = inject(TaskFilterCloudService); constructor() { super(); diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-assignment-filter/task-assignment-filter.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/task-assignment-filter/task-assignment-filter.component.ts index e8f33c1de4..b76949d7b0 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/task-assignment-filter/task-assignment-filter.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/task-assignment-filter/task-assignment-filter.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges } from '@angular/core'; +import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, inject } from '@angular/core'; import { MatSelectChange, MatSelectModule } from '@angular/material/select'; import { AssignmentType, TaskFilterProperties, TaskStatusFilter } from '../../models/filter-cloud.model'; import { IdentityUserModel } from '../../../../people/models/identity-user.model'; @@ -35,6 +35,8 @@ import { PeopleCloudComponent } from '../../../../people/components/people-cloud styleUrls: ['./task-assignment-filter.component.scss'] }) export class TaskAssignmentFilterCloudComponent implements OnInit, OnChanges { + private readonly identityUserService = inject(IdentityUserService); + @Input() appName: string; @Input() taskFilterProperty: TaskFilterProperties; @@ -53,8 +55,6 @@ export class TaskAssignmentFilterCloudComponent implements OnInit, OnChanges { groupForm = new FormControl(''); assignmentTypeOptions: DropdownOption[]; - constructor(private identityUserService: IdentityUserService) {} - ngOnChanges(changes: SimpleChanges): void { if (changes?.status?.currentValue !== changes?.status?.previousValue) { this.changeAssignmentTypeByStatus(changes?.status?.currentValue); diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.ts index 4ff3e8ccad..76ce03e4fd 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filter-dialog/task-filter-dialog-cloud.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { AbstractControl, ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { CommonModule } from '@angular/common'; @@ -32,18 +32,16 @@ import { MatButtonModule } from '@angular/material/button'; encapsulation: ViewEncapsulation.None }) export class TaskFilterDialogCloudComponent implements OnInit { + private readonly fb = inject(UntypedFormBuilder); + dialogRef = inject<MatDialogRef<TaskFilterDialogCloudComponent>>(MatDialogRef); + data = inject(MAT_DIALOG_DATA); + // eslint-disable-next-line @typescript-eslint/naming-convention public static ACTION_SAVE = 'SAVE'; defaultIcon = 'inbox'; filterForm: UntypedFormGroup; - constructor( - private fb: UntypedFormBuilder, - public dialogRef: MatDialogRef<TaskFilterDialogCloudComponent>, - @Inject(MAT_DIALOG_DATA) public data - ) {} - ngOnInit() { this.filterForm = this.fb.group({ name: [this.data.name, Validators.required] diff --git a/lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts b/lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts index d03bb19ce0..ab67edc240 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts @@ -78,7 +78,7 @@ export class TaskFilterCloudModel { private _dueDateTo: string; private _createdFrom: string; private _createdTo: string; - private dateRangeFilterService = new DateRangeFilterService(); + private readonly dateRangeFilterService = new DateRangeFilterService(); processVariableFilters?: ProcessVariableFilterModel[]; diff --git a/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.ts b/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.ts index a509927908..62e526ff00 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.ts @@ -26,7 +26,6 @@ import { TaskCloudNodePaging } from '../../../models/task-cloud.model'; import { NotificationCloudService } from '../../../services/notification-cloud.service'; import { TaskCloudEngineEvent } from '../../../models/engine-event-cloud.model'; import { IdentityUserService } from '../../../people/services/identity-user.service'; -import { AdfHttpClient } from '@alfresco/adf-core/api'; const TASK_EVENT_SUBSCRIPTION_QUERY = ` subscription { @@ -48,22 +47,17 @@ const TASK_EVENT_SUBSCRIPTION_QUERY = ` providedIn: 'root' }) export class TaskFilterCloudService extends BaseCloudService { + private readonly notificationCloudService = inject(NotificationCloudService); + public preferenceService = inject<PreferenceCloudServiceInterface>(TASK_FILTERS_SERVICE_TOKEN); protected identityUserService = inject(IdentityUserService); - private filtersSubject = new BehaviorSubject<TaskFilterCloudModel[]>([]); + private readonly filtersSubject = new BehaviorSubject<TaskFilterCloudModel[]>([]); filters$ = this.filtersSubject.asObservable(); - private filterKeyToBeRefreshedSource = new Subject<string>(); + private readonly filterKeyToBeRefreshedSource = new Subject<string>(); filterKeyToBeRefreshed$ = this.filterKeyToBeRefreshedSource.asObservable(); - constructor( - private notificationCloudService: NotificationCloudService, - adfHttpClient: AdfHttpClient - ) { - super(adfHttpClient); - } - /** * Creates and returns the default task filters for an app. * diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts index 5e453db1ce..4a478690bd 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts @@ -16,7 +16,7 @@ */ import { ContentLinkModel, FormModel, FormOutcomeEvent, FormRenderingService } from '@alfresco/adf-core'; -import { Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { FormCloudComponent } from '../../../../form/components/form-cloud.component'; import { AttachFileCloudWidgetComponent } from '../../../../form/components/widgets/attach-file/attach-file-cloud-widget.component'; import { DateCloudWidgetComponent } from '../../../../form/components/widgets/date/date-cloud.widget'; @@ -36,6 +36,9 @@ import { FormCustomOutcomesComponent } from '../../../../form/components/form-cl encapsulation: ViewEncapsulation.None }) export class TaskFormCloudComponent { + private readonly taskCloudService = inject(TaskCloudService); + private readonly formRenderingService = inject(FormRenderingService); + /** App id to fetch corresponding form and values. */ @Input() appName: string = ''; @@ -174,10 +177,7 @@ export class TaskFormCloudComponent { loading: boolean = false; - constructor( - private taskCloudService: TaskCloudService, - private formRenderingService: FormRenderingService - ) { + constructor() { this.formRenderingService.setComponentTypeResolver('upload', () => AttachFileCloudWidgetComponent, true); this.formRenderingService.setComponentTypeResolver('dropdown', () => DropdownCloudWidgetComponent, true); this.formRenderingService.setComponentTypeResolver('date', () => DateCloudWidgetComponent, true); diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/claim-task/claim-task-cloud.directive.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/claim-task/claim-task-cloud.directive.ts index 5c16642d01..8284ece984 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/claim-task/claim-task-cloud.directive.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/claim-task/claim-task-cloud.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, Input, HostListener, Output, EventEmitter, OnInit, ElementRef, Renderer2 } from '@angular/core'; +import { Directive, Input, HostListener, Output, EventEmitter, OnInit, ElementRef, Renderer2, inject } from '@angular/core'; import { IdentityUserService } from '../../../../../people/services/identity-user.service'; import { TaskCloudService } from '../../../../services/task-cloud.service'; import { firstValueFrom } from 'rxjs'; @@ -25,6 +25,11 @@ import { firstValueFrom } from 'rxjs'; selector: '[adf-cloud-claim-task]' }) export class ClaimTaskCloudDirective implements OnInit { + private readonly el = inject(ElementRef); + private readonly renderer = inject(Renderer2); + private readonly taskListService = inject(TaskCloudService); + private readonly identityUserService = inject(IdentityUserService); + /** (Required) The id of the task. */ @Input() taskId: string; @@ -43,13 +48,6 @@ export class ClaimTaskCloudDirective implements OnInit { invalidParams: string[] = []; - constructor( - private readonly el: ElementRef, - private readonly renderer: Renderer2, - private taskListService: TaskCloudService, - private identityUserService: IdentityUserService - ) {} - ngOnInit() { this.validateInputs(); } diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/unclaim-task/unclaim-task-cloud.directive.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/unclaim-task/unclaim-task-cloud.directive.ts index 23d1aa4957..0290f0fc9b 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/unclaim-task/unclaim-task-cloud.directive.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/unclaim-task/unclaim-task-cloud.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, Input, HostListener, Output, EventEmitter, OnInit, ElementRef, Renderer2 } from '@angular/core'; +import { Directive, Input, HostListener, Output, EventEmitter, OnInit, ElementRef, Renderer2, inject } from '@angular/core'; import { TaskCloudService } from '../../../../services/task-cloud.service'; import { firstValueFrom } from 'rxjs'; @@ -24,6 +24,10 @@ import { firstValueFrom } from 'rxjs'; selector: '[adf-cloud-unclaim-task]' }) export class UnClaimTaskCloudDirective implements OnInit { + private readonly el = inject(ElementRef); + private readonly renderer = inject(Renderer2); + private readonly taskListService = inject(TaskCloudService); + /** (Required) The id of the task. */ @Input() taskId: string; @@ -42,12 +46,6 @@ export class UnClaimTaskCloudDirective implements OnInit { invalidParams: string[] = []; - constructor( - private readonly el: ElementRef, - private readonly renderer: Renderer2, - private taskListService: TaskCloudService - ) {} - ngOnInit() { this.validateInputs(); } diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/complete-task/complete-task.directive.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/complete-task/complete-task.directive.ts index cb558b9272..10422648a9 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/complete-task/complete-task.directive.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/complete-task/complete-task.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, Input, HostListener, Output, EventEmitter, OnInit, ElementRef, Renderer2 } from '@angular/core'; +import { Directive, Input, HostListener, Output, EventEmitter, OnInit, ElementRef, Renderer2, inject } from '@angular/core'; import { TaskCloudService } from '../../../../services/task-cloud.service'; import { firstValueFrom } from 'rxjs'; @@ -24,6 +24,10 @@ import { firstValueFrom } from 'rxjs'; selector: '[adf-cloud-complete-task]' }) export class CompleteTaskDirective implements OnInit { + private readonly el = inject(ElementRef); + private readonly renderer = inject(Renderer2); + private readonly taskListService = inject(TaskCloudService); + /** (Required) The id of the task. */ @Input() taskId: string; @@ -42,12 +46,6 @@ export class CompleteTaskDirective implements OnInit { invalidParams: string[] = []; - constructor( - private readonly el: ElementRef, - private readonly renderer: Renderer2, - private readonly taskListService: TaskCloudService - ) {} - ngOnInit() { this.validateInputs(); } diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts index 648d1dcd53..7dec2eebd0 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts @@ -188,7 +188,7 @@ export class UserTaskCloudComponent implements OnInit, OnChanges { taskTypeEnum = UserTaskContentType; screenId: string; - private taskCloudService: TaskCloudService = inject(TaskCloudService); + private readonly taskCloudService: TaskCloudService = inject(TaskCloudService); private readonly taskTypeResolverService = inject(TaskTypeResolverService); private readonly destroyRef = inject(DestroyRef); diff --git a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.ts index 46e9c6c55b..1f8314760e 100644 --- a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.ts @@ -48,6 +48,11 @@ import { MatCardModule } from '@angular/material/card'; encapsulation: ViewEncapsulation.None }) export class TaskHeaderCloudComponent implements OnInit, OnChanges { + private readonly taskCloudService = inject(TaskCloudService); + private readonly translationService = inject(TranslationService); + private readonly appConfig = inject(AppConfigService); + private readonly cardViewUpdateService = inject(CardViewUpdateService); + /** (Required) The name of the application. */ @Input({ required: true }) appName: string = ''; @@ -86,12 +91,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges { private readonly destroyRef = inject(DestroyRef); - constructor( - private taskCloudService: TaskCloudService, - private translationService: TranslationService, - private appConfig: AppConfigService, - private cardViewUpdateService: CardViewUpdateService - ) { + constructor() { this.dateFormat = this.appConfig.get('adf-cloud-task-header.defaultDateFormat'); this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale'); } diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/base-task-list-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-list/components/base-task-list-cloud.component.ts index 5d438f48aa..b92044d565 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/components/base-task-list-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/components/base-task-list-cloud.component.ts @@ -194,16 +194,16 @@ export abstract class BaseTaskListCloudComponent<T = unknown> protected isLoadingPreferences$ = new BehaviorSubject<boolean>(true); protected readonly destroyRef = inject(DestroyRef); + protected readonly appConfigService = inject(AppConfigService); + protected readonly taskCloudService = inject(TaskCloudService); + protected readonly userPreferences = inject(UserPreferencesService); + private readonly cloudPreferenceService: PreferenceCloudServiceInterface; - constructor( - appConfigService: AppConfigService, - private taskCloudService: TaskCloudService, - private userPreferences: UserPreferencesService, - presetKey: string, - private cloudPreferenceService: PreferenceCloudServiceInterface - ) { - super(appConfigService, presetKey, taskPresetsCloudDefaultModel); - this.size = userPreferences.paginationSize; + // eslint-disable-next-line @angular-eslint/prefer-inject + constructor(presetKey: string, cloudPreferenceService: PreferenceCloudServiceInterface) { + super(presetKey, taskPresetsCloudDefaultModel); + this.cloudPreferenceService = cloudPreferenceService; + this.size = this.userPreferences.paginationSize; this.pagination = new BehaviorSubject<PaginationModel>({ maxItems: this.size, diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list/service-task-list-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list/service-task-list-cloud.component.ts index 3e6530993b..29f5dba03b 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list/service-task-list-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list/service-task-list-cloud.component.ts @@ -15,21 +15,18 @@ * limitations under the License. */ -import { Component, Inject, Input, ViewEncapsulation } from '@angular/core'; +import { Component, Input, ViewEncapsulation, inject } from '@angular/core'; import { - AppConfigService, ColumnsSelectorComponent, DataTableComponent, EmptyContentComponent, LoadingContentTemplateDirective, MainMenuDataTableTemplateDirective, - NoContentTemplateDirective, - UserPreferencesService + NoContentTemplateDirective } from '@alfresco/adf-core'; import { ServiceTaskQueryCloudRequestModel } from '../../models/service-task-cloud.model'; import { BaseTaskListCloudComponent } from '../base-task-list-cloud.component'; import { ServiceTaskListCloudService } from '../../services/service-task-list-cloud.service'; -import { TaskCloudService } from '../../../services/task-cloud.service'; import { BehaviorSubject, combineLatest } from 'rxjs'; import { PreferenceCloudServiceInterface, TASK_LIST_PREFERENCES_SERVICE_TOKEN } from '../../../../services/public-api'; import { map } from 'rxjs/operators'; @@ -58,22 +55,20 @@ const PRESET_KEY = 'adf-cloud-service-task-list.presets'; encapsulation: ViewEncapsulation.None }) export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent { + private readonly serviceTaskListCloudService = inject(ServiceTaskListCloudService); + @Input() queryParams: { [key: string]: any } = {}; - private isReloadingSubject$ = new BehaviorSubject<boolean>(false); + private readonly isReloadingSubject$ = new BehaviorSubject<boolean>(false); isLoading$ = combineLatest([this.isLoadingPreferences$, this.isReloadingSubject$]).pipe( map(([isLoadingPreferences, isReloading]) => isLoadingPreferences || isReloading) ); - constructor( - private serviceTaskListCloudService: ServiceTaskListCloudService, - appConfigService: AppConfigService, - taskCloudService: TaskCloudService, - userPreferences: UserPreferencesService, - @Inject(TASK_LIST_PREFERENCES_SERVICE_TOKEN) cloudPreferenceService: PreferenceCloudServiceInterface - ) { - super(appConfigService, taskCloudService, userPreferences, PRESET_KEY, cloudPreferenceService); + constructor() { + const cloudPreferenceService = inject<PreferenceCloudServiceInterface>(TASK_LIST_PREFERENCES_SERVICE_TOKEN); + + super(PRESET_KEY, cloudPreferenceService); } reload() { diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/task-list/task-list-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-list/components/task-list/task-list-cloud.component.ts index b9b4ecf538..302634ad69 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/components/task-list/task-list-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/components/task-list/task-list-cloud.component.ts @@ -15,22 +15,19 @@ * limitations under the License. */ -import { Component, Inject, Input, ViewEncapsulation } from '@angular/core'; +import { Component, Input, ViewEncapsulation, inject } from '@angular/core'; import { AdfDateFnsAdapter, - AppConfigService, ColumnsSelectorComponent, DataTableComponent, EmptyContentComponent, LoadingContentTemplateDirective, MainMenuDataTableTemplateDirective, MOMENT_DATE_FORMATS, - NoContentTemplateDirective, - UserPreferencesService + NoContentTemplateDirective } from '@alfresco/adf-core'; import { TaskListRequestModel, TaskQueryCloudRequestModel } from '../../../../models/filter-cloud-model'; import { BaseTaskListCloudComponent } from '../base-task-list-cloud.component'; -import { TaskCloudService } from '../../../services/task-cloud.service'; import { TASK_LIST_CLOUD_TOKEN, TASK_LIST_PREFERENCES_SERVICE_TOKEN } from '../../../../services/cloud-token.service'; import { PreferenceCloudServiceInterface } from '../../../../services/preference-cloud.interface'; import { TaskListCloudServiceInterface } from '../../../../services/task-list-cloud.service.interface'; @@ -74,6 +71,9 @@ const PRESET_KEY = 'adf-cloud-task-list.presets'; encapsulation: ViewEncapsulation.None }) export class TaskListCloudComponent extends BaseTaskListCloudComponent<ProcessListDataColumnCustomData> { + taskListCloudService = inject<TaskListCloudServiceInterface>(TASK_LIST_CLOUD_TOKEN); + private readonly viewModelCreator = inject(VariableMapperService); + /** * The assignee of the process. Possible values are: "assignee" (the current user is the assignee), * "candidate" (the current user is a task candidate", "group_x" (the task is assigned to a group @@ -262,22 +262,16 @@ export class TaskListCloudComponent extends BaseTaskListCloudComponent<ProcessLi rows: TaskInstanceCloudListViewModel[] = []; declare dataAdapter: TasksListDatatableAdapter | undefined; - private isReloadingSubject$ = new BehaviorSubject<boolean>(false); + private readonly isReloadingSubject$ = new BehaviorSubject<boolean>(false); isLoading$ = combineLatest([this.isLoadingPreferences$, this.isReloadingSubject$]).pipe( map(([isLoadingPreferences, isReloading]) => isLoadingPreferences || isReloading) ); - private fetchProcessesTrigger$ = new Subject<void>(); + private readonly fetchProcessesTrigger$ = new Subject<void>(); - constructor( - @Inject(TASK_LIST_CLOUD_TOKEN) public taskListCloudService: TaskListCloudServiceInterface, - appConfigService: AppConfigService, - taskCloudService: TaskCloudService, - userPreferences: UserPreferencesService, - @Inject(TASK_LIST_PREFERENCES_SERVICE_TOKEN) cloudPreferenceService: PreferenceCloudServiceInterface, - private viewModelCreator: VariableMapperService - ) { - super(appConfigService, taskCloudService, userPreferences, PRESET_KEY, cloudPreferenceService); + constructor() { + const cloudPreferenceService = inject<PreferenceCloudServiceInterface>(TASK_LIST_PREFERENCES_SERVICE_TOKEN); + super(PRESET_KEY, cloudPreferenceService); combineLatest([this.isLoadingPreferences$, this.isColumnSchemaCreated$, this.fetchProcessesTrigger$]) .pipe( diff --git a/lib/process-services/src/lib/app-list/apps-list.component.ts b/lib/process-services/src/lib/app-list/apps-list.component.ts index 0566b4622c..5d66e1b0b9 100644 --- a/lib/process-services/src/lib/app-list/apps-list.component.ts +++ b/lib/process-services/src/lib/app-list/apps-list.component.ts @@ -17,7 +17,7 @@ import { CustomEmptyContentTemplateDirective, EmptyContentComponent } from '@alfresco/adf-core'; import { AppsProcessService } from '../services/apps-process.service'; -import { AfterContentInit, Component, ContentChild, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { AfterContentInit, Component, ContentChild, EventEmitter, Input, OnInit, Output, ViewEncapsulation, inject } from '@angular/core'; import { IconModel } from './icon.model'; import { finalize, map } from 'rxjs/operators'; import { AppDefinitionRepresentation } from '@alfresco/js-api'; @@ -54,6 +54,8 @@ export const APP_LIST_LAYOUT_GRID: string = 'GRID'; host: { class: 'adf-apps' } }) export class AppsListComponent implements OnInit, AfterContentInit { + private readonly appsProcessService = inject(AppsProcessService); + @ContentChild(CustomEmptyContentTemplateDirective) emptyCustomContent: CustomEmptyContentTemplateDirective; @@ -86,7 +88,6 @@ export class AppsListComponent implements OnInit, AfterContentInit { hasEmptyCustomContentTemplate: boolean = false; private iconsMDL: IconModel; - constructor(private appsProcessService: AppsProcessService) {} ngOnInit() { if (!this.isValidType()) { diff --git a/lib/process-services/src/lib/app-list/icon.model.ts b/lib/process-services/src/lib/app-list/icon.model.ts index b88f8e2550..b0a78743ec 100644 --- a/lib/process-services/src/lib/app-list/icon.model.ts +++ b/lib/process-services/src/lib/app-list/icon.model.ts @@ -20,9 +20,9 @@ const DEFAULT_TASKS_APP_MATERIAL_ICON: string = 'favorite_border'; /* spellchecker: disable */ export class IconModel { private iconsMDL: Map<string, string>; - private supportedIconTypes = ['glyphicon', 'fa' /*Font awesome*/]; + private readonly supportedIconTypes = ['glyphicon', 'fa' /*Font awesome*/]; - private sharedIconsMap = [ + private readonly sharedIconsMap = [ ['asterisk', 'ac_unit'], ['plus', 'add'], ['euro', 'euro_symbol'], @@ -164,7 +164,7 @@ export class IconModel { initIconsMDL() { this.iconsMDL = new Map<string, string>(); - this.supportedIconTypes.forEach(iconType => { + this.supportedIconTypes.forEach((iconType) => { this.sharedIconsMap.forEach(([iconKey, iconValue]) => { this.iconsMDL.set(`${iconType}-${iconKey}`, iconValue); }); diff --git a/lib/process-services/src/lib/app-list/select-apps-dialog/select-apps-dialog.component.spec.ts b/lib/process-services/src/lib/app-list/select-apps-dialog/select-apps-dialog.component.spec.ts index 2a0c765594..3af65cb1eb 100644 --- a/lib/process-services/src/lib/app-list/select-apps-dialog/select-apps-dialog.component.spec.ts +++ b/lib/process-services/src/lib/app-list/select-apps-dialog/select-apps-dialog.component.spec.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component } from '@angular/core'; +import { Component, inject as inject_1 } from '@angular/core'; import { TestBed, ComponentFixture } from '@angular/core/testing'; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { OverlayContainer } from '@angular/cdk/overlay'; @@ -29,11 +29,11 @@ import { SelectAppsDialogComponent } from './select-apps-dialog.component'; template: '' }) export class DialogSelectAppTestComponent { + private readonly dialog = inject_1(MatDialog); + processId: any; dialogRef: any; - constructor(private dialog: MatDialog) {} - startProcessAction() { this.dialogRef = this.dialog.open(SelectAppsDialogComponent, { width: '630px' diff --git a/lib/process-services/src/lib/app-list/select-apps-dialog/select-apps-dialog.component.ts b/lib/process-services/src/lib/app-list/select-apps-dialog/select-apps-dialog.component.ts index fe8fbb4fdd..0c8a3e4cfe 100644 --- a/lib/process-services/src/lib/app-list/select-apps-dialog/select-apps-dialog.component.ts +++ b/lib/process-services/src/lib/app-list/select-apps-dialog/select-apps-dialog.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Inject, ViewEncapsulation } from '@angular/core'; +import { Component, ViewEncapsulation, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { AppsProcessService } from '../../services/apps-process.service'; import { CommonModule } from '@angular/common'; @@ -31,15 +31,15 @@ import { MatButtonModule } from '@angular/material/button'; encapsulation: ViewEncapsulation.None }) export class SelectAppsDialogComponent { + private readonly appsProcessService = inject(AppsProcessService); + dialogRef = inject<MatDialogRef<SelectAppsDialogComponent>>(MatDialogRef); + data = inject(MAT_DIALOG_DATA); + processApps: any; selectedProcess: any; - constructor( - private appsProcessService: AppsProcessService, - public dialogRef: MatDialogRef<SelectAppsDialogComponent>, - @Inject(MAT_DIALOG_DATA) public data: any - ) { + constructor() { this.appsProcessService.getDeployedApplications().subscribe((apps) => { this.processApps = apps.filter((currentApp) => currentApp.id); }); diff --git a/lib/process-services/src/lib/attachment/create-process-attachment/create-process-attachment.component.ts b/lib/process-services/src/lib/attachment/create-process-attachment/create-process-attachment.component.ts index 11f8a7e479..2f4ef583f4 100644 --- a/lib/process-services/src/lib/attachment/create-process-attachment/create-process-attachment.component.ts +++ b/lib/process-services/src/lib/attachment/create-process-attachment/create-process-attachment.component.ts @@ -30,7 +30,7 @@ import { MatIconModule } from '@angular/material/icon'; templateUrl: './create-process-attachment.component.html' }) export class CreateProcessAttachmentComponent implements OnChanges { - private processContentService = inject(ProcessContentService); + private readonly processContentService = inject(ProcessContentService); /** (required) The ID of the process instance to display. */ @Input({ required: true }) diff --git a/lib/process-services/src/lib/attachment/create-task-attachment/create-task-attachment.component.ts b/lib/process-services/src/lib/attachment/create-task-attachment/create-task-attachment.component.ts index c91ba0592f..b7d899db41 100644 --- a/lib/process-services/src/lib/attachment/create-task-attachment/create-task-attachment.component.ts +++ b/lib/process-services/src/lib/attachment/create-task-attachment/create-task-attachment.component.ts @@ -30,7 +30,7 @@ import { MatIconModule } from '@angular/material/icon'; templateUrl: './create-task-attachment.component.html' }) export class AttachmentComponent implements OnChanges { - private processContentService = inject(ProcessContentService); + private readonly processContentService = inject(ProcessContentService); /** (required) The numeric ID of the task to display. */ @Input({ required: true }) diff --git a/lib/process-services/src/lib/attachment/process-attachment-list/process-attachment-list.component.ts b/lib/process-services/src/lib/attachment/process-attachment-list/process-attachment-list.component.ts index ae4dd131cd..4d03b1cf0f 100644 --- a/lib/process-services/src/lib/attachment/process-attachment-list/process-attachment-list.component.ts +++ b/lib/process-services/src/lib/attachment/process-attachment-list/process-attachment-list.component.ts @@ -35,7 +35,8 @@ import { OnChanges, Output, SimpleChanges, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { ProcessContentService } from '../../form/services/process-content.service'; import { CommonModule } from '@angular/common'; @@ -60,6 +61,11 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; encapsulation: ViewEncapsulation.None }) export class ProcessAttachmentListComponent implements OnChanges, AfterContentInit { + private readonly processContentService = inject(ProcessContentService); + private readonly downloadService = inject(DownloadService); + private readonly thumbnailService = inject(ThumbnailService); + private readonly ngZone = inject(NgZone); + @ContentChild(EmptyListComponent) emptyTemplate: EmptyListComponent; @@ -99,13 +105,6 @@ export class ProcessAttachmentListComponent implements OnChanges, AfterContentIn attachments: any[] = []; isLoading: boolean = false; - constructor( - private processContentService: ProcessContentService, - private downloadService: DownloadService, - private thumbnailService: ThumbnailService, - private ngZone: NgZone - ) {} - ngOnChanges(changes: SimpleChanges) { if (changes['processInstanceId']?.currentValue) { this.loadAttachmentsByProcessInstanceId(changes['processInstanceId'].currentValue); diff --git a/lib/process-services/src/lib/attachment/task-attachment-list/task-attachment-list.component.ts b/lib/process-services/src/lib/attachment/task-attachment-list/task-attachment-list.component.ts index 8dfe29c5cb..cb9fcf0446 100644 --- a/lib/process-services/src/lib/attachment/task-attachment-list/task-attachment-list.component.ts +++ b/lib/process-services/src/lib/attachment/task-attachment-list/task-attachment-list.component.ts @@ -36,7 +36,8 @@ import { OnChanges, Output, SimpleChanges, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { ProcessContentService } from '../../form/services/process-content.service'; import { CommonModule } from '@angular/common'; @@ -62,6 +63,11 @@ import { TranslatePipe } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None }) export class TaskAttachmentListComponent implements OnChanges, AfterContentInit { + private readonly activitiContentService = inject(ProcessContentService); + private readonly downloadService = inject(DownloadService); + private readonly thumbnailService = inject(ThumbnailService); + private readonly ngZone = inject(NgZone); + @ContentChild(EmptyListComponent) emptyTemplate: EmptyListComponent; @@ -97,13 +103,6 @@ export class TaskAttachmentListComponent implements OnChanges, AfterContentInit attachments: any[] = []; isLoading: boolean = false; - constructor( - private activitiContentService: ProcessContentService, - private downloadService: DownloadService, - private thumbnailService: ThumbnailService, - private ngZone: NgZone - ) {} - ngOnChanges(changes: SimpleChanges) { if (changes['taskId']?.currentValue) { this.loadAttachmentsByTaskId(changes['taskId'].currentValue); diff --git a/lib/process-services/src/lib/form/form-list/form-list.component.ts b/lib/process-services/src/lib/form/form-list/form-list.component.ts index 40de7b7040..6c76ace923 100644 --- a/lib/process-services/src/lib/form/form-list/form-list.component.ts +++ b/lib/process-services/src/lib/form/form-list/form-list.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Input, OnChanges, ViewEncapsulation } from '@angular/core'; +import { Component, Input, OnChanges, ViewEncapsulation, inject } from '@angular/core'; import { ModelService } from '../services/model.service'; import { CommonModule } from '@angular/common'; import { DataColumnComponent, DataColumnListComponent, DataTableComponent } from '@alfresco/adf-core'; @@ -27,12 +27,12 @@ import { DataColumnComponent, DataColumnListComponent, DataTableComponent } from encapsulation: ViewEncapsulation.None }) export class FormListComponent implements OnChanges { + protected modelService = inject(ModelService); + /** The array that contains the information to show inside the list. */ @Input() forms: any[] = []; - constructor(protected modelService: ModelService) {} - ngOnChanges() { this.getForms(); } diff --git a/lib/process-services/src/lib/form/form.component.ts b/lib/process-services/src/lib/form/form.component.ts index eef4491bd1..9994b0e551 100644 --- a/lib/process-services/src/lib/form/form.component.ts +++ b/lib/process-services/src/lib/form/form.component.ts @@ -76,7 +76,7 @@ export class FormComponent extends FormBaseComponent implements OnInit, OnChange protected visibilityService = inject(WidgetVisibilityService); protected ecmModelService = inject(EcmModelService); protected nodeService = inject(NodesApiService); - private cdRef = inject(ChangeDetectorRef); + private readonly cdRef = inject(ChangeDetectorRef); /** Task id to fetch corresponding form and values. */ @Input() diff --git a/lib/process-services/src/lib/form/services/activiti-alfresco.service.ts b/lib/process-services/src/lib/form/services/activiti-alfresco.service.ts index 97c84f7707..b3730ddc8a 100644 --- a/lib/process-services/src/lib/form/services/activiti-alfresco.service.ts +++ b/lib/process-services/src/lib/form/services/activiti-alfresco.service.ts @@ -17,7 +17,7 @@ import { AlfrescoApiService, SitesService } from '@alfresco/adf-content-services'; import { ExternalContent } from '@alfresco/adf-core'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { IntegrationAlfrescoOnPremiseApi, Node, @@ -33,6 +33,9 @@ import { map, catchError } from 'rxjs/operators'; providedIn: 'root' }) export class ActivitiContentService { + private readonly apiService = inject(AlfrescoApiService); + private readonly sitesService = inject(SitesService); + static UNKNOWN_ERROR_MESSAGE: string = 'Unknown error'; static GENERIC_ERROR_MESSAGE: string = 'Server error'; @@ -49,8 +52,6 @@ export class ActivitiContentService { return this._contentApi; } - constructor(private apiService: AlfrescoApiService, private sitesService: SitesService) {} - /** * Returns a list of child nodes below the specified folder * @@ -128,8 +129,8 @@ export class ActivitiContentService { errMsg = error.message ? error.message : error.status - ? `${error.status} - ${error.statusText}` - : ActivitiContentService.GENERIC_ERROR_MESSAGE; + ? `${error.status} - ${error.statusText}` + : ActivitiContentService.GENERIC_ERROR_MESSAGE; } return throwError(errMsg); } diff --git a/lib/process-services/src/lib/form/services/ecm-model.service.ts b/lib/process-services/src/lib/form/services/ecm-model.service.ts index 0a54befd3e..0b219329e7 100644 --- a/lib/process-services/src/lib/form/services/ecm-model.service.ts +++ b/lib/process-services/src/lib/form/services/ecm-model.service.ts @@ -17,7 +17,7 @@ import { AlfrescoApiService } from '@alfresco/adf-content-services'; import { FormModel } from '@alfresco/adf-core'; -import { EventEmitter, Injectable } from '@angular/core'; +import { EventEmitter, Injectable, inject } from '@angular/core'; import { Observable, from } from 'rxjs'; import { map, catchError } from 'rxjs/operators'; import { CustomModelApi } from '@alfresco/js-api'; @@ -26,6 +26,8 @@ import { CustomModelApi } from '@alfresco/js-api'; providedIn: 'root' }) export class EcmModelService { + private readonly apiService = inject(AlfrescoApiService); + public static MODEL_NAMESPACE: string = 'activitiForms'; public static MODEL_NAME: string = 'activitiFormsModel'; public static TYPE_MODEL: string = 'cm:folder'; @@ -38,8 +40,6 @@ export class EcmModelService { return this._customModelApi; } - constructor(private apiService: AlfrescoApiService) {} - public createEcmTypeForActivitiForm(formName: string, form: FormModel): Observable<any> { return new Observable((observer) => { this.searchActivitiEcmModel().subscribe( diff --git a/lib/process-services/src/lib/form/services/editor.service.ts b/lib/process-services/src/lib/form/services/editor.service.ts index 2448d686bc..c4556eef89 100644 --- a/lib/process-services/src/lib/form/services/editor.service.ts +++ b/lib/process-services/src/lib/form/services/editor.service.ts @@ -16,7 +16,7 @@ */ import { AlfrescoApiService } from '@alfresco/adf-content-services'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from, throwError } from 'rxjs'; import { FormModelsApi, FormRepresentation } from '@alfresco/js-api'; import { catchError, map } from 'rxjs/operators'; @@ -26,6 +26,8 @@ import { FormDefinitionModel } from '../model/form-definition.model'; providedIn: 'root' }) export class EditorService { + private readonly apiService = inject(AlfrescoApiService); + static UNKNOWN_ERROR_MESSAGE: string = 'Unknown error'; static GENERIC_ERROR_MESSAGE: string = 'Server error'; @@ -35,8 +37,6 @@ export class EditorService { return this._editorApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Saves a form. * diff --git a/lib/process-services/src/lib/form/services/model.service.ts b/lib/process-services/src/lib/form/services/model.service.ts index 689339c111..ef1d9ae496 100644 --- a/lib/process-services/src/lib/form/services/model.service.ts +++ b/lib/process-services/src/lib/form/services/model.service.ts @@ -16,7 +16,7 @@ */ import { AlfrescoApiService } from '@alfresco/adf-content-services'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from, throwError } from 'rxjs'; import { ModelsApi } from '@alfresco/js-api'; import { catchError, map } from 'rxjs/operators'; @@ -25,6 +25,8 @@ import { catchError, map } from 'rxjs/operators'; providedIn: 'root' }) export class ModelService { + private readonly apiService = inject(AlfrescoApiService); + static UNKNOWN_ERROR_MESSAGE: string = 'Unknown error'; static GENERIC_ERROR_MESSAGE: string = 'Server error'; @@ -34,8 +36,6 @@ export class ModelService { return this._modelsApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Create a Form. * diff --git a/lib/process-services/src/lib/form/services/process-content.service.ts b/lib/process-services/src/lib/form/services/process-content.service.ts index 14ccbefe46..888ed6eaf5 100644 --- a/lib/process-services/src/lib/form/services/process-content.service.ts +++ b/lib/process-services/src/lib/form/services/process-content.service.ts @@ -16,7 +16,7 @@ */ import { AlfrescoApiService } from '@alfresco/adf-content-services'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ActivitiContentApi, RelatedContentRepresentation, @@ -30,6 +30,8 @@ import { catchError } from 'rxjs/operators'; providedIn: 'root' }) export class ProcessContentService { + private readonly apiService = inject(AlfrescoApiService); + static UNKNOWN_ERROR_MESSAGE: string = 'Unknown error'; static GENERIC_ERROR_MESSAGE: string = 'Server error'; @@ -39,8 +41,6 @@ export class ProcessContentService { return this._contentApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Create temporary related content from an uploaded file. * diff --git a/lib/process-services/src/lib/form/services/process-definition.service.ts b/lib/process-services/src/lib/form/services/process-definition.service.ts index 630e6f8ed0..f0fd3adc73 100644 --- a/lib/process-services/src/lib/form/services/process-definition.service.ts +++ b/lib/process-services/src/lib/form/services/process-definition.service.ts @@ -17,7 +17,7 @@ import { AlfrescoApiService } from '@alfresco/adf-content-services'; import { FormFieldOption } from '@alfresco/adf-core'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from, throwError } from 'rxjs'; import { ProcessDefinitionsApi } from '@alfresco/js-api'; import { catchError } from 'rxjs/operators'; @@ -27,6 +27,8 @@ import { DynamicTableColumnOption } from '../widgets/dynamic-table/editors/model providedIn: 'root' }) export class ProcessDefinitionService { + private readonly apiService = inject(AlfrescoApiService); + static UNKNOWN_ERROR_MESSAGE: string = 'Unknown error'; static GENERIC_ERROR_MESSAGE: string = 'Server error'; @@ -36,8 +38,6 @@ export class ProcessDefinitionService { return this._processDefinitionsApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Gets values of fields populated by a REST backend using a process ID. * @@ -88,8 +88,8 @@ export class ProcessDefinitionService { errMsg = error.message ? error.message : error.status - ? `${error.status} - ${error.statusText}` - : ProcessDefinitionService.GENERIC_ERROR_MESSAGE; + ? `${error.status} - ${error.statusText}` + : ProcessDefinitionService.GENERIC_ERROR_MESSAGE; } return throwError(errMsg); } diff --git a/lib/process-services/src/lib/form/services/task-form.service.ts b/lib/process-services/src/lib/form/services/task-form.service.ts index 17aba2b36b..07407e9036 100644 --- a/lib/process-services/src/lib/form/services/task-form.service.ts +++ b/lib/process-services/src/lib/form/services/task-form.service.ts @@ -17,7 +17,7 @@ import { AlfrescoApiService } from '@alfresco/adf-content-services'; import { FormFieldOption, FormValues, TaskProcessVariableModel } from '@alfresco/adf-core'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { from, Observable, throwError } from 'rxjs'; import { CompleteFormRepresentation, SaveFormRepresentation, TaskFormsApi } from '@alfresco/js-api'; import { catchError, map } from 'rxjs/operators'; @@ -27,6 +27,8 @@ import { DynamicTableColumnOption } from '../widgets/dynamic-table/editors/model providedIn: 'root' }) export class TaskFormService { + private readonly apiService = inject(AlfrescoApiService); + static UNKNOWN_ERROR_MESSAGE: string = 'Unknown error'; static GENERIC_ERROR_MESSAGE: string = 'Server error'; @@ -36,8 +38,6 @@ export class TaskFormService { return this._taskFormsApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Saves a task form. * diff --git a/lib/process-services/src/lib/form/services/task.service.ts b/lib/process-services/src/lib/form/services/task.service.ts index eba012fdd0..decd28a314 100644 --- a/lib/process-services/src/lib/form/services/task.service.ts +++ b/lib/process-services/src/lib/form/services/task.service.ts @@ -16,7 +16,7 @@ */ import { AlfrescoApiService } from '@alfresco/adf-content-services'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from, throwError } from 'rxjs'; import { TaskRepresentation, TasksApi } from '@alfresco/js-api'; import { catchError, map } from 'rxjs/operators'; @@ -25,6 +25,8 @@ import { catchError, map } from 'rxjs/operators'; providedIn: 'root' }) export class TaskService { + private readonly apiService = inject(AlfrescoApiService); + static UNKNOWN_ERROR_MESSAGE: string = 'Unknown error'; static GENERIC_ERROR_MESSAGE: string = 'Server error'; @@ -34,8 +36,6 @@ export class TaskService { return this._taskApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Gets a task. * diff --git a/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget-dialog.component.ts b/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget-dialog.component.ts index 2bd3b5c9db..408b61e54d 100644 --- a/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget-dialog.component.ts +++ b/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget-dialog.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, DestroyRef, inject, Inject, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { AuthenticationService, LoginDialogPanelComponent, TranslationService } from '@alfresco/adf-core'; import { AttachFileWidgetDialogComponentData } from './attach-file-widget-dialog-component.interface'; @@ -48,6 +48,12 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; ] }) export class AttachFileWidgetDialogComponent implements OnInit { + private readonly translation = inject(TranslationService); + data = inject<AttachFileWidgetDialogComponentData>(MAT_DIALOG_DATA); + private readonly externalApiService = inject(AlfrescoApiService); + private readonly authenticationService = inject(AuthenticationService); + private readonly matDialogRef = inject<MatDialogRef<AttachFileWidgetDialogComponent>>(MatDialogRef); + @ViewChild('adfLoginPanel') loginPanel: LoginDialogPanelComponent; @@ -58,13 +64,10 @@ export class AttachFileWidgetDialogComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private translation: TranslationService, - @Inject(MAT_DIALOG_DATA) public data: AttachFileWidgetDialogComponentData, - private externalApiService: AlfrescoApiService, - private authenticationService: AuthenticationService, - private matDialogRef: MatDialogRef<AttachFileWidgetDialogComponent> - ) { + constructor() { + const data = this.data; + const externalApiService = this.externalApiService; + (externalApiService as ExternalAlfrescoApiService).init(data.ecmHost, data.context); this.action = data.actionName ? data.actionName.toUpperCase() : 'CHOOSE'; this.buttonActionName = `ATTACH-FILE.ACTIONS.${this.action}`; diff --git a/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget-dialog.service.ts b/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget-dialog.service.ts index 882d736bf5..356ed1b924 100644 --- a/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget-dialog.service.ts +++ b/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget-dialog.service.ts @@ -17,7 +17,7 @@ import { AlfrescoApiService } from '@alfresco/adf-content-services'; import { MatDialog } from '@angular/material/dialog'; -import { EventEmitter, Injectable, Output } from '@angular/core'; +import { EventEmitter, Injectable, Output, inject } from '@angular/core'; import { TranslationService } from '@alfresco/adf-core'; import { Observable, of, Subject } from 'rxjs'; import { AttachFileWidgetDialogComponentData } from './attach-file-widget-dialog-component.interface'; @@ -30,14 +30,15 @@ import { switchMap } from 'rxjs/operators'; }) // eslint-disable-next-line @angular-eslint/directive-class-suffix export class AttachFileWidgetDialogService { + private readonly dialog = inject(MatDialog); + private readonly translation = inject(TranslationService); + /** Emitted when an error occurs. */ @Output() error = new EventEmitter<any>(); private externalApis: { [key: string]: AlfrescoApiService } = {}; - constructor(private dialog: MatDialog, private translation: TranslationService) {} - /** * Opens a dialog to choose a file to upload. * diff --git a/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget.component.ts b/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget.component.ts index 6b71a2ca73..fc700b3cd6 100644 --- a/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget.component.ts +++ b/lib/process-services/src/lib/form/widgets/content-widget/attach-file-widget.component.ts @@ -43,6 +43,15 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; encapsulation: ViewEncapsulation.None }) export class AttachFileWidgetComponent extends UploadWidgetComponent implements OnInit { + private readonly activitiContentService = inject(ActivitiContentService); + private readonly contentService = inject(ContentService); + private readonly contentDialog = inject(ContentNodeDialogService); + private readonly appConfigService = inject(AppConfigService); + private readonly downloadService = inject(DownloadService); + private readonly router = inject(Router); + private readonly activatedRoute = inject(ActivatedRoute); + private readonly attachDialogService = inject(AttachFileWidgetDialogService); + typeId = 'AttachFileWidgetComponent'; repositoryList: AlfrescoEndpointRepresentation[] = []; isStartProcessPage = false; @@ -51,19 +60,6 @@ export class AttachFileWidgetComponent extends UploadWidgetComponent implements private readonly destroyRef = inject(DestroyRef); - constructor( - private activitiContentService: ActivitiContentService, - private contentService: ContentService, - private contentDialog: ContentNodeDialogService, - private appConfigService: AppConfigService, - private downloadService: DownloadService, - private router: Router, - private activatedRoute: ActivatedRoute, - private attachDialogService: AttachFileWidgetDialogService - ) { - super(); - } - ngOnInit() { super.ngOnInit(); this.isStartProcessPage = this.router.url.split('?')[0].includes('start-process'); diff --git a/lib/process-services/src/lib/form/widgets/content-widget/attach-folder-widget.component.ts b/lib/process-services/src/lib/form/widgets/content-widget/attach-folder-widget.component.ts index 405814805f..60c3a06cb7 100644 --- a/lib/process-services/src/lib/form/widgets/content-widget/attach-folder-widget.component.ts +++ b/lib/process-services/src/lib/form/widgets/content-widget/attach-folder-widget.component.ts @@ -17,7 +17,7 @@ /* eslint-disable @angular-eslint/component-selector */ -import { Component, ViewEncapsulation, OnInit } from '@angular/core'; +import { Component, ViewEncapsulation, OnInit, inject } from '@angular/core'; import { WidgetComponent, FormService, ErrorWidgetComponent } from '@alfresco/adf-core'; import { ContentNodeDialogService, NodesApiService } from '@alfresco/adf-content-services'; import { Node } from '@alfresco/js-api'; @@ -46,14 +46,14 @@ import { MatButtonModule } from '@angular/material/button'; encapsulation: ViewEncapsulation.None }) export class AttachFolderWidgetComponent extends WidgetComponent implements OnInit { + private readonly contentDialog = inject(ContentNodeDialogService); + formService = inject(FormService); + private readonly nodeService = inject(NodesApiService); + typeId = 'AttachFolderWidgetComponent'; hasFolder: boolean = false; selectedFolderName: string = ''; - constructor(private contentDialog: ContentNodeDialogService, public formService: FormService, private nodeService: NodesApiService) { - super(); - } - ngOnInit() { if (this.field?.value) { this.hasFolder = true; diff --git a/lib/process-services/src/lib/form/widgets/document/content.widget.ts b/lib/process-services/src/lib/form/widgets/document/content.widget.ts index e1fa0a19c7..0e5c228f97 100644 --- a/lib/process-services/src/lib/form/widgets/document/content.widget.ts +++ b/lib/process-services/src/lib/form/widgets/document/content.widget.ts @@ -16,7 +16,7 @@ */ import { UrlService, ContentLinkModel, FormService, DownloadService } from '@alfresco/adf-core'; -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewEncapsulation, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { ProcessContentService } from '../../services/process-content.service'; import { CommonModule } from '@angular/common'; @@ -33,6 +33,11 @@ import { MatButtonModule } from '@angular/material/button'; encapsulation: ViewEncapsulation.None }) export class ContentWidgetComponent implements OnChanges { + protected formService = inject(FormService); + private readonly downloadService = inject(DownloadService); + private readonly urlService = inject(UrlService); + private readonly processContentService = inject(ProcessContentService); + /** The content id to show. */ @Input() id: string; @@ -59,13 +64,6 @@ export class ContentWidgetComponent implements OnChanges { content: ContentLinkModel; - constructor( - protected formService: FormService, - private downloadService: DownloadService, - private urlService: UrlService, - private processContentService: ProcessContentService - ) {} - ngOnChanges(changes: SimpleChanges) { const contentId = changes['id']; if (contentId?.currentValue) { diff --git a/lib/process-services/src/lib/form/widgets/dynamic-table/dynamic-table.widget.ts b/lib/process-services/src/lib/form/widgets/dynamic-table/dynamic-table.widget.ts index 47e784e7cc..b0fc04371c 100644 --- a/lib/process-services/src/lib/form/widgets/dynamic-table/dynamic-table.widget.ts +++ b/lib/process-services/src/lib/form/widgets/dynamic-table/dynamic-table.widget.ts @@ -53,11 +53,11 @@ export class DynamicTableWidgetComponent extends WidgetComponent implements OnIn editMode: boolean = false; editRow: DynamicTableRow = null; - private selectArrayCode = [32, 0, 13]; + private readonly selectArrayCode = [32, 0, 13]; public elementRef = inject(ElementRef); - private visibilityService = inject(WidgetVisibilityService); - private cd = inject(ChangeDetectorRef); + private readonly visibilityService = inject(WidgetVisibilityService); + private readonly cd = inject(ChangeDetectorRef); ngOnInit() { if (this.field) { diff --git a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/date/date.editor.ts b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/date/date.editor.ts index 26b7d9de56..e535e050cb 100644 --- a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/date/date.editor.ts +++ b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/date/date.editor.ts @@ -16,7 +16,7 @@ */ import { ADF_DATE_FORMATS, AdfDateFnsAdapter, DateFnsUtils } from '@alfresco/adf-core'; -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { MatDatepickerInputEvent, MatDatepickerModule } from '@angular/material/datepicker'; import { DynamicTableColumn } from '../models/dynamic-table-column.model'; @@ -39,6 +39,8 @@ import { FormsModule } from '@angular/forms'; styleUrls: ['./date.editor.scss'] }) export class DateEditorComponent implements OnInit { + private readonly dateAdapter = inject<DateAdapter<Date>>(DateAdapter); + DATE_FORMAT: string = 'DD-MM-YYYY'; @Input() @@ -56,8 +58,6 @@ export class DateEditorComponent implements OnInit { minDate: Date; maxDate: Date; - constructor(private dateAdapter: DateAdapter<Date>) {} - ngOnInit() { const dateAdapter = this.dateAdapter as AdfDateFnsAdapter; dateAdapter.displayFormat = this.DATE_FORMAT; diff --git a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/datetime/datetime.editor.ts b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/datetime/datetime.editor.ts index 18ccfa4b40..7503c320ab 100644 --- a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/datetime/datetime.editor.ts +++ b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/datetime/datetime.editor.ts @@ -16,7 +16,7 @@ */ import { ADF_DATETIME_FORMATS, ADF_DATE_FORMATS, AdfDateFnsAdapter, AdfDateTimeFnsAdapter, DateFnsUtils } from '@alfresco/adf-core'; -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { DynamicTableColumn } from '../models/dynamic-table-column.model'; import { DynamicTableRow } from '../models/dynamic-table-row.model'; @@ -40,6 +40,8 @@ import { FormsModule } from '@angular/forms'; styleUrls: ['./datetime.editor.scss'] }) export class DateTimeEditorComponent implements OnInit { + private readonly dateAdapter = inject<DateAdapter<Date>>(DateAdapter); + DATE_TIME_FORMAT: string = 'DD/MM/YYYY HH:mm'; @Input() @@ -57,8 +59,6 @@ export class DateTimeEditorComponent implements OnInit { minDate: Date; maxDate: Date; - constructor(private dateAdapter: DateAdapter<Date>) {} - ngOnInit() { const momentDateAdapter = this.dateAdapter as AdfDateFnsAdapter; momentDateAdapter.displayFormat = this.DATE_TIME_FORMAT; diff --git a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/dropdown/dropdown.editor.ts b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/dropdown/dropdown.editor.ts index dd2aca4960..8308b5c931 100644 --- a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/dropdown/dropdown.editor.ts +++ b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/dropdown/dropdown.editor.ts @@ -18,7 +18,7 @@ /* eslint-disable @angular-eslint/component-selector */ import { FormService, FormFieldModel } from '@alfresco/adf-core'; -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { DynamicTableColumnOption } from '../models/dynamic-table-column-option.model'; import { DynamicTableColumn } from '../models/dynamic-table-column.model'; import { DynamicTableRow } from '../models/dynamic-table-row.model'; @@ -37,6 +37,10 @@ import { FormsModule } from '@angular/forms'; styleUrls: ['./dropdown.editor.scss'] }) export class DropdownEditorComponent implements OnInit { + formService = inject(FormService); + private readonly taskFormService = inject(TaskFormService); + private readonly processDefinitionService = inject(ProcessDefinitionService); + value: any = null; options: DynamicTableColumnOption[] = []; @@ -49,12 +53,6 @@ export class DropdownEditorComponent implements OnInit { @Input() column: DynamicTableColumn; - constructor( - public formService: FormService, - private taskFormService: TaskFormService, - private processDefinitionService: ProcessDefinitionService - ) {} - ngOnInit() { const field = this.table.field; if (field) { diff --git a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/date-cell-validator-model.ts b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/date-cell-validator-model.ts index 635d77b567..c2091f5fd6 100644 --- a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/date-cell-validator-model.ts +++ b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/date-cell-validator-model.ts @@ -26,7 +26,7 @@ import { isValid } from 'date-fns'; export class DateCellValidator implements CellValidator { static DATE_TYPE = 'Date'; - private supportedTypes: string[] = [DateCellValidator.DATE_TYPE]; + private readonly supportedTypes: string[] = [DateCellValidator.DATE_TYPE]; isSupported(column: DynamicTableColumn): boolean { return !!(column?.editable && this.supportedTypes.indexOf(column?.type) > -1); diff --git a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/dynamic-table.widget.model.ts b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/dynamic-table.widget.model.ts index d808756673..be839cf4bf 100644 --- a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/dynamic-table.widget.model.ts +++ b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/dynamic-table.widget.model.ts @@ -54,7 +54,10 @@ export class DynamicTableModel extends FormWidgetModel { } } - constructor(field: FormFieldModel, private formService: FormService) { + constructor( + field: FormFieldModel, + private readonly formService: FormService + ) { super(field.form, field.json); this.field = field; @@ -66,7 +69,7 @@ export class DynamicTableModel extends FormWidgetModel { } if (field.json.value) { - this.rows = field.json.value.map((obj) => ({ selected: false, value: obj } as DynamicTableRow)); + this.rows = field.json.value.map((obj) => ({ selected: false, value: obj }) as DynamicTableRow); } } diff --git a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/number-cell-validator.model.ts b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/number-cell-validator.model.ts index 4703d8266c..5d4ea73509 100644 --- a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/number-cell-validator.model.ts +++ b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/number-cell-validator.model.ts @@ -23,7 +23,7 @@ import { DynamicTableColumn } from './dynamic-table-column.model'; import { DynamicTableRow } from './dynamic-table-row.model'; export class NumberCellValidator implements CellValidator { - private supportedTypes: string[] = ['Number', 'Amount']; + private readonly supportedTypes: string[] = ['Number', 'Amount']; isSupported(column: DynamicTableColumn): boolean { return column?.required && this.supportedTypes.indexOf(column.type) > -1; diff --git a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/required-cell-validator.model.ts b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/required-cell-validator.model.ts index 53b28e6f97..73444b7dd6 100644 --- a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/required-cell-validator.model.ts +++ b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/models/required-cell-validator.model.ts @@ -23,7 +23,7 @@ import { DynamicTableColumn } from './dynamic-table-column.model'; import { DynamicTableRow } from './dynamic-table-row.model'; export class RequiredCellValidator implements CellValidator { - private supportedTypes: string[] = ['String', 'Number', 'Amount', 'Date', 'Dropdown']; + private readonly supportedTypes: string[] = ['String', 'Number', 'Amount', 'Date', 'Dropdown']; isSupported(column: DynamicTableColumn): boolean { return column?.required && this.supportedTypes.indexOf(column.type) > -1; diff --git a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/row-editor/row.editor.spec.ts b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/row-editor/row.editor.spec.ts index 0ecbfa635d..2fc0483131 100644 --- a/lib/process-services/src/lib/form/widgets/dynamic-table/editors/row-editor/row.editor.spec.ts +++ b/lib/process-services/src/lib/form/widgets/dynamic-table/editors/row-editor/row.editor.spec.ts @@ -21,14 +21,21 @@ import { DynamicTableRow } from '../models/dynamic-table-row.model'; import { DynamicTableModel } from '../models/dynamic-table.widget.model'; import { RowEditorComponent } from './row.editor'; import { DynamicRowValidationSummary } from '../models/dynamic-row-validation-summary.model'; +import { TestBed } from '@angular/core/testing'; describe('RowEditorComponent', () => { let component: RowEditorComponent; + let formService: FormService; beforeEach(() => { + TestBed.configureTestingModule({ + providers: [FormService] + }); + + formService = TestBed.inject(FormService); component = new RowEditorComponent(); const field = new FormFieldModel(new FormModel()); - component.table = new DynamicTableModel(field, new FormService()); + component.table = new DynamicTableModel(field, formService); component.row = {} as DynamicTableRow; component.column = {} as DynamicTableColumn; }); diff --git a/lib/process-services/src/lib/form/widgets/radio-buttons/radio-buttons.widget.ts b/lib/process-services/src/lib/form/widgets/radio-buttons/radio-buttons.widget.ts index 400ce8bd81..e397e4eedd 100644 --- a/lib/process-services/src/lib/form/widgets/radio-buttons/radio-buttons.widget.ts +++ b/lib/process-services/src/lib/form/widgets/radio-buttons/radio-buttons.widget.ts @@ -45,8 +45,8 @@ import { FormsModule } from '@angular/forms'; encapsulation: ViewEncapsulation.None }) export class RadioButtonsWidgetComponent extends WidgetComponent implements OnInit { - private taskFormService = inject(TaskFormService); - private processDefinitionService = inject(ProcessDefinitionService); + private readonly taskFormService = inject(TaskFormService); + private readonly processDefinitionService = inject(ProcessDefinitionService); ngOnInit() { if (this.isValidRestConfig() && !this.isReadOnlyForm()) { diff --git a/lib/process-services/src/lib/form/widgets/typeahead/typeahead.widget.ts b/lib/process-services/src/lib/form/widgets/typeahead/typeahead.widget.ts index 67fda419f7..95de622eba 100644 --- a/lib/process-services/src/lib/form/widgets/typeahead/typeahead.widget.ts +++ b/lib/process-services/src/lib/form/widgets/typeahead/typeahead.widget.ts @@ -53,8 +53,8 @@ export class TypeaheadWidgetComponent extends WidgetComponent implements OnInit oldValue: string; options: FormFieldOption[] = []; - private taskFormService = inject(TaskFormService); - private processDefinitionService = inject(ProcessDefinitionService); + private readonly taskFormService = inject(TaskFormService); + private readonly processDefinitionService = inject(ProcessDefinitionService); ngOnInit() { if (this.field.form.taskId && this.field.restUrl) { diff --git a/lib/process-services/src/lib/people/components/people-search-field/people-search-field.component.ts b/lib/process-services/src/lib/people/components/people-search-field/people-search-field.component.ts index bf1ddee53c..c082dea72a 100644 --- a/lib/process-services/src/lib/people/components/people-search-field/people-search-field.component.ts +++ b/lib/process-services/src/lib/people/components/people-search-field/people-search-field.component.ts @@ -16,7 +16,7 @@ */ import { DataColumnComponent, DataColumnListComponent, TranslationService } from '@alfresco/adf-core'; -import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, Output, ViewEncapsulation, inject } from '@angular/core'; import { ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; import { debounceTime, switchMap } from 'rxjs/operators'; import { Observable, of } from 'rxjs'; @@ -46,6 +46,9 @@ import { PeopleListComponent } from '../people-list/people-list.component'; encapsulation: ViewEncapsulation.None }) export class PeopleSearchFieldComponent { + peopleProcessService = inject(PeopleProcessService); + private readonly translationService = inject(TranslationService); + @Input() performSearch: PerformSearchCallback; @@ -60,7 +63,7 @@ export class PeopleSearchFieldComponent { defaultPlaceholder = 'ADF_TASK_LIST.PEOPLE.SEARCH_USER'; - constructor(public peopleProcessService: PeopleProcessService, private translationService: TranslationService) { + constructor() { this.users$ = this.searchUser.valueChanges.pipe( debounceTime(200), switchMap((searchWord: string) => { diff --git a/lib/process-services/src/lib/people/components/people-selector/people-selector.component.ts b/lib/process-services/src/lib/people/components/people-selector/people-selector.component.ts index 8ed27521a1..09f60c24fd 100644 --- a/lib/process-services/src/lib/people/components/people-selector/people-selector.component.ts +++ b/lib/process-services/src/lib/people/components/people-selector/people-selector.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, ViewChild, ViewEncapsulation, EventEmitter, Input, Output } from '@angular/core'; +import { Component, ViewChild, ViewEncapsulation, EventEmitter, Input, Output, inject } from '@angular/core'; import { PerformSearchCallback } from '../../interfaces/perform-search-callback.interface'; import { TranslationService } from '@alfresco/adf-core'; import { PeopleSearchFieldComponent } from '../people-search-field/people-search-field.component'; @@ -39,6 +39,9 @@ const DEFAULT_ASSIGNEE_PLACEHOLDER = 'ADF_TASK_LIST.PEOPLE.ASSIGNEE'; encapsulation: ViewEncapsulation.None }) export class PeopleSelectorComponent { + private readonly peopleProcessService = inject(PeopleProcessService); + private readonly translationService = inject(TranslationService); + @Input() peopleId: LightUserRepresentation; @@ -53,7 +56,7 @@ export class PeopleSelectorComponent { selectedUser: LightUserRepresentation; defaultPlaceholder: string; - constructor(private peopleProcessService: PeopleProcessService, private translationService: TranslationService) { + constructor() { this.peopleIdChange = new EventEmitter(); this.performSearch = this.searchUser.bind(this); this.defaultPlaceholder = this.translationService.instant(DEFAULT_ASSIGNEE_PLACEHOLDER); diff --git a/lib/process-services/src/lib/people/components/people/people.component.ts b/lib/process-services/src/lib/people/components/people/people.component.ts index 08f9e7676a..384aaebdf2 100644 --- a/lib/process-services/src/lib/people/components/people/people.component.ts +++ b/lib/process-services/src/lib/people/components/people/people.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { Observable, Observer } from 'rxjs'; import { UserEventModel } from '../../../task-list/models/user-event.model'; import { PeopleSearchComponent } from '../people-search/people-search.component'; @@ -46,6 +46,8 @@ import { DataColumnComponent, DataColumnListComponent } from '@alfresco/adf-core encapsulation: ViewEncapsulation.None }) export class PeopleComponent { + peopleProcessService = inject(PeopleProcessService); + /** The array of User objects to display. */ @Input() people: LightUserRepresentation[] = []; @@ -69,7 +71,7 @@ export class PeopleComponent { private peopleSearchObserver: Observer<LightUserRepresentation[]>; - constructor(public peopleProcessService: PeopleProcessService) { + constructor() { this.peopleSearch$ = new Observable<LightUserRepresentation[]>((observer) => (this.peopleSearchObserver = observer)).pipe(share()); } diff --git a/lib/process-services/src/lib/process-comments/services/comment-process.service.ts b/lib/process-services/src/lib/process-comments/services/comment-process.service.ts index 8803fd5afa..0493504115 100644 --- a/lib/process-services/src/lib/process-comments/services/comment-process.service.ts +++ b/lib/process-services/src/lib/process-comments/services/comment-process.service.ts @@ -16,7 +16,7 @@ */ import { AlfrescoApiService } from '@alfresco/adf-content-services'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from } from 'rxjs'; import { CommentModel, CommentsService, User } from '@alfresco/adf-core'; import { map } from 'rxjs/operators'; @@ -27,14 +27,15 @@ import { PeopleProcessService } from '../../services/people-process.service'; providedIn: 'root' }) export class CommentProcessService implements CommentsService { + private readonly apiService = inject(AlfrescoApiService); + private readonly peopleProcessService = inject(PeopleProcessService); + private _commentsApi: ActivitiCommentsApi; get commentsApi(): ActivitiCommentsApi { this._commentsApi = this._commentsApi ?? new ActivitiCommentsApi(this.apiService.getInstance()); return this._commentsApi; } - constructor(private apiService: AlfrescoApiService, private peopleProcessService: PeopleProcessService) {} - /** * Gets all comments that have been added to a process instance. * diff --git a/lib/process-services/src/lib/process-list/components/process-filters/process-filters.component.ts b/lib/process-services/src/lib/process-list/components/process-filters/process-filters.component.ts index 114ec32fea..963fdf5291 100644 --- a/lib/process-services/src/lib/process-list/components/process-filters/process-filters.component.ts +++ b/lib/process-services/src/lib/process-list/components/process-filters/process-filters.component.ts @@ -36,6 +36,11 @@ import { MatIconModule } from '@angular/material/icon'; encapsulation: ViewEncapsulation.None }) export class ProcessFiltersComponent implements OnInit, OnChanges { + private readonly processFilterService = inject(ProcessFilterService); + private readonly appsProcessService = inject(AppsProcessService); + private readonly router = inject(Router); + private readonly location = inject(Location); + /** * The parameters to filter the task filter. If there is no match then the default one * (ie, the first filter in the list) is selected. @@ -82,13 +87,6 @@ export class ProcessFiltersComponent implements OnInit, OnChanges { private readonly destroyRef = inject(DestroyRef); - constructor( - private processFilterService: ProcessFilterService, - private appsProcessService: AppsProcessService, - private router: Router, - private location: Location - ) {} - ngOnInit() { this.iconsMDL = new IconModel(); this.router.events diff --git a/lib/process-services/src/lib/process-list/components/process-instance-details/process-instance-details.component.ts b/lib/process-services/src/lib/process-list/components/process-instance-details/process-instance-details.component.ts index 68797c49c9..2556787acd 100644 --- a/lib/process-services/src/lib/process-list/components/process-instance-details/process-instance-details.component.ts +++ b/lib/process-services/src/lib/process-list/components/process-instance-details/process-instance-details.component.ts @@ -16,7 +16,7 @@ */ import { CommonModule, DatePipe } from '@angular/common'; -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild, inject } from '@angular/core'; import { TaskDetailsEvent } from '../../../task-list'; import { ProcessService } from '../../services/process.service'; import { ProcessInstanceHeaderComponent } from '../process-instance-header/process-instance-header.component'; @@ -42,6 +42,8 @@ import { ProcessCommentsComponent } from '../../../process-comments'; styleUrls: ['./process-instance-details.component.css'] }) export class ProcessInstanceDetailsComponent implements OnChanges { + private readonly processService = inject(ProcessService); + /** (required) The numeric ID of the process instance to display. */ @Input({ required: true }) processInstanceId: string; @@ -78,8 +80,6 @@ export class ProcessInstanceDetailsComponent implements OnChanges { processInstanceDetails: ProcessInstanceRepresentation; - constructor(private processService: ProcessService) {} - ngOnChanges(changes: SimpleChanges) { const processInstanceId = changes['processInstanceId']; if (processInstanceId && !processInstanceId.currentValue) { diff --git a/lib/process-services/src/lib/process-list/components/process-instance-header/process-instance-header.component.ts b/lib/process-services/src/lib/process-list/components/process-instance-header/process-instance-header.component.ts index d6cc9cac70..3f28247094 100644 --- a/lib/process-services/src/lib/process-list/components/process-instance-header/process-instance-header.component.ts +++ b/lib/process-services/src/lib/process-list/components/process-instance-header/process-instance-header.component.ts @@ -24,7 +24,7 @@ import { CardViewTextItemModel, TranslationService } from '@alfresco/adf-core'; -import { Component, Input, OnChanges } from '@angular/core'; +import { Component, Input, OnChanges, inject } from '@angular/core'; import { ProcessInstanceRepresentation } from '@alfresco/js-api'; import { CommonModule } from '@angular/common'; import { MatCardModule } from '@angular/material/card'; @@ -36,6 +36,9 @@ import { MatCardModule } from '@angular/material/card'; styleUrls: ['./process-instance-header.component.css'] }) export class ProcessInstanceHeaderComponent implements OnChanges { + private readonly translationService = inject(TranslationService); + private readonly appConfig = inject(AppConfigService); + /** (**required**) Full details of the process instance to display information about. */ @Input({ required: true }) processInstance: ProcessInstanceRepresentation; @@ -44,7 +47,7 @@ export class ProcessInstanceHeaderComponent implements OnChanges { dateFormat: string; dateLocale: string; - constructor(private translationService: TranslationService, private appConfig: AppConfigService) { + constructor() { this.dateFormat = this.appConfig.get('dateValues.defaultDateFormat'); this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale'); } diff --git a/lib/process-services/src/lib/process-list/components/process-instance-tasks/process-instance-tasks.component.ts b/lib/process-services/src/lib/process-list/components/process-instance-tasks/process-instance-tasks.component.ts index 12d9f6cf4a..1b4a944c32 100644 --- a/lib/process-services/src/lib/process-list/components/process-instance-tasks/process-instance-tasks.component.ts +++ b/lib/process-services/src/lib/process-list/components/process-instance-tasks/process-instance-tasks.component.ts @@ -38,6 +38,9 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; styleUrls: ['./process-instance-tasks.component.css'] }) export class ProcessInstanceTasksComponent implements OnInit, OnChanges { + private readonly processService = inject(ProcessService); + private readonly dialog = inject(MatDialog); + /** The ID of the process instance to display tasks for. */ @Input({ required: true }) processInstanceDetails: ProcessInstanceRepresentation; @@ -75,7 +78,7 @@ export class ProcessInstanceTasksComponent implements OnInit, OnChanges { private readonly destroyRef = inject(DestroyRef); - constructor(private processService: ProcessService, private dialog: MatDialog) { + constructor() { this.task$ = new Observable<TaskRepresentation>((observer) => (this.taskObserver = observer)).pipe(share()); this.completedTask$ = new Observable<TaskRepresentation>((observer) => (this.completedTaskObserver = observer)).pipe(share()); } diff --git a/lib/process-services/src/lib/process-list/components/process-list/process-list.component.spec.ts b/lib/process-services/src/lib/process-list/components/process-list/process-list.component.spec.ts index 1fda80ec3f..22a0580e6c 100644 --- a/lib/process-services/src/lib/process-list/components/process-list/process-list.component.spec.ts +++ b/lib/process-services/src/lib/process-list/components/process-list/process-list.component.spec.ts @@ -594,7 +594,7 @@ class ProcessListContextMenuComponent implements OnInit { appId: number; - private performAction$ = new Subject<any>(); + private readonly performAction$ = new Subject<any>(); ngOnInit() { this.performContextActions(); diff --git a/lib/process-services/src/lib/process-list/components/process-list/process-list.component.ts b/lib/process-services/src/lib/process-list/components/process-list/process-list.component.ts index 9a5c71f117..ba35745b92 100644 --- a/lib/process-services/src/lib/process-list/components/process-list/process-list.component.ts +++ b/lib/process-services/src/lib/process-list/components/process-list/process-list.component.ts @@ -23,7 +23,6 @@ import { CustomLoadingContentTemplateDirective, DataRow, DataColumn, - AppConfigService, PaginatedComponent, PaginationModel, UserPreferencesService, @@ -35,7 +34,7 @@ import { NoContentTemplateDirective, ObjectDataRow } from '@alfresco/adf-core'; -import { AfterContentInit, Component, ContentChild, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; +import { AfterContentInit, Component, ContentChild, EventEmitter, Input, OnChanges, Output, SimpleChanges, inject } from '@angular/core'; import { ProcessService } from '../../services/process.service'; import { BehaviorSubject } from 'rxjs'; import { finalize } from 'rxjs/operators'; @@ -84,6 +83,9 @@ export const processPresetsDefaultModel = { templateUrl: './process-list.component.html' }) export class ProcessInstanceListComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent { + private readonly processService = inject(ProcessService); + private readonly userPreferences = inject(UserPreferencesService); + @ContentChild(CustomEmptyContentTemplateDirective) customEmptyContent: CustomEmptyContentTemplateDirective; @@ -192,12 +194,8 @@ export class ProcessInstanceListComponent extends DataTableSchema implements OnC sorting: any[] = ['created', 'desc']; pagination: BehaviorSubject<PaginationModel>; - constructor( - private readonly processService: ProcessService, - private readonly userPreferences: UserPreferencesService, - appConfig: AppConfigService - ) { - super(appConfig, PRESET_KEY, processPresetsDefaultModel); + constructor() { + super(PRESET_KEY, processPresetsDefaultModel); this.size = this.userPreferences.paginationSize; this.pagination = new BehaviorSubject<PaginationModel>({ diff --git a/lib/process-services/src/lib/process-list/components/start-process/start-process.component.ts b/lib/process-services/src/lib/process-list/components/start-process/start-process.component.ts index 48d05d340f..66a4cd73c1 100644 --- a/lib/process-services/src/lib/process-list/components/start-process/start-process.component.ts +++ b/lib/process-services/src/lib/process-list/components/start-process/start-process.component.ts @@ -82,6 +82,12 @@ const PROCESS_DEFINITION_IDENTIFIER_REG_EXP = new RegExp('%{processdefinition}', encapsulation: ViewEncapsulation.None }) export class StartProcessInstanceComponent implements OnChanges, OnInit { + private readonly processService = inject(ProcessService); + private readonly contentService = inject(ActivitiContentService); + private readonly appsProcessService = inject(AppsProcessService); + private readonly appConfig = inject(AppConfigService); + private readonly datePipe = inject(LocalizedDatePipe); + /** * Limit the list of processes that can be started to those * contained in the specified app. @@ -166,14 +172,6 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private processService: ProcessService, - private contentService: ActivitiContentService, - private appsProcessService: AppsProcessService, - private appConfig: AppConfigService, - private datePipe: LocalizedDatePipe - ) {} - ngOnInit() { this.processNameInput = new UntypedFormControl('', [ Validators.required, diff --git a/lib/process-services/src/lib/process-list/services/process-filter.service.ts b/lib/process-services/src/lib/process-list/services/process-filter.service.ts index 4c2d062cc0..ae8d256cc1 100644 --- a/lib/process-services/src/lib/process-list/services/process-filter.service.ts +++ b/lib/process-services/src/lib/process-list/services/process-filter.service.ts @@ -16,7 +16,7 @@ */ import { AlfrescoApiService } from '@alfresco/adf-content-services'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from, forkJoin } from 'rxjs'; import { map } from 'rxjs/operators'; import { @@ -29,14 +29,14 @@ import { providedIn: 'root' }) export class ProcessFilterService { + private readonly alfrescoApiService = inject(AlfrescoApiService); + private _userFiltersApi: UserFiltersApi; get userFiltersApi(): UserFiltersApi { this._userFiltersApi = this._userFiltersApi ?? new UserFiltersApi(this.alfrescoApiService.getInstance()); return this._userFiltersApi; } - constructor(private alfrescoApiService: AlfrescoApiService) {} - /** * Gets all filters defined for a Process App. * diff --git a/lib/process-services/src/lib/process-list/services/process.service.ts b/lib/process-services/src/lib/process-list/services/process.service.ts index d59cee6ebb..30e0049b29 100644 --- a/lib/process-services/src/lib/process-list/services/process.service.ts +++ b/lib/process-services/src/lib/process-list/services/process.service.ts @@ -40,7 +40,7 @@ import { DatePipe } from '@angular/common'; providedIn: 'root' }) export class ProcessService { - private alfrescoApiService = inject(AlfrescoApiService); + private readonly alfrescoApiService = inject(AlfrescoApiService); private _tasksApi: TasksApi; get tasksApi(): TasksApi { diff --git a/lib/process-services/src/lib/services/apps-process.service.ts b/lib/process-services/src/lib/services/apps-process.service.ts index d5b907c988..afd0f46a11 100644 --- a/lib/process-services/src/lib/services/apps-process.service.ts +++ b/lib/process-services/src/lib/services/apps-process.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { RuntimeAppDefinitionsApi, AppDefinitionRepresentation } from '@alfresco/js-api'; import { Observable, from } from 'rxjs'; import { AlfrescoApiService } from '@alfresco/adf-content-services'; @@ -25,14 +25,14 @@ import { map } from 'rxjs/operators'; providedIn: 'root' }) export class AppsProcessService { + private readonly apiService = inject(AlfrescoApiService); + private _appsApi: RuntimeAppDefinitionsApi; get appsApi(): RuntimeAppDefinitionsApi { this._appsApi = this._appsApi ?? new RuntimeAppDefinitionsApi(this.apiService.getInstance()); return this._appsApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Gets a list of deployed apps for this user. * diff --git a/lib/process-services/src/lib/services/people-process.service.ts b/lib/process-services/src/lib/services/people-process.service.ts index 9d34261651..81097b5ecc 100644 --- a/lib/process-services/src/lib/services/people-process.service.ts +++ b/lib/process-services/src/lib/services/people-process.service.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, from } from 'rxjs'; import { GroupModel } from '@alfresco/adf-core'; import { map } from 'rxjs/operators'; @@ -26,6 +26,8 @@ import { TaskActionsApi, UsersApi, ActivitiGroupsApi, UserProfileApi, UserRepres providedIn: 'root' }) export class PeopleProcessService { + private readonly apiService = inject(AlfrescoApiService); + private _taskActionsApi: TaskActionsApi; get taskActionsApi(): TaskActionsApi { this._taskActionsApi = this._taskActionsApi ?? new TaskActionsApi(this.apiService.getInstance()); @@ -50,8 +52,6 @@ export class PeopleProcessService { return this._profileApi; } - constructor(private apiService: AlfrescoApiService) {} - /** * Gets information about the current user. * diff --git a/lib/process-services/src/lib/task-list/components/attach-form/attach-form.component.ts b/lib/process-services/src/lib/task-list/components/attach-form/attach-form.component.ts index 3fd46fb9b2..80f02fbeab 100644 --- a/lib/process-services/src/lib/task-list/components/attach-form/attach-form.component.ts +++ b/lib/process-services/src/lib/task-list/components/attach-form/attach-form.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, inject } from '@angular/core'; import { Form } from '../../models/form.model'; import { TaskListService } from '../../services/tasklist.service'; import { ReactiveFormsModule, UntypedFormControl, Validators } from '@angular/forms'; @@ -36,6 +36,10 @@ import { FormComponent } from '../../../form'; styleUrls: ['./attach-form.component.scss'] }) export class AttachFormComponent implements OnInit, OnChanges { + private readonly taskService = inject(TaskListService); + private readonly modelService = inject(ModelService); + private readonly taskFormService = inject(TaskFormService); + /** Id of the task. */ @Input({ required: true }) taskId: any; @@ -64,8 +68,6 @@ export class AttachFormComponent implements OnInit, OnChanges { attachFormControl: UntypedFormControl; - constructor(private taskService: TaskListService, private modelService: ModelService, private taskFormService: TaskFormService) {} - ngOnInit() { this.attachFormControl = new UntypedFormControl('', Validators.required); this.attachFormControl.valueChanges.subscribe((currentValue) => { diff --git a/lib/process-services/src/lib/task-list/components/checklist/checklist.component.ts b/lib/process-services/src/lib/task-list/components/checklist/checklist.component.ts index c3c9b1a32f..76615f283a 100644 --- a/lib/process-services/src/lib/task-list/components/checklist/checklist.component.ts +++ b/lib/process-services/src/lib/task-list/components/checklist/checklist.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild, inject } from '@angular/core'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { TaskListService } from '../../services/tasklist.service'; import { TaskRepresentation } from '@alfresco/js-api'; @@ -45,6 +45,9 @@ import { FormsModule } from '@angular/forms'; styleUrls: ['./checklist.component.scss'] }) export class ChecklistComponent implements OnChanges { + private readonly taskListService = inject(TaskListService); + private readonly dialog = inject(MatDialog); + /** * The id of the parent task to which subtasks are attached. */ @@ -81,8 +84,6 @@ export class ChecklistComponent implements OnChanges { checklist: TaskRepresentation[] = []; - constructor(private taskListService: TaskListService, private dialog: MatDialog) {} - ngOnChanges(changes: SimpleChanges) { const taskId = changes['taskId']; if (taskId?.currentValue) { diff --git a/lib/process-services/src/lib/task-list/components/start-task/start-task.component.spec.ts b/lib/process-services/src/lib/task-list/components/start-task/start-task.component.spec.ts index 46cfdbae14..c9e168e788 100644 --- a/lib/process-services/src/lib/task-list/components/start-task/start-task.component.spec.ts +++ b/lib/process-services/src/lib/task-list/components/start-task/start-task.component.spec.ts @@ -153,10 +153,10 @@ describe('StartTaskComponent', () => { }) ); const successSpy = spyOn(component.success, 'emit'); + component.taskDetailsModel = new TaskRepresentation(taskDetailsMock); component.taskForm.controls['name'].setValue('fakeName'); component.taskForm.controls['formKey'].setValue(1204); component.appId = 42; - component.taskDetailsModel = new TaskRepresentation(taskDetailsMock); fixture.detectChanges(); const createTaskButton = element.querySelector<HTMLElement>('#button-start'); createTaskButton.click(); @@ -259,11 +259,11 @@ describe('StartTaskComponent', () => { it('should not assign task when no assignee is selected', () => { const successSpy = spyOn(component.success, 'emit'); + component.taskDetailsModel = new TaskRepresentation(taskDetailsMock); component.taskForm.controls['name'].setValue('fakeName'); component.taskForm.controls['formKey'].setValue(1204); component.appId = 42; component.assigneeId = null; - component.taskDetailsModel = new TaskRepresentation(taskDetailsMock); fixture.detectChanges(); const createTaskButton = element.querySelector<HTMLElement>('#button-start'); createTaskButton.click(); diff --git a/lib/process-services/src/lib/task-list/components/start-task/start-task.component.ts b/lib/process-services/src/lib/task-list/components/start-task/start-task.component.ts index 94b51cc1e0..ad8ca0e36d 100644 --- a/lib/process-services/src/lib/task-list/components/start-task/start-task.component.ts +++ b/lib/process-services/src/lib/task-list/components/start-task/start-task.component.ts @@ -64,6 +64,9 @@ const MAX_LENGTH = 255; encapsulation: ViewEncapsulation.None }) export class StartTaskComponent implements OnInit { + private readonly taskService = inject(TaskListService); + private readonly formBuilder = inject(UntypedFormBuilder); + /** (required) The id of the app. */ @Input({ required: true }) appId: number; @@ -95,8 +98,6 @@ export class StartTaskComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); - constructor(private taskService: TaskListService, private formBuilder: UntypedFormBuilder) {} - ngOnInit() { if (this.name) { this.taskDetailsModel.name = this.name; diff --git a/lib/process-services/src/lib/task-list/components/task-details/task-details.component.ts b/lib/process-services/src/lib/task-list/components/task-details/task-details.component.ts index 56029b2d61..f06d022d0a 100644 --- a/lib/process-services/src/lib/task-list/components/task-details/task-details.component.ts +++ b/lib/process-services/src/lib/task-list/components/task-details/task-details.component.ts @@ -38,7 +38,8 @@ import { SimpleChanges, TemplateRef, ViewChild, - ViewEncapsulation + ViewEncapsulation, + inject } from '@angular/core'; import { MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { Observable, Observer, of } from 'rxjs'; @@ -88,6 +89,12 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; encapsulation: ViewEncapsulation.None }) export class TaskDetailsComponent implements OnInit, OnChanges { + private readonly taskListService = inject(TaskListService); + private readonly peopleProcessService = inject(PeopleProcessService); + private readonly cardViewUpdateService = inject(CardViewUpdateService); + private readonly dialog = inject(MatDialog); + private readonly destroyRef = inject(DestroyRef); + @ViewChild('errorDialog') errorDialog: TemplateRef<any>; @@ -209,14 +216,6 @@ export class TaskDetailsComponent implements OnInit, OnChanges { private peopleSearchObserver: Observer<LightUserRepresentation[]>; - constructor( - private readonly taskListService: TaskListService, - private readonly peopleProcessService: PeopleProcessService, - private readonly cardViewUpdateService: CardViewUpdateService, - private readonly dialog: MatDialog, - private readonly destroyRef: DestroyRef - ) {} - ngOnInit() { this.peopleSearch = new Observable<LightUserRepresentation[]>((observer) => (this.peopleSearchObserver = observer)).pipe(share()); diff --git a/lib/process-services/src/lib/task-list/components/task-filters/task-filters.component.ts b/lib/process-services/src/lib/task-list/components/task-filters/task-filters.component.ts index 4c6635299f..1c3203c0d2 100644 --- a/lib/process-services/src/lib/task-list/components/task-filters/task-filters.component.ts +++ b/lib/process-services/src/lib/task-list/components/task-filters/task-filters.component.ts @@ -38,6 +38,12 @@ import { MatIconModule } from '@angular/material/icon'; encapsulation: ViewEncapsulation.None }) export class TaskFiltersComponent implements OnInit, OnChanges { + private readonly taskFilterService = inject(TaskFilterService); + private readonly taskListService = inject(TaskListService); + private readonly appsProcessService = inject(AppsProcessService); + private readonly router = inject(Router); + private readonly activatedRoute = inject(ActivatedRoute); + /** * Parameters to use for the task filter. If there is no match then * the default filter (the first one the list) is selected. @@ -83,14 +89,6 @@ export class TaskFiltersComponent implements OnInit, OnChanges { private readonly destroyRef = inject(DestroyRef); - constructor( - private taskFilterService: TaskFilterService, - private taskListService: TaskListService, - private appsProcessService: AppsProcessService, - private router: Router, - private activatedRoute: ActivatedRoute - ) {} - ngOnInit() { this.iconsMDL = new IconModel(); this.router.events diff --git a/lib/process-services/src/lib/task-list/components/task-form/claim-task.directive.ts b/lib/process-services/src/lib/task-list/components/task-form/claim-task.directive.ts index e281802569..b403432362 100644 --- a/lib/process-services/src/lib/task-list/components/task-form/claim-task.directive.ts +++ b/lib/process-services/src/lib/task-list/components/task-form/claim-task.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, Input, Output, EventEmitter, HostListener, OnInit } from '@angular/core'; +import { Directive, Input, Output, EventEmitter, HostListener, OnInit, inject } from '@angular/core'; import { TaskListService } from '../../services/tasklist.service'; @Directive({ @@ -23,6 +23,8 @@ import { TaskListService } from '../../services/tasklist.service'; selector: '[adf-claim-task]' }) export class ClaimTaskDirective implements OnInit { + private readonly taskListService = inject(TaskListService); + /** (Required) The id of the task. */ @Input() taskId: string; @@ -37,8 +39,6 @@ export class ClaimTaskDirective implements OnInit { invalidParams: string[] = []; - constructor(private taskListService: TaskListService) {} - @HostListener('click') onClick() { try { diff --git a/lib/process-services/src/lib/task-list/components/task-form/task-form.component.ts b/lib/process-services/src/lib/task-list/components/task-form/task-form.component.ts index 3035fa8f21..3acf925a25 100644 --- a/lib/process-services/src/lib/task-list/components/task-form/task-form.component.ts +++ b/lib/process-services/src/lib/task-list/components/task-form/task-form.component.ts @@ -153,9 +153,9 @@ export class TaskFormComponent implements OnInit, OnChanges { loading: boolean = false; internalReadOnlyForm: boolean = false; - private taskListService = inject(TaskListService); - private peopleProcessService = inject(PeopleProcessService); - private translationService = inject(TranslationService); + private readonly taskListService = inject(TaskListService); + private readonly peopleProcessService = inject(PeopleProcessService); + private readonly translationService = inject(TranslationService); ngOnInit() { this.peopleProcessService.getCurrentUserInfo().subscribe((user) => { diff --git a/lib/process-services/src/lib/task-list/components/task-form/unclaim-task.directive.ts b/lib/process-services/src/lib/task-list/components/task-form/unclaim-task.directive.ts index 88d99c1ad2..e91c893a36 100644 --- a/lib/process-services/src/lib/task-list/components/task-form/unclaim-task.directive.ts +++ b/lib/process-services/src/lib/task-list/components/task-form/unclaim-task.directive.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Directive, HostListener, Input, Output, EventEmitter, OnInit } from '@angular/core'; +import { Directive, HostListener, Input, Output, EventEmitter, OnInit, inject } from '@angular/core'; import { TaskListService } from '../../services/tasklist.service'; @Directive({ @@ -23,6 +23,8 @@ import { TaskListService } from '../../services/tasklist.service'; selector: '[adf-unclaim-task]' }) export class UnclaimTaskDirective implements OnInit { + private readonly taskListService = inject(TaskListService); + /** (Required) The id of the task. */ @Input() taskId: string; @@ -37,8 +39,6 @@ export class UnclaimTaskDirective implements OnInit { invalidParams: string[] = []; - constructor(private taskListService: TaskListService) {} - @HostListener('click') onClick() { try { diff --git a/lib/process-services/src/lib/task-list/components/task-header/task-header.component.ts b/lib/process-services/src/lib/task-list/components/task-header/task-header.component.ts index 59d13653ff..195c54f9b7 100644 --- a/lib/process-services/src/lib/task-list/components/task-header/task-header.component.ts +++ b/lib/process-services/src/lib/task-list/components/task-header/task-header.component.ts @@ -51,6 +51,11 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; encapsulation: ViewEncapsulation.None }) export class TaskHeaderComponent implements OnChanges, OnInit { + private readonly peopleProcessService = inject(PeopleProcessService); + private readonly translationService = inject(TranslationService); + private readonly appConfig = inject(AppConfigService); + private readonly cardViewUpdateService = inject(CardViewUpdateService); + /** The name of the form. */ @Input() formName: string = null; @@ -94,12 +99,7 @@ export class TaskHeaderComponent implements OnChanges, OnInit { private readonly destroyRef = inject(DestroyRef); - constructor( - private peopleProcessService: PeopleProcessService, - private translationService: TranslationService, - private readonly appConfig: AppConfigService, - private readonly cardViewUpdateService: CardViewUpdateService - ) { + constructor() { this.dateFormat = this.appConfig.get('dateValues.defaultDateFormat'); this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale'); } diff --git a/lib/process-services/src/lib/task-list/components/task-list/task-list.component.spec.ts b/lib/process-services/src/lib/task-list/components/task-list/task-list.component.spec.ts index 8587b46bdb..ab96337775 100644 --- a/lib/process-services/src/lib/task-list/components/task-list/task-list.component.spec.ts +++ b/lib/process-services/src/lib/task-list/components/task-list/task-list.component.spec.ts @@ -749,7 +749,7 @@ describe('Task List: Custom EmptyTemplateComponent', () => { class TaskListContextMenuComponent implements OnInit { @Output() contextAction = new EventEmitter<any>(); - private performAction$ = new Subject<any>(); + private readonly performAction$ = new Subject<any>(); ngOnInit() { this.performContextActions(); diff --git a/lib/process-services/src/lib/task-list/components/task-list/task-list.component.ts b/lib/process-services/src/lib/task-list/components/task-list/task-list.component.ts index eb5430224d..4cc22c70a1 100644 --- a/lib/process-services/src/lib/task-list/components/task-list/task-list.component.ts +++ b/lib/process-services/src/lib/task-list/components/task-list/task-list.component.ts @@ -16,7 +16,6 @@ */ import { - AppConfigService, CustomEmptyContentTemplateDirective, CustomLoadingContentTemplateDirective, DataCellEvent, @@ -73,6 +72,9 @@ export const PRESET_KEY = 'adf-task-list.presets'; styleUrls: ['./task-list.component.css'] }) export class TaskListComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent, OnInit { + private readonly taskListService = inject(TaskListService); + private readonly userPreferences = inject(UserPreferencesService); + @ContentChild(CustomEmptyContentTemplateDirective) customEmptyContent: CustomEmptyContentTemplateDirective; @@ -226,8 +228,8 @@ export class TaskListComponent extends DataTableSchema implements OnChanges, Aft private readonly destroyRef = inject(DestroyRef); - constructor(private taskListService: TaskListService, appConfigService: AppConfigService, private userPreferences: UserPreferencesService) { - super(appConfigService, PRESET_KEY, taskPresetsDefaultModel); + constructor() { + super(PRESET_KEY, taskPresetsDefaultModel); this.pagination = new BehaviorSubject<PaginationModel>({ maxItems: this.size, diff --git a/lib/process-services/src/lib/task-list/models/task-details.event.ts b/lib/process-services/src/lib/task-list/models/task-details.event.ts index d8df9ca0c7..ce1f79fb5e 100644 --- a/lib/process-services/src/lib/task-list/models/task-details.event.ts +++ b/lib/process-services/src/lib/task-list/models/task-details.event.ts @@ -18,7 +18,7 @@ import { TaskRepresentation } from '@alfresco/js-api'; export class TaskDetailsEvent { - private _value: TaskRepresentation; + private readonly _value: TaskRepresentation; private _defaultPrevented: boolean = false; get value(): TaskRepresentation { diff --git a/lib/stories/.storybook/main.ts b/lib/stories/.storybook/main.ts index 1962bc6d5d..2157142e11 100644 --- a/lib/stories/.storybook/main.ts +++ b/lib/stories/.storybook/main.ts @@ -26,6 +26,9 @@ const config: StorybookConfig = { config.performance = { hints: false }; } return config; + }, + core: { + disableTelemetry: true } }; diff --git a/package-lock.json b/package-lock.json index 35b4274b85..46ffe7126e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,9 +52,9 @@ "@angular-devkit/build-angular": "19.2.19", "@angular-devkit/core": "19.2.19", "@angular-devkit/schematics": "19.2.19", - "@angular-eslint/eslint-plugin": "19.3.0", - "@angular-eslint/eslint-plugin-template": "19.3.0", - "@angular-eslint/template-parser": "19.3.0", + "@angular-eslint/eslint-plugin": "19.8.1", + "@angular-eslint/eslint-plugin-template": "19.8.1", + "@angular-eslint/template-parser": "19.8.1", "@angular/compiler-cli": "19.2.18", "@chromatic-com/storybook": "4.1.3", "@nx/angular": "22.1.3", @@ -201,6 +201,7 @@ "integrity": "sha512-uIxi6Vzss6+ycljVhkyPUPWa20w8qxJL9lEn0h6+sX/fhM8Djt0FHIuTQjoX58EoMaQ/1jrXaRaGimkbaFcG9A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "2.3.0", "@angular-devkit/architect": "0.1902.19", @@ -922,6 +923,7 @@ "integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -1001,6 +1003,7 @@ "integrity": "sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", @@ -1084,6 +1087,7 @@ "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ajv": "8.17.1", "ajv-formats": "3.0.1", @@ -1146,21 +1150,21 @@ } }, "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-19.3.0.tgz", - "integrity": "sha512-63Zci4pvnUR1iSkikFlNbShF1tO5HOarYd8fvNfmOZwFfZ/1T3j3bCy9YbE+aM5SYrWqPaPP/OcwZ3wJ8WNvqA==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-19.8.1.tgz", + "integrity": "sha512-WXi1YbSs7SIQo48u+fCcc5Nt14/T4QzYQPLZUnjtsUXPgQG7ZoahhcGf7PPQ+n0V3pSopHOlSHwqK+tSsYK87A==", "dev": true, "license": "MIT" }, "node_modules/@angular-eslint/eslint-plugin": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-19.3.0.tgz", - "integrity": "sha512-nBLslLI20KnVbqlfNW7GcnI9R6cYCvRGjOE2QYhzxM316ciAQ62tvQuXP9ZVnRBLSKDAVnMeC0eTq9O4ysrxrQ==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-19.8.1.tgz", + "integrity": "sha512-wZEBMPwD2TRhifG751hcj137EMIEaFmsxRB2EI+vfINCgPnFGSGGOHXqi8aInn9fXqHs7VbXkAzXYdBsvy1m4Q==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "19.3.0", - "@angular-eslint/utils": "19.3.0" + "@angular-eslint/bundled-angular-compiler": "19.8.1", + "@angular-eslint/utils": "19.8.1" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -1169,18 +1173,19 @@ } }, "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-19.3.0.tgz", - "integrity": "sha512-WyouppTpOYut+wvv13wlqqZ8EHoDrCZxNfGKuEUYK1BPmQlTB8EIZfQH4iR1rFVS28Rw+XRIiXo1x3oC0SOfnA==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-19.8.1.tgz", + "integrity": "sha512-0ZVQldndLrDfB0tzFe/uIwvkUcakw8qGxvkEU0l7kSbv/ngNQ/qrkRi7P64otB15inIDUNZI2jtmVat52dqSfQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "19.3.0", - "@angular-eslint/utils": "19.3.0", + "@angular-eslint/bundled-angular-compiler": "19.8.1", + "@angular-eslint/utils": "19.8.1", "aria-query": "5.3.2", "axobject-query": "4.1.0" }, "peerDependencies": { + "@angular-eslint/template-parser": "19.8.1", "@typescript-eslint/types": "^7.11.0 || ^8.0.0", "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", @@ -1188,13 +1193,14 @@ } }, "node_modules/@angular-eslint/template-parser": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-19.3.0.tgz", - "integrity": "sha512-VxMNgsHXMWbbmZeBuBX5i8pzsSSEaoACVpaE+j8Muk60Am4Mxc0PytJm4n3znBSvI3B7Kq2+vStSRYPkOER4lA==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-19.8.1.tgz", + "integrity": "sha512-pQiOg+se1AU/ncMlnJ9V6xYnMQ84qI1BGWuJpbU6A99VTXJg90scg0+T7DWmKssR1YjP5qmmBtrZfKsHEcLW/A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@angular-eslint/bundled-angular-compiler": "19.3.0", + "@angular-eslint/bundled-angular-compiler": "19.8.1", "eslint-scope": "^8.0.2" }, "peerDependencies": { @@ -1203,13 +1209,13 @@ } }, "node_modules/@angular-eslint/utils": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.3.0.tgz", - "integrity": "sha512-ovvbQh96FIJfepHqLCMdKFkPXr3EbcvYc9kMj9hZyIxs/9/VxwPH7x25mMs4VsL6rXVgH2FgG5kR38UZlcTNNw==", + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.8.1.tgz", + "integrity": "sha512-gVDKYWmAjeTPtaYmddT/HS03fCebXJtrk8G1MouQIviZbHqLjap6TbVlzlkBigRzaF0WnFnrDduQslkJzEdceA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "19.3.0" + "@angular-eslint/bundled-angular-compiler": "19.8.1" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -1222,6 +1228,7 @@ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-19.2.18.tgz", "integrity": "sha512-c76x1t+OiSstPsvJdHmV8Q4taF+8SxWKqiY750fOjpd01it4jJbU6YQqIroC6Xie7154zZIxOTHH2uTj+nm5qA==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -1239,6 +1246,7 @@ "integrity": "sha512-SFzQ1bRkNFiOVu+aaz+9INmts7tDUrsHLEr9HmARXr9qk5UmR8prlw39p2u+Bvi6/lCiJ18TZMQQl9mGyr63lg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "2.3.0", "@angular-devkit/architect": "0.1902.19", @@ -1324,6 +1332,7 @@ "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.2.19.tgz", "integrity": "sha512-PCpJagurPBqciqcq4Z8+3OtKLb7rSl4w/qBJoIMua8CgnrjvA1i+SWawhdtfI1zlY8FSwhzLwXV0CmWWfFzQPg==", "license": "MIT", + "peer": true, "dependencies": { "parse5": "^7.1.2", "tslib": "^2.3.0" @@ -1339,6 +1348,7 @@ "resolved": "https://registry.npmjs.org/@angular/common/-/common-19.2.18.tgz", "integrity": "sha512-CrV02Omzw/QtfjlEVXVPJVXipdx83NuA+qSASZYrxrhKFusUZyK3P/Zznqg+wiAeNDbedQwMUVqoAARHf0xQrw==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -1355,6 +1365,7 @@ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-19.2.18.tgz", "integrity": "sha512-3MscvODxRVxc3Cs0ZlHI5Pk5rEvE80otfvxZTMksOZuPlv1B+S8MjWfc3X3jk9SbyUEzODBEH55iCaBHD48V3g==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -1368,6 +1379,7 @@ "integrity": "sha512-N4TMtLfImJIoMaRL6mx7885UBeQidywptHH6ACZj71Ar6++DBc1mMlcwuvbeJCd3r3y8MQ5nLv5PZSN/tHr13w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "7.26.9", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -1444,6 +1456,7 @@ "resolved": "https://registry.npmjs.org/@angular/core/-/core-19.2.18.tgz", "integrity": "sha512-+QRrf0Igt8ccUWXHA+7doK5W6ODyhHdqVyblSlcQ8OciwkzIIGGEYNZom5OZyWMh+oI54lcSeyV2O3xaDepSrQ==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -1460,6 +1473,7 @@ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.2.18.tgz", "integrity": "sha512-pe40934jWhoS7DyGl7jyZdoj1gvBgur2t1zrJD+csEkTitYnW14+La2Pv6SW1pNX5nIzFsgsS9Nex1KcH5S6Tw==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -1478,6 +1492,7 @@ "resolved": "https://registry.npmjs.org/@angular/material/-/material-19.2.19.tgz", "integrity": "sha512-auIE6JUzTIA3LyYklh9J/T7u64crmphxUBgAa0zcOMDog6SYfwbNe9YeLQqua5ek4OUAOdK/BHHfVl5W5iaUoQ==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -1509,6 +1524,7 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.2.18.tgz", "integrity": "sha512-eahtsHPyXTYLARs9YOlXhnXGgzw0wcyOcDkBvNWK/3lA0NHIgIHmQgXAmBo+cJ+g9skiEQTD2OmSrrwbFKWJkw==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -1531,6 +1547,7 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-19.2.18.tgz", "integrity": "sha512-wqDtK2yVN5VDqVeOSOfqELdu40fyoIDknBGSxA27CEXzFVdMWJyIpuvUi+GMa+9eGjlS+1uVVBaRwxmnuvHj+A==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -1567,6 +1584,7 @@ "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.13.1.tgz", "integrity": "sha512-HaAt62h3jNUXpJ1v5HNgUiCzPP1c5zc2Q/FeTb2cTk/v09YlhoqKKHQFJI7St50VCJ5q8JVIc03I5bRcBrQxsg==", "license": "MIT", + "peer": true, "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@wry/caches": "^1.0.0", @@ -1687,6 +1705,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", @@ -3621,6 +3640,7 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -3841,7 +3861,8 @@ "version": "4.0.19", "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.19.tgz", "integrity": "sha512-VYHtPnZt/Zd/ATbW3rtexWpBnHUohUrQOHff/2JBhsVgxOrksAxJnLAO43Q1ayLJBJUUwNVo+RU0sx0aaysZfg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@cspell/dict-dart": { "version": "2.3.2", @@ -3961,13 +3982,15 @@ "version": "4.0.14", "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.14.tgz", "integrity": "sha512-2bf7n+kS92g+cMKV0wr9o/Oq9n8JzU7CcrB96gIh2GHgnF+0xDOqO2W/1KeFAqOfqosoOVE48t+4dnEMkkoJ2Q==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@cspell/dict-html-symbol-entities": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.5.tgz", "integrity": "sha512-429alTD4cE0FIwpMucvSN35Ld87HCyuM8mF731KU5Rm4Je2SG6hmVx7nkBsLyrmH3sQukTcr1GaiZsiEg8svPA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@cspell/dict-java": { "version": "5.0.12", @@ -4138,7 +4161,8 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.2.3.tgz", "integrity": "sha512-zXh1wYsNljQZfWWdSPYwQhpwiuW0KPW1dSd8idjMRvSD0aSvWWHoWlrMsmZeRl4qM4QCEAjua8+cjflm41cQBg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@cspell/dict-vue": { "version": "3.0.5", @@ -4322,6 +4346,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -4365,6 +4390,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -7656,6 +7682,7 @@ "integrity": "sha512-8PFQxtmXc6ukBC4CqGIoc96M2Ly9WVwCPu4Ffvt+K/SB6rGbeFeZoYAwREV1zGNMJ5v5ly6+AHIEOBxNuSnzSg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@module-federation/bridge-react-webpack-plugin": "0.21.6", "@module-federation/cli": "0.21.6", @@ -7992,6 +8019,7 @@ "integrity": "sha512-GzqNytPPnQVejl78XFFvtLyj3XNgPu6rSac2EbQkCxV3uzAD3kFQ7SW0VuwXWbQeRTUgt/4Nl/o+BrJXy14lsw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@module-federation/runtime": "0.22.1", "@module-federation/webpack-bundler-runtime": "0.22.1" @@ -8145,6 +8173,7 @@ "integrity": "sha512-fnP+ZOZTFeBGiTAnxve+axGmiYn2D60h86nUISXjXClK3LUY1krUfPgf6MaD4YDJ4i51OGXZWPekeMe16pkd8Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@module-federation/runtime": "0.21.6", "@module-federation/webpack-bundler-runtime": "0.21.6" @@ -9902,8 +9931,7 @@ "optional": true, "os": [ "darwin" - ], - "peer": true + ] }, "node_modules/@nx/cypress/node_modules/@nx/nx-darwin-x64": { "version": "21.6.10", @@ -9917,8 +9945,7 @@ "optional": true, "os": [ "darwin" - ], - "peer": true + ] }, "node_modules/@nx/cypress/node_modules/@nx/nx-freebsd-x64": { "version": "21.6.10", @@ -9932,8 +9959,7 @@ "optional": true, "os": [ "freebsd" - ], - "peer": true + ] }, "node_modules/@nx/cypress/node_modules/@nx/nx-linux-arm-gnueabihf": { "version": "21.6.10", @@ -9947,8 +9973,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@nx/cypress/node_modules/@nx/nx-linux-arm64-gnu": { "version": "21.6.10", @@ -9962,8 +9987,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@nx/cypress/node_modules/@nx/nx-linux-arm64-musl": { "version": "21.6.10", @@ -9977,8 +10001,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@nx/cypress/node_modules/@nx/nx-linux-x64-gnu": { "version": "21.6.10", @@ -9992,8 +10015,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@nx/cypress/node_modules/@nx/nx-linux-x64-musl": { "version": "21.6.10", @@ -10007,8 +10029,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@nx/cypress/node_modules/@nx/nx-win32-arm64-msvc": { "version": "21.6.10", @@ -10022,8 +10043,7 @@ "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@nx/cypress/node_modules/@nx/nx-win32-x64-msvc": { "version": "21.6.10", @@ -10037,8 +10057,7 @@ "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@nx/cypress/node_modules/@nx/workspace": { "version": "20.8.4", @@ -10452,7 +10471,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", "@yarnpkg/lockfile": "^1.1.0", @@ -10525,7 +10543,6 @@ "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/diff-sequences": "30.0.1", "@jest/get-type": "30.1.0", @@ -10583,7 +10600,6 @@ "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", @@ -10599,7 +10615,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -10612,8 +10627,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@nx/cypress/node_modules/restore-cursor": { "version": "3.1.0", @@ -11208,6 +11222,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", "@yarnpkg/lockfile": "^1.1.0", @@ -12549,8 +12564,7 @@ "optional": true, "os": [ "darwin" - ], - "peer": true + ] }, "node_modules/@nx/storybook/node_modules/@nx/nx-darwin-x64": { "version": "21.6.10", @@ -12564,8 +12578,7 @@ "optional": true, "os": [ "darwin" - ], - "peer": true + ] }, "node_modules/@nx/storybook/node_modules/@nx/nx-freebsd-x64": { "version": "21.6.10", @@ -12579,8 +12592,7 @@ "optional": true, "os": [ "freebsd" - ], - "peer": true + ] }, "node_modules/@nx/storybook/node_modules/@nx/nx-linux-arm-gnueabihf": { "version": "21.6.10", @@ -12594,8 +12606,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@nx/storybook/node_modules/@nx/nx-linux-arm64-gnu": { "version": "21.6.10", @@ -12609,8 +12620,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@nx/storybook/node_modules/@nx/nx-linux-arm64-musl": { "version": "21.6.10", @@ -12624,8 +12634,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@nx/storybook/node_modules/@nx/nx-linux-x64-gnu": { "version": "21.6.10", @@ -12639,8 +12648,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@nx/storybook/node_modules/@nx/nx-linux-x64-musl": { "version": "21.6.10", @@ -12654,8 +12662,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@nx/storybook/node_modules/@nx/nx-win32-arm64-msvc": { "version": "21.6.10", @@ -12669,8 +12676,7 @@ "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@nx/storybook/node_modules/@nx/nx-win32-x64-msvc": { "version": "21.6.10", @@ -12684,8 +12690,7 @@ "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@nx/storybook/node_modules/@nx/workspace": { "version": "20.8.4", @@ -13099,7 +13104,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", "@yarnpkg/lockfile": "^1.1.0", @@ -13172,7 +13176,6 @@ "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/diff-sequences": "30.0.1", "@jest/get-type": "30.1.0", @@ -13230,7 +13233,6 @@ "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", @@ -13246,7 +13248,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -13259,8 +13260,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@nx/storybook/node_modules/restore-cursor": { "version": "3.1.0", @@ -13932,6 +13932,7 @@ "integrity": "sha512-pOxtKWUfvf0oD8Geqs8D89Q2xpstRTaSY+F6Ut/Wd0GnEjUjO32SS1ymAM6WggGPHDZN4qpNrd5cfIxQmAbRLg==", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", "@yarnpkg/lockfile": "^1.1.0", @@ -14589,6 +14590,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", "@yarnpkg/lockfile": "^1.1.0", @@ -15917,6 +15919,7 @@ "integrity": "sha512-GUiTRTz6+gbfM2g3ixXqrvPSeHmyAFu/qHEZZjbYFeDtZhpy1gVaVAHiZfaaIIm+vRlNi7JmULWFZQFKwpQB9Q==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@module-federation/runtime-tools": "0.22.0", "@rspack/binding": "1.7.3", @@ -16237,6 +16240,7 @@ "integrity": "sha512-q1xbQYLG/JR0P0/jma3sUUWubw/6859WC5Y/+l2xGEvIqtoMKBYBzN4Nrud8rdLVEFfIDNEIbKQ4Rwr/JemO3g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@angular-devkit/core": "19.2.7", "@angular-devkit/schematics": "19.2.7", @@ -16531,6 +16535,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -16818,6 +16823,7 @@ "integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -16931,7 +16937,6 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -16952,7 +16957,6 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -16963,7 +16967,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -16977,7 +16980,6 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -16988,7 +16990,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -17003,8 +17004,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@testing-library/jest-dom": { "version": "6.9.1", @@ -17109,8 +17109,7 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -17242,6 +17241,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -17268,6 +17268,7 @@ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", "license": "MIT", + "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -17414,6 +17415,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~5.26.4" } @@ -17924,6 +17926,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -18263,6 +18266,7 @@ "integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -18380,6 +18384,7 @@ "integrity": "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.53.1", @@ -19254,6 +19259,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -19369,6 +19375,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -20367,6 +20374,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -20697,6 +20705,7 @@ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz", "integrity": "sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==", "license": "MIT", + "peer": true, "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -20719,6 +20728,7 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "license": "MIT", + "peer": true, "dependencies": { "readdirp": "^4.0.1" }, @@ -22860,6 +22870,7 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -23105,7 +23116,6 @@ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -23241,8 +23251,7 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dom-converter": { "version": "0.2.0", @@ -23463,6 +23472,7 @@ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "iconv-lite": "^0.6.2" } @@ -23911,6 +23921,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -23967,6 +23978,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -25443,6 +25455,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -26168,6 +26181,7 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==", "license": "MIT", + "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -26192,6 +26206,7 @@ "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-6.0.6.tgz", "integrity": "sha512-zgfER9s+ftkGKUZgc0xbx8T7/HMO4AV5/YuYiFc+AtgcO5T0v8AxYYNQ+ltzuzDZgNkYJaFspm5MMYLjQzrkmw==", "license": "MIT", + "peer": true, "engines": { "node": ">=20" }, @@ -27832,7 +27847,8 @@ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.13.0.tgz", "integrity": "sha512-vsYjfh7lyqvZX5QgqKc4YH8phs7g96Z8bsdIFNEU3VqXhlHaq+vov/Fgn/sr6MiUczdZkyXRC3TX369Ll4Nzbw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/jasmine-reporters": { "version": "2.5.2", @@ -27861,6 +27877,7 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -29603,6 +29620,7 @@ "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -32463,6 +32481,7 @@ "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@colors/colors": "1.5.0", "body-parser": "^1.19.0", @@ -33248,6 +33267,7 @@ "resolved": "https://registry.npmjs.org/less/-/less-4.2.2.tgz", "integrity": "sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -34213,7 +34233,6 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -34865,6 +34884,7 @@ "integrity": "sha512-dFuwFsDJMBSd1YtmLLcX5bNNUCQUlRqgf34aXA+79PmkOP+0eF8GP2949wq3+jMjmFTNm80Oo8IUYiSLwklKCQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rollup/plugin-json": "^6.1.0", "@rollup/wasm-node": "^4.24.0", @@ -35273,6 +35293,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", "@yarnpkg/lockfile": "^1.1.0", @@ -36638,6 +36659,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", @@ -37350,6 +37372,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -38653,6 +38676,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -39294,8 +39318,7 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "devOptional": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/schema-utils": { "version": "4.3.3", @@ -40123,6 +40146,7 @@ "integrity": "sha512-fIQnFtpksRRgHR1CO1onGX3djaog4qsW/c5U8arqYTkUEr2TaWpn05mIJDOBoPJFlOdqFrB4Ttv0PZJxV7avhw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1", @@ -40557,6 +40581,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", @@ -41001,6 +41026,7 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -42047,6 +42073,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -42189,7 +42216,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tsscmp": { "version": "1.0.6", @@ -42398,6 +42426,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -42797,6 +42826,7 @@ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -43287,6 +43317,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz", "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -43725,6 +43756,7 @@ "integrity": "sha512-khZGfAeJx6I8K9zKohEWWYN6KDlVw2DHownoe+6Vtwj1LP9WFgegXnVMSkZ/dBEBtXFwrkkydsaPFlB7f8wU2A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-html-community": "0.0.8", "html-entities": "^2.1.0", @@ -44230,6 +44262,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -44494,7 +44527,8 @@ "version": "0.15.0", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.0.tgz", "integrity": "sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA==", - "license": "MIT" + "license": "MIT", + "peer": true } } } diff --git a/package.json b/package.json index 7a1ef4692b..d7a1a4986c 100644 --- a/package.json +++ b/package.json @@ -81,9 +81,9 @@ "@angular-devkit/build-angular": "19.2.19", "@angular-devkit/core": "19.2.19", "@angular-devkit/schematics": "19.2.19", - "@angular-eslint/eslint-plugin": "19.3.0", - "@angular-eslint/eslint-plugin-template": "19.3.0", - "@angular-eslint/template-parser": "19.3.0", + "@angular-eslint/eslint-plugin": "19.8.1", + "@angular-eslint/eslint-plugin-template": "19.8.1", + "@angular-eslint/template-parser": "19.8.1", "@angular/compiler-cli": "19.2.18", "@chromatic-com/storybook": "4.1.3", "@nx/angular": "22.1.3",