From 3078387325dc0932b5ea72fcd88121643706576c Mon Sep 17 00:00:00 2001 From: dominikiwanekhyland <141320833+dominikiwanekhyland@users.noreply.github.com> Date: Tue, 19 Nov 2024 11:54:00 +0100 Subject: [PATCH] [ACS-8959] Introduce new `takeUntilDestroyed` operator where possible (#10388) --- .../lib/aspect-list/aspect-list.component.ts | 20 +++--- .../lib/breadcrumb/breadcrumb.component.ts | 27 ++++--- .../categories-management.component.ts | 28 +++++--- .../content-metadata.component.ts | 22 +++--- .../property-groups-translator.service.ts | 37 ++++++---- .../content-node-selector-panel.component.ts | 72 ++++++++++++------- .../content-node-selector.component.ts | 32 ++++----- .../content-node-share.dialog.ts | 16 +---- .../content-node-share.directive.ts | 19 ++--- .../src/lib/dialogs/library/library.dialog.ts | 30 ++++---- .../document-list.component.spec.ts | 25 +++++-- .../components/document-list.component.ts | 29 ++++---- .../filter-header/filter-header.component.ts | 38 ++++++---- .../library-name-column.component.ts | 25 ++++--- .../library-role-column.component.ts | 27 +++---- .../library-status-column.component.ts | 16 ++--- .../name-column/name-column.component.ts | 25 ++++--- .../components/pop-over.directive.ts | 25 ++++--- .../search-check-list.component.ts | 18 ++--- ...earch-chip-autocomplete-input.component.ts | 35 +++++---- .../components/search-control.component.ts | 19 ++--- .../search-date-range-tabbed.component.ts | 20 +++--- .../search-date-range.component.ts | 26 +++---- .../search-datetime-range.component.ts | 35 +++++---- ...rch-filter-autocomplete-chips.component.ts | 17 ++--- .../search-facet-tabbed-content.component.ts | 18 ++--- .../search-filter-chips.component.ts | 16 ++--- .../search-filter-container.component.ts | 23 +++--- .../search-logical-filter.component.ts | 18 ++--- .../search-properties.component.ts | 16 ++--- .../search-slider/search-slider.component.ts | 17 ++--- .../search-text/search-text.component.ts | 19 ++--- .../lib/search/components/search.component.ts | 22 ++---- .../services/search-facet-filters.service.ts | 22 +++--- .../tag/tag-actions/tag-actions.component.ts | 26 ++++--- .../lib/tag/tag-list/tag-list.component.ts | 17 ++--- .../tag-node-list/tag-node-list.component.ts | 17 ++--- .../tags-creator/tags-creator.component.ts | 25 +++++-- .../components/base-upload/upload-base.ts | 16 ++--- .../file-uploading-dialog.component.ts | 46 ++++++------ .../version-manager/version-list.component.ts | 35 +++++---- .../version-upload.component.ts | 17 ++--- .../components/alfresco-viewer.component.ts | 22 +++--- .../feature-override-indicator.component.ts | 15 ++-- .../lib/components/flags/flags.component.ts | 26 +++---- .../src/lib/directives/features.directive.ts | 20 ++---- .../lib/directives/not-features.directive.ts | 18 ++--- .../lib/components/shell/shell.component.ts | 29 ++++---- .../card-view/components/base-card-view.ts | 15 ++-- .../card-view-dateitem.component.ts | 14 ++-- .../card-view-selectitem.component.ts | 3 +- .../select-filter-input.component.ts | 32 +++++---- .../card-view-textitem.component.ts | 24 ++++--- .../boolean-cell/boolean-cell.component.ts | 4 +- .../columns-selector.component.ts | 23 +++--- .../datatable-cell.component.ts | 25 ++++--- .../datatable/datatable.component.ts | 25 +++---- .../icon-cell/icon-cell.component.ts | 4 +- .../resizable/resizable.directive.spec.ts | 7 +- .../resizable/resizable.directive.ts | 16 ++--- .../resizable/resize-handle.directive.ts | 7 +- .../lib/dialogs/dialog/dialog.component.ts | 19 ++--- .../infinite-select-scroll.directive.ts | 16 ++--- .../dynamic-chip-list.component.ts | 4 -- .../widgets/date-time/date-time.widget.ts | 25 +++---- .../components/widgets/date/date.widget.ts | 18 ++--- .../identity-user-info.component.ts | 12 +--- .../sidenav-layout.component.ts | 30 ++++---- .../login/components/login/login.component.ts | 17 ++--- .../notification-history.component.ts | 27 ++++--- .../infinite-pagination.component.ts | 20 +++--- .../lib/pagination/pagination.component.ts | 30 ++++---- .../src/lib/pipes/decimal-number.pipe.spec.ts | 8 ++- lib/core/src/lib/pipes/decimal-number.pipe.ts | 17 ++--- lib/core/src/lib/pipes/localized-date.pipe.ts | 8 +-- lib/core/src/lib/pipes/time-ago.pipe.spec.ts | 6 +- lib/core/src/lib/pipes/time-ago.pipe.ts | 32 +++------ .../search-text-input.component.ts | 31 +++++--- .../search-text/search-trigger.directive.ts | 18 +++-- .../pdf-viewer/pdf-viewer.component.ts | 3 - .../viewer-render/viewer-render.component.ts | 24 ++++--- .../lib/viewer/components/viewer.component.ts | 18 ++--- .../directives/viewer-extension.directive.ts | 16 ++--- .../analytics-report-parameters.component.ts | 20 +++--- .../form/components/form-cloud.component.ts | 60 ++++++++-------- ...attach-file-cloud-widget.component.spec.ts | 65 +++++++++-------- .../widgets/date/date-cloud.widget.ts | 28 +++----- .../widgets/dropdown/dropdown-cloud.widget.ts | 25 +++---- .../widgets/group/group-cloud.widget.ts | 24 +++---- .../widgets/people/people-cloud.widget.ts | 23 +++--- .../radio-buttons-cloud.widget.ts | 11 ++- .../form-cloud-spinner.service.spec.ts | 11 +-- .../spinner/form-cloud-spinner.service.ts | 9 ++- .../group/components/group-cloud.component.ts | 34 +++++---- .../components/people-cloud.component.ts | 36 +++++----- .../directives/cancel-process.directive.ts | 16 ++--- .../edit-process-filter-cloud.component.ts | 28 ++++---- .../process-filters-cloud.component.ts | 34 +++++---- .../process-header-cloud.component.ts | 34 +++++---- .../process-list-cloud.component.ts | 64 ++++++++--------- .../start-process-cloud.component.ts | 28 ++++---- .../components/start-task-cloud.component.ts | 20 +++--- .../base-task-filters-cloud.component.ts | 12 +--- .../base-edit-task-filter-cloud.component.ts | 26 +++---- ...dit-service-task-filter-cloud.component.ts | 8 +-- .../edit-task-filter-cloud.component.ts | 14 ++-- .../service-task-filters-cloud.component.ts | 15 +++- .../task-filters-cloud.component.ts | 20 ++++-- .../components/task-form-cloud.component.ts | 31 ++++---- .../components/task-header-cloud.component.ts | 50 +++++++------ .../base-task-list-cloud.component.ts | 52 ++++++++------ .../service-task-list-cloud.component.ts | 18 ++--- .../components/task-list-cloud.component.ts | 24 +++---- .../src/lib/app-list/apps-list.component.ts | 21 +++--- .../src/lib/form/form.component.ts | 50 ++++++------- .../form/start-form/start-form.component.ts | 7 +- .../attach-file-widget-dialog.component.ts | 27 +++---- .../attach-file-widget.component.ts | 35 +++++---- .../form/widgets/dropdown/dropdown.widget.ts | 26 +++---- .../process-filters.component.ts | 18 ++--- .../process-instance-tasks.component.ts | 21 +++--- .../start-process/start-process.component.ts | 42 +++++++---- .../no-task-detail-template.directive.spec.ts | 2 +- .../start-task/start-task.component.ts | 29 ++++---- .../task-details/task-details.component.ts | 28 ++++---- .../task-filters/task-filters.component.ts | 18 ++--- .../task-header/task-header.component.ts | 3 +- .../task-list/task-list.component.ts | 50 +++++++------ 128 files changed, 1452 insertions(+), 1546 deletions(-) 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 be229dff94..6656fc3856 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 @@ -15,10 +15,10 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { NodesApiService } from '../common/services/nodes-api.service'; -import { Observable, Subject, zip } from 'rxjs'; -import { concatMap, map, takeUntil, tap } from 'rxjs/operators'; +import { Observable, zip } from 'rxjs'; +import { concatMap, map, tap } from 'rxjs/operators'; import { AspectListService } from './services/aspect-list.service'; import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox'; import { AspectEntry } from '@alfresco/js-api'; @@ -27,6 +27,7 @@ import { MatExpansionModule } from '@angular/material/expansion'; import { MatTableModule } from '@angular/material/table'; import { TranslateModule } from '@ngx-translate/core'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-aspect-list', @@ -36,7 +37,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; styleUrls: ['./aspect-list.component.scss'], encapsulation: ViewEncapsulation.None }) -export class AspectListComponent implements OnInit, OnDestroy { +export class AspectListComponent implements OnInit { /** Node Id of the node that we want to update */ @Input() nodeId: string = ''; @@ -60,15 +61,10 @@ export class AspectListComponent implements OnInit, OnDestroy { notDisplayedAspects: string[] = []; hasEqualAspect: boolean = true; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private aspectListService: AspectListService, private nodeApiService: NodesApiService) {} - ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - ngOnInit(): void { let aspects$: Observable; if (this.nodeId) { @@ -89,10 +85,10 @@ export class AspectListComponent implements OnInit, OnDestroy { this.updateCounter.emit(this.nodeAspects.length); }), concatMap(() => this.aspectListService.getAspects()), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ); } else { - aspects$ = this.aspectListService.getAspects().pipe(takeUntil(this.onDestroy$)); + aspects$ = this.aspectListService.getAspects().pipe(takeUntilDestroyed(this.destroyRef)); } this.aspects$ = aspects$.pipe(map((aspects) => aspects.filter((aspect) => !this.excludedAspects.includes(aspect.entry.id)))); } diff --git a/lib/content-services/src/lib/breadcrumb/breadcrumb.component.ts b/lib/content-services/src/lib/breadcrumb/breadcrumb.component.ts index 949d679a7b..6676f6bd37 100644 --- a/lib/content-services/src/lib/breadcrumb/breadcrumb.component.ts +++ b/lib/content-services/src/lib/breadcrumb/breadcrumb.component.ts @@ -15,15 +15,25 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild, ViewEncapsulation, OnDestroy } from '@angular/core'; +import { + Component, + DestroyRef, + EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; import { MatSelect, MatSelectModule } from '@angular/material/select'; import { Node, PathElement } from '@alfresco/js-api'; import { DocumentListComponent } from '../document-list/components/document-list.component'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; import { MatIconModule } from '@angular/material/icon'; import { TranslateModule } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-breadcrumb', @@ -34,7 +44,7 @@ import { TranslateModule } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-breadcrumb' } }) -export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy { +export class BreadcrumbComponent implements OnInit, OnChanges { /** Active node, builds UI based on folderNode.path.elements collection. */ @Input() folderNode: Node = null; @@ -86,7 +96,7 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy { route: PathElement[] = []; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); get hasRoot(): boolean { return !!this.root; @@ -104,7 +114,7 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy { this.transform = this.transform ? this.transform : null; if (this.target) { - this.target.$folderNode.pipe(takeUntil(this.onDestroy$)).subscribe((folderNode: Node) => { + this.target.$folderNode.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((folderNode: Node) => { this.folderNode = folderNode; this.recalculateNodes(); }); @@ -201,9 +211,4 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy { } } } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 9e6513b499..fe448d8308 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 @@ -16,10 +16,22 @@ */ import { Category } from '@alfresco/js-api'; -import { Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; +import { + Component, + DestroyRef, + ElementRef, + EventEmitter, + inject, + Input, + OnDestroy, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; import { EMPTY, Observable, Subject, timer } from 'rxjs'; -import { debounce, first, map, takeUntil, tap } from 'rxjs/operators'; +import { debounce, first, map, tap } from 'rxjs/operators'; import { CategoriesManagementMode } from './categories-management-mode'; import { CategoryService } from '../services/category.service'; import { CommonModule } from '@angular/common'; @@ -30,6 +42,7 @@ import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatListModule } from '@angular/material/list'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; interface CategoryNameControlErrors { duplicatedExistingCategory?: boolean; @@ -66,7 +79,6 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy { private existingCategoryLoaded$ = new Subject(); private cancelExistingCategoriesLoading$ = new Subject(); - private onDestroy$ = new Subject(); private _categoryNameControl = new FormControl( '', [this.validateIfNotAlreadyAdded.bind(this), this.validateEmptyCategory, Validators.required], @@ -147,6 +159,8 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy { @ViewChild('categoryNameInput') private categoryNameInputElement: ElementRef; + private readonly destroyRef = inject(DestroyRef); + constructor(private categoryService: CategoryService) {} ngOnInit() { @@ -162,11 +176,11 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy { this.cancelExistingCategoriesLoading$.next(); }), debounce((name: string) => (name ? timer(300) : EMPTY)), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((name: string) => this.onNameControlValueChange(name)); - this.categoryNameControl.statusChanges.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.setCategoryNameControlErrorMessageKey()); + this.categoryNameControl.statusChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.setCategoryNameControlErrorMessageKey()); this.setCategoryNameControlErrorMessageKey(); @@ -178,7 +192,7 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy { this._categoryNameControl.removeValidators(Validators.required); this.categories.forEach((category) => this.initialCategories.push(category)); if (this.classifiableChanged) { - this.classifiableChanged.pipe(takeUntil(this.onDestroy$)).subscribe(() => { + this.classifiableChanged.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { this.categories = []; this.categoryNameControlVisible = false; this.categoryNameControlVisibleChange.emit(false); @@ -188,8 +202,6 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy { } ngOnDestroy() { - this.onDestroy$.next(); - this.onDestroy$.complete(); this.cancelExistingCategoriesLoading$.next(); this.cancelExistingCategoriesLoading$.complete(); } 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 3264294291..5cc86ba60e 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 @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, Input, OnChanges, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core'; import { Category, CategoryEntry, CategoryLinkBody, CategoryPaging, Node, TagBody, TagEntry, TagPaging } from '@alfresco/js-api'; import { forkJoin, Observable, of, Subject, zip } from 'rxjs'; import { @@ -28,8 +28,8 @@ import { UpdateNotification } from '@alfresco/adf-core'; import { ContentMetadataService } from '../../services/content-metadata.service'; -import { CardViewGroup, PresetConfig, ContentMetadataCustomPanel, ContentMetadataPanel } from '../../interfaces/content-metadata.interfaces'; -import { catchError, debounceTime, map, takeUntil } from 'rxjs/operators'; +import { CardViewGroup, ContentMetadataCustomPanel, ContentMetadataPanel, PresetConfig } from '../../interfaces/content-metadata.interfaces'; +import { catchError, debounceTime, map } from 'rxjs/operators'; import { CardViewContentUpdateService } from '../../../common/services/card-view-content-update.service'; import { NodesApiService } from '../../../common/services/nodes-api.service'; import { TagsCreatorMode } from '../../../tag/tags-creator/tags-creator-mode'; @@ -49,6 +49,7 @@ import { CategoriesManagementComponent } from '../../../category'; import { DynamicExtensionComponent } from '@alfresco/adf-extensions'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { TagsCreatorComponent } from '../../../tag'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const DEFAULT_SEPARATOR = ', '; @@ -80,9 +81,7 @@ enum DefaultPanels { host: { class: 'adf-content-metadata' }, encapsulation: ViewEncapsulation.None }) -export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy { - protected onDestroy$ = new Subject(); - +export class ContentMetadataComponent implements OnChanges, OnInit { /** (required) The node entity to fetch metadata about */ @Input() node: Node; @@ -167,6 +166,8 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy { panelTitle: '' }; + private readonly destroyRef = inject(DestroyRef); + constructor( private contentMetadataService: ContentMetadataService, private cardViewContentUpdateService: CardViewContentUpdateService, @@ -185,14 +186,14 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy { ngOnInit() { this.cardViewContentUpdateService.itemUpdated$ - .pipe(debounceTime(500), takeUntil(this.onDestroy$)) + .pipe(debounceTime(500), takeUntilDestroyed(this.destroyRef)) .subscribe((updatedNode: UpdateNotification) => { this.hasMetadataChanged = true; this.targetProperty = updatedNode.target; this.updateChanges(updatedNode.changed); }); - this.cardViewContentUpdateService.updatedAspect$.pipe(debounceTime(500), takeUntil(this.onDestroy$)).subscribe((node) => { + this.cardViewContentUpdateService.updatedAspect$.pipe(debounceTime(500), takeUntilDestroyed(this.destroyRef)).subscribe((node) => { this.node.aspectNames = node?.aspectNames; this.loadProperties(node); }); @@ -274,11 +275,6 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy { } } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - updateChanges(updatedNodeChanges) { Object.keys(updatedNodeChanges).map((propertyGroup: string) => { if (typeof updatedNodeChanges[propertyGroup] === 'object') { 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 6f3e07da7f..fc80722ca3 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 @@ -15,27 +15,27 @@ * limitations under the License. */ -import { inject, Injectable } from '@angular/core'; +import { inject, Injectable, Injector, runInInjectionContext } from '@angular/core'; import { - CardViewItemProperties, - CardViewItem, - CardViewTextItemModel, + AppConfigService, CardViewBoolItemModel, CardViewDateItemModel, - CardViewSelectItemModel, CardViewDatetimeItemModel, - CardViewIntItemModel, - CardViewLongItemModel, CardViewFloatItemModel, - MultiValuePipe, - AppConfigService, + CardViewIntItemModel, + CardViewItem, + CardViewItemProperties, + CardViewLongItemModel, + CardViewSelectItemModel, + CardViewTextItemModel, DecimalNumberPipe, LogService, + MultiValuePipe, UserPreferencesService } from '@alfresco/adf-core'; -import { Property, CardViewGroup, OrganisedPropertyGroup } from '../interfaces/content-metadata.interfaces'; +import { CardViewGroup, OrganisedPropertyGroup, Property } from '../interfaces/content-metadata.interfaces'; import { of } from 'rxjs'; -import { Definition, Constraint, Property as PropertyBase } from '@alfresco/js-api'; +import { Constraint, Definition, Property as PropertyBase } from '@alfresco/js-api'; const D_TEXT = 'd:text'; const D_MLTEXT = 'd:mltext'; @@ -59,6 +59,8 @@ export class PropertyGroupTranslatorService { valueSeparator: string; + private readonly injector = inject(Injector); + constructor() { this.valueSeparator = this.appConfig.get('content-metadata.multi-value-pipe-separator'); } @@ -156,10 +158,7 @@ export class PropertyGroupTranslatorService { cardViewItemProperty = new CardViewFloatItemModel( Object.assign(propertyDefinition, { multivalued: isMultiValued, - pipes: [ - { pipe: new DecimalNumberPipe(this.userPreferenceService, this.appConfig) }, - { pipe: new MultiValuePipe(), params: [this.valueSeparator] } - ] + pipes: [{ pipe: this.getDecimalNumberPipe() }, { pipe: new MultiValuePipe(), params: [this.valueSeparator] }] }) ); break; @@ -218,4 +217,12 @@ export class PropertyGroupTranslatorService { private isEmpty(value: any): boolean { return value === undefined || value === null || value === ''; } + + private getDecimalNumberPipe(): DecimalNumberPipe { + let decimalNumberPipe: DecimalNumberPipe; + runInInjectionContext(this.injector, () => { + decimalNumberPipe = new DecimalNumberPipe(this.userPreferenceService, this.appConfig); + }); + return decimalNumberPipe; + } } 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 defd4222df..1dbe2d7949 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 @@ -15,32 +15,56 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation, OnDestroy } from '@angular/core'; import { + Component, + DestroyRef, + EventEmitter, + inject, + Input, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { + CustomEmptyContentTemplateDirective, + DataColumnComponent, + DataColumnListComponent, + DataSorting, HighlightDirective, - UserPreferencesService, - UserPreferenceValues, InfinitePaginationComponent, PaginatedComponent, - DataSorting, ShowHeaderMode, - ToolbarTitleComponent, ToolbarComponent, - DataColumnListComponent, - DataColumnComponent, - CustomEmptyContentTemplateDirective + ToolbarTitleComponent, + UserPreferencesService, + UserPreferenceValues } from '@alfresco/adf-core'; -import { NodesApiService, UploadService, FileUploadCompleteEvent, FileUploadDeleteEvent, SitesService } from '../../common'; +import { + FileUploadCompleteEvent, + FileUploadDeleteEvent, + NodesApiService, + SitesService, + UploadService +} from '../../common'; import { ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; -import { Node, NodePaging, Pagination, SiteEntry, SitePaging, NodeEntry, SearchRequest, RequestScope } from '@alfresco/js-api'; +import { + Node, + NodeEntry, + NodePaging, + Pagination, + RequestScope, + SearchRequest, + SiteEntry, + SitePaging +} from '@alfresco/js-api'; import { DocumentListComponent } from '../../document-list/components/document-list.component'; import { RowFilter } from '../../document-list/data/row-filter.model'; import { ImageResolver } from '../../document-list/data/image-resolver.model'; import { CustomResourcesService } from '../../document-list/services/custom-resources.service'; import { ShareDataRow } from '../../document-list/data/share-data-row.model'; import { NodeEntryEvent } from '../../document-list/components/node.event'; -import { debounceTime, takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; +import { debounceTime } from 'rxjs/operators'; import { ContentNodeSelectorPanelService } from './content-node-selector-panel.service'; import { CommonModule } from '@angular/common'; import { MatFormFieldModule } from '@angular/material/form-field'; @@ -55,6 +79,7 @@ import { NameLocationCellComponent } from '../name-location-cell/name-location-c import { DropdownBreadcrumbComponent } from '../../breadcrumb/dropdown-breadcrumb.component'; import { SearchQueryBuilderService } from '../../search/services/search-query-builder.service'; import { SearchPanelComponent } from '../../search/components/search-panel/search-panel.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export type ValidationFunction = (entry: Node) => boolean; @@ -92,7 +117,7 @@ export const defaultValidation = () => true; host: { class: 'adf-content-node-selector-panel' }, providers: [SearchQueryBuilderService] }) -export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { +export class ContentNodeSelectorPanelComponent implements OnInit { // eslint-disable-next-line @typescript-eslint/naming-convention DEFAULT_PAGINATION: Pagination = new Pagination({ maxItems: 25, @@ -303,7 +328,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { searchPanelExpanded: boolean = false; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor( private customResourcesService: CustomResourcesService, @@ -333,13 +358,13 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { } ngOnInit() { - this.searchInput.valueChanges.pipe(debounceTime(this.debounceSearch), takeUntil(this.onDestroy$)).subscribe((searchValue: string) => { + this.searchInput.valueChanges.pipe(debounceTime(this.debounceSearch), takeUntilDestroyed(this.destroyRef)).subscribe((searchValue: string) => { this.searchTerm = searchValue; this.queryBuilderService.userQuery = searchValue.length > 0 ? `${searchValue}*` : searchValue; this.queryBuilderService.update(); }); - this.queryBuilderService.updated.pipe(takeUntil(this.onDestroy$)).subscribe((searchRequest) => { + this.queryBuilderService.updated.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((searchRequest) => { if (searchRequest) { this.hasValidQuery = true; this.prepareDialogForNewSearch(searchRequest); @@ -351,7 +376,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { } }); - this.queryBuilderService.executed.pipe(takeUntil(this.onDestroy$)).subscribe((results: NodePaging) => { + this.queryBuilderService.executed.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((results: NodePaging) => { if (this.hasValidQuery) { this.showSearchResults(results); } @@ -359,7 +384,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { this.userPreferencesService .select(UserPreferenceValues.PaginationSize) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((pagSize) => (this.pageSize = pagSize)); this.target = this.documentList; @@ -380,16 +405,11 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { this.resetPagination(); this.setSearchScopeToNodes(); - this.documentList.$folderNode.pipe(takeUntil(this.onDestroy$)).subscribe((currentNode: Node) => { + this.documentList.$folderNode.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((currentNode: Node) => { this.currentFolder.emit(currentNode); }); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - toggleSearchPanel() { this.searchPanelExpanded = !this.searchPanelExpanded; } @@ -400,7 +420,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { private onFileUploadEvent() { this.uploadService.fileUploadComplete - .pipe(debounceTime(500), takeUntil(this.onDestroy$)) + .pipe(debounceTime(500), takeUntilDestroyed(this.destroyRef)) .subscribe((fileUploadEvent: FileUploadCompleteEvent) => { this.currentUploadBatch.push(fileUploadEvent.data); if (!this.uploadService.isUploading()) { @@ -412,7 +432,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { } private onFileUploadDeletedEvent() { - this.uploadService.fileUploadDeleted.pipe(takeUntil(this.onDestroy$)).subscribe((deletedFileEvent: FileUploadDeleteEvent) => { + this.uploadService.fileUploadDeleted.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((deletedFileEvent: FileUploadDeleteEvent) => { this.documentList.unselectRowFromNodeId(deletedFileEvent.file.data.entry.id); this.documentList.reloadWithoutResettingSelection(); }); 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 5d3397c035..837b1fd737 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,9 +15,15 @@ * limitations under the License. */ -import { Component, Inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, Inject, OnInit, ViewEncapsulation } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; -import { TranslationService, NotificationService, ToolbarTitleComponent, ToolbarComponent, EmptyListComponent } from '@alfresco/adf-core'; +import { + EmptyListComponent, + NotificationService, + ToolbarComponent, + ToolbarTitleComponent, + TranslationService +} from '@alfresco/adf-core'; import { Node } from '@alfresco/js-api'; import { AllowableOperationsEnum } from '../common/models/allowable-operations.enum'; import { ContentService } from '../common/services/content.service'; @@ -26,8 +32,6 @@ import { ContentNodeSelectorComponentData } from './content-node-selector.compon import { NodeEntryEvent } from '../document-list/components/node.event'; import { NodeAction } from '../document-list/models/node-action.enum'; import { OverlayContainer } from '@angular/cdk/overlay'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; import { MatTabsModule } from '@angular/material/tabs'; import { TranslateModule } from '@ngx-translate/core'; @@ -39,6 +43,7 @@ import { FileUploadingDialogComponent } from '../upload/components/file-uploadin import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel/content-node-selector-panel.component'; import { UploadButtonComponent } from '../upload/components/upload-button.component'; import { MatButtonModule } from '@angular/material/button'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-content-node-selector', @@ -64,8 +69,7 @@ import { MatButtonModule } from '@angular/material/button'; styleUrls: ['./content-node-selector.component.scss'], encapsulation: ViewEncapsulation.None }) -export class ContentNodeSelectorComponent implements OnInit, OnDestroy { - private onDestroy$ = new Subject(); +export class ContentNodeSelectorComponent implements OnInit { title: string; action: NodeAction; @@ -81,6 +85,8 @@ export class ContentNodeSelectorComponent implements OnInit, OnDestroy { emptyFolderImageUrl: string = './assets/images/empty_doc_lib.svg'; breadcrumbFolderNode: Node; + private readonly destroyRef = inject(DestroyRef); + constructor( private translation: TranslationService, private contentService: ContentService, @@ -99,7 +105,7 @@ export class ContentNodeSelectorComponent implements OnInit, OnDestroy { ngOnInit() { this.dialog .keydownEvents() - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((event) => { if (event?.key === 'Escape') { event.preventDefault(); @@ -110,28 +116,22 @@ export class ContentNodeSelectorComponent implements OnInit, OnDestroy { this.dialog .backdropClick() - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.close(); }); this.dialog .afterOpened() - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.overlayContainer.getContainerElement().setAttribute('role', 'main'); }); - this.uploadService.fileUploadStarting.pipe(takeUntil(this.onDestroy$)).subscribe(() => { + this.uploadService.fileUploadStarting.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { this.uploadStarted = true; }); } - - ngOnDestroy() { - this.onDestroy$.next(); - this.onDestroy$.complete(); - } - close() { this.dialog.close(); this.overlayContainer.getContainerElement().setAttribute('role', 'region'); 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 041d6f3a2c..aa00520b1f 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,18 +15,17 @@ * limitations under the License. */ -import { Component, Inject, OnInit, ViewEncapsulation, ViewChild, OnDestroy } from '@angular/core'; +import { Component, Inject, OnInit, ViewChild, ViewEncapsulation } 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'; -import { Subject } from 'rxjs'; import { ContentService } from '../common/services/content.service'; import { SharedLinksApiService } from './services/shared-links-api.service'; import { SharedLinkBodyCreate } from '@alfresco/js-api'; import { ClipboardDirective, ConfirmDialogComponent } from '@alfresco/adf-core'; import { ContentNodeShareSettings } from './content-node-share.settings'; import { RenditionService } from '../common/services/rendition.service'; -import { format, add, endOfDay, isBefore } from 'date-fns'; +import { add, endOfDay, format, isBefore } from 'date-fns'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { MatIconModule } from '@angular/material/icon'; @@ -61,7 +60,7 @@ interface SharedDialogFormProps { host: { class: 'adf-share-dialog' }, encapsulation: ViewEncapsulation.None }) -export class ShareDialogComponent implements OnInit, OnDestroy { +export class ShareDialogComponent implements OnInit { private minDateValidator = (control: FormControl): any => isBefore(endOfDay(new Date(control.value)), this.minDate) ? { invalidDate: true } : null; @@ -80,9 +79,6 @@ export class ShareDialogComponent implements OnInit, OnDestroy { @ViewChild('slideToggleExpirationDate', { static: true }) slideToggleExpirationDate; - - private onDestroy$ = new Subject(); - constructor( private sharedLinksApiService: SharedLinksApiService, private dialogRef: MatDialogRef, @@ -121,12 +117,6 @@ export class ShareDialogComponent implements OnInit, OnDestroy { get time(): FormControl { return this.form.controls['time']; } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - onSlideShareChange(event: MatSlideToggleChange) { if (event.checked) { this.createSharedLinks(this.data.node.entry.id); 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 c438cc9b6a..0b85d252f5 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 @@ -15,21 +15,21 @@ * limitations under the License. */ -import { Directive, Input, HostListener, OnChanges, NgZone, OnDestroy } from '@angular/core'; +import { DestroyRef, Directive, HostListener, inject, Input, NgZone, OnChanges } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { NodeEntry, NodesApi } from '@alfresco/js-api'; import { ShareDialogComponent } from './content-node-share.dialog'; -import { Observable, from, Subject } from 'rxjs'; +import { from, Observable } from 'rxjs'; import { AlfrescoApiService } from '../services/alfresco-api.service'; -import { takeUntil } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Directive({ selector: '[adf-share]', standalone: true, exportAs: 'adfShare' }) -export class NodeSharedDirective implements OnChanges, OnDestroy { +export class NodeSharedDirective implements OnChanges { isFile: boolean = false; isShared: boolean = false; @@ -42,7 +42,6 @@ export class NodeSharedDirective implements OnChanges, OnDestroy { @Input() baseShareUrl: string; - private onDestroy$ = new Subject(); _nodesApi: NodesApi; get nodesApi(): NodesApi { @@ -50,13 +49,9 @@ export class NodeSharedDirective implements OnChanges, OnDestroy { return this._nodesApi; } + private readonly destroyRef = inject(DestroyRef); + constructor(private dialog: MatDialog, private zone: NgZone, private alfrescoApiService: AlfrescoApiService) {} - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - shareNode(nodeEntry: NodeEntry) { if (nodeEntry?.entry?.isFile) { // shared and favorite @@ -92,7 +87,7 @@ export class NodeSharedDirective implements OnChanges, OnDestroy { } ngOnChanges() { - this.zone.onStable.pipe(takeUntil(this.onDestroy$)).subscribe(() => { + this.zone.onStable.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { if (this.node?.entry) { this.isFile = this.node.entry.isFile; this.isShared = this.node.entry.properties ? this.node.entry.properties['qshare:sharedId'] : false; 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 ebae76c319..fc9e99a01e 100644 --- a/lib/content-services/src/lib/dialogs/library/library.dialog.ts +++ b/lib/content-services/src/lib/dialogs/library/library.dialog.ts @@ -15,21 +15,21 @@ * limitations under the License. */ -import { Observable, Subject } from 'rxjs'; -import { Component, OnInit, Output, EventEmitter, OnDestroy, ViewEncapsulation } from '@angular/core'; +import { Observable } from 'rxjs'; +import { Component, DestroyRef, EventEmitter, inject, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { - UntypedFormBuilder, - UntypedFormGroup, - Validators, - UntypedFormControl, AbstractControl, + FormsModule, ReactiveFormsModule, - FormsModule + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validators } from '@angular/forms'; import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { QueriesApi, SiteBodyCreate, SiteEntry, SitePaging } from '@alfresco/js-api'; import { NotificationService } from '@alfresco/adf-core'; -import { debounceTime, finalize, mergeMap, takeUntil } from 'rxjs/operators'; +import { debounceTime, finalize, mergeMap } from 'rxjs/operators'; import { SitesService } from '../../common/services/sites.service'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; @@ -39,6 +39,7 @@ import { AutoFocusDirective } from '../../directives'; import { MatRadioModule } from '@angular/material/radio'; import { MatButtonModule } from '@angular/material/button'; import { AlfrescoApiService } from '../../services'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-library-dialog', @@ -60,7 +61,7 @@ import { AlfrescoApiService } from '../../services'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-library-dialog' } }) -export class LibraryDialogComponent implements OnInit, OnDestroy { +export class LibraryDialogComponent implements OnInit { /** Emitted when an error occurs. */ @Output() error: EventEmitter = new EventEmitter(); @@ -73,8 +74,6 @@ export class LibraryDialogComponent implements OnInit, OnDestroy { @Output() success: EventEmitter = new EventEmitter(); - onDestroy$: Subject = new Subject(); - createTitle = 'LIBRARY.DIALOG.CREATE_TITLE'; libraryTitleExists = false; form: UntypedFormGroup; @@ -96,6 +95,8 @@ export class LibraryDialogComponent implements OnInit, OnDestroy { return this._queriesApi; } + private readonly destroyRef = inject(DestroyRef); + constructor( private alfrescoApiService: AlfrescoApiService, private sitesService: SitesService, @@ -126,7 +127,7 @@ export class LibraryDialogComponent implements OnInit, OnDestroy { (title) => this.checkLibraryNameExists(title), (title) => title ), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((title: string) => { if (!this.form.controls['id'].dirty && this.canGenerateId(title)) { @@ -136,11 +137,6 @@ export class LibraryDialogComponent implements OnInit, OnDestroy { }); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - get title(): string { const { title } = this.form.value; 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 317f11bd1a..e654dc3d2a 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 @@ -30,7 +30,16 @@ import { import { FavoritePaging, FavoritePagingList, Node, NodeEntry, NodePaging } from '@alfresco/js-api'; import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; -import { Component, CUSTOM_ELEMENTS_SCHEMA, QueryList, SimpleChange, SimpleChanges, ViewChild } from '@angular/core'; +import { + Component, + CUSTOM_ELEMENTS_SCHEMA, + Injector, + QueryList, + runInInjectionContext, + SimpleChange, + SimpleChanges, + ViewChild +} from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; import { By } from '@angular/platform-browser'; @@ -84,6 +93,7 @@ describe('DocumentList', () => { let spyFolder: any; let spyFolderNode: any; let authenticationService: AuthenticationService; + let injector: Injector; beforeEach(() => { TestBed.configureTestingModule({ @@ -106,6 +116,7 @@ describe('DocumentList', () => { contentService = TestBed.inject(ContentService); appConfigService = TestBed.inject(AppConfigService); authenticationService = TestBed.inject(AuthenticationService); + injector = TestBed.inject(Injector); spyFolder = spyOn(documentListService, 'getFolder').and.returnValue(of({ list: {} })); spyFolderNode = spyOn(documentListService, 'getFolderNode').and.returnValue(of(new NodeEntry({ entry: new Node() }))); @@ -140,7 +151,7 @@ describe('DocumentList', () => { spyOn(documentList, 'resetSelection').and.callThrough(); spyOn(documentList, 'reload').and.callThrough(); - documentList.ngOnDestroy(); + fixture.destroy(); documentListService.reload(); expect(documentList.resetSelection).not.toHaveBeenCalled(); @@ -158,7 +169,7 @@ describe('DocumentList', () => { it('should not reset selection after component is destroyed', () => { spyOn(documentList, 'resetSelection').and.callThrough(); - documentList.ngOnDestroy(); + fixture.destroy(); documentListService.resetSelection(); expect(documentList.resetSelection).not.toHaveBeenCalled(); @@ -1126,7 +1137,9 @@ describe('DocumentList', () => { it('should display [empty folder] template ', () => { fixture.detectChanges(); - documentList.dataTable = new DataTableComponent(null, null, matIconRegistryMock, domSanitizerMock); + runInInjectionContext(injector, () => { + documentList.dataTable = new DataTableComponent(null, null, matIconRegistryMock, domSanitizerMock); + }) expect(documentList.dataTable).toBeDefined(); expect(fixture.debugElement.query(By.css('adf-empty-list'))).not.toBeNull(); }); @@ -1145,7 +1158,9 @@ describe('DocumentList', () => { }); it('should empty folder NOT show the pagination', () => { - documentList.dataTable = new DataTableComponent(null, null, matIconRegistryMock, domSanitizerMock); + runInInjectionContext(injector, () => { + documentList.dataTable = new DataTableComponent(null, null, matIconRegistryMock, domSanitizerMock); + }) expect(documentList.isEmpty()).toBeTruthy(); expect(element.querySelector('alfresco-pagination')).toBe(null); 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 22930be07c..f089cab00c 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 @@ -51,12 +51,13 @@ import { AfterContentInit, Component, ContentChild, + DestroyRef, ElementRef, EventEmitter, HostListener, + inject, Input, OnChanges, - OnDestroy, OnInit, Output, SimpleChanges, @@ -65,7 +66,6 @@ import { } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { BehaviorSubject, of, Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { ContentService, NodesApiService } from '../../common'; import { FilterSearch } from '../../search'; import { RowFilter } from '../data/row-filter.model'; @@ -86,6 +86,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { MatIconModule } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const BYTES_TO_MB_CONVERSION_VALUE = 1048576; @@ -118,7 +119,7 @@ const BYTES_TO_MB_CONVERSION_VALUE = 1048576; encapsulation: ViewEncapsulation.None, host: { class: 'adf-document-list' } }) -export class DocumentListComponent extends DataTableSchema implements OnInit, OnChanges, OnDestroy, AfterContentInit, PaginatedComponent { +export class DocumentListComponent extends DataTableSchema implements OnInit, OnChanges, AfterContentInit, PaginatedComponent { static SINGLE_CLICK_NAVIGATION: string = 'click'; static DOUBLE_CLICK_NAVIGATION: string = 'dblclick'; @@ -444,7 +445,8 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On private rowMenuCache: { [key: string]: ContentActionModel[] } = {}; private loadingTimeout: any; - private onDestroy$ = new Subject(); + + private readonly destroyRef = inject(DestroyRef); private _nodesApi: NodesApi; get nodesApi(): NodesApi { @@ -466,13 +468,13 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On private dialog: MatDialog ) { super(appConfig, 'default', presetsDefaultModel); - this.nodeService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node) => { + this.nodeService.nodeUpdated.pipe(takeUntilDestroyed()).subscribe((node) => { this.dataTableService.rowUpdate.next({ id: node.id, obj: { entry: node } }); }); this.userPreferencesService .select(UserPreferenceValues.PaginationSize) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed()) .subscribe((pagSize) => { this.maxItems = this._pagination.maxItems = pagSize; }); @@ -534,7 +536,7 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On this.data.setImageResolver(this.imageResolver); } - this.contextActionHandler.pipe(takeUntil(this.onDestroy$)).subscribe((val) => this.contextActionCallback(val)); + this.contextActionHandler.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((val) => this.contextActionCallback(val)); this.enforceSingleClickNavigationForMobile(); if (this.filterValue && Object.keys(this.filterValue).length > 0) { @@ -544,19 +546,19 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On this.setPresetKey(this.columnsPresetKey); } - this.documentListService.reload$.pipe(takeUntil(this.onDestroy$)).subscribe(() => { + this.documentListService.reload$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { this.resetSelection(); this.reload(); }); - this.documentListService.resetSelection$.pipe(takeUntil(this.onDestroy$)).subscribe(() => { + this.documentListService.resetSelection$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { this.resetSelection(); }); } ngAfterContentInit() { if (this.columnList) { - this.columnList.columns.changes.pipe(takeUntil(this.onDestroy$)).subscribe(() => { + this.columnList.columns.changes.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { this.createColumns(); this.data.setColumns(this.columns); }); @@ -751,7 +753,7 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On const handlerSub = typeof action.handler === 'function' ? action.handler(node, this, action.permission) : of(true); if (typeof action.execute === 'function' && handlerSub) { - handlerSub.pipe(takeUntil(this.onDestroy$)).subscribe(() => action.execute(node)); + handlerSub.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => action.execute(node)); } } } @@ -1038,11 +1040,6 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On this._pagination.maxItems = this.maxItems; } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - private handleError(err: any) { if (err.message) { try { diff --git a/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.ts b/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.ts index 24250a4b67..9f596fc1f6 100644 --- a/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.ts +++ b/lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.ts @@ -15,15 +15,27 @@ * limitations under the License. */ -import { Component, Inject, OnInit, OnChanges, SimpleChanges, Input, Output, EventEmitter, OnDestroy } from '@angular/core'; -import { PaginationModel, DataSorting, HeaderFilterTemplateDirective } from '@alfresco/adf-core'; +import { + Component, + DestroyRef, + EventEmitter, + Inject, + inject, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges +} from '@angular/core'; +import { DataSorting, HeaderFilterTemplateDirective, PaginationModel } from '@alfresco/adf-core'; import { SearchHeaderQueryBuilderService } from '../../../search/services/search-header-query-builder.service'; import { FilterSearch } from './../../../search/models/filter-search.interface'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { ADF_DOCUMENT_PARENT_COMPONENT } from '../document-list.token'; import { CommonModule } from '@angular/common'; -import { SearchFilterContainerComponent } from '../../../search/components/search-filter-container/search-filter-container.component'; +import { + SearchFilterContainerComponent +} from '../../../search/components/search-filter-container/search-filter-container.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-filter-header', @@ -31,7 +43,7 @@ import { SearchFilterContainerComponent } from '../../../search/components/searc imports: [CommonModule, HeaderFilterTemplateDirective, SearchFilterContainerComponent], templateUrl: './filter-header.component.html' }) -export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy { +export class FilterHeaderComponent implements OnInit, OnChanges { /** (optional) Initial filter value to sort . */ @Input() value: any = {}; @@ -45,14 +57,15 @@ export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy { filterSelection: EventEmitter = new EventEmitter(); isFilterServiceActive: boolean; - private onDestroy$ = new Subject(); + + private readonly destroyRef = inject(DestroyRef); constructor(@Inject(ADF_DOCUMENT_PARENT_COMPONENT) private documentList: any, private searchFilterQueryBuilder: SearchHeaderQueryBuilderService) { this.isFilterServiceActive = this.searchFilterQueryBuilder.isFilterServiceActive(); } ngOnInit() { - this.searchFilterQueryBuilder.executed.pipe(takeUntil(this.onDestroy$)).subscribe((newNodePaging) => { + this.searchFilterQueryBuilder.executed.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((newNodePaging) => { this.documentList.node = newNodePaging; this.documentList.reload(); }); @@ -81,13 +94,13 @@ export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy { } initDataPagination() { - this.documentList.pagination.pipe(takeUntil(this.onDestroy$)).subscribe((newPagination: PaginationModel) => { + this.documentList.pagination.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((newPagination: PaginationModel) => { this.searchFilterQueryBuilder.setupCurrentPagination(newPagination.maxItems, newPagination.skipCount); }); } initDataSorting() { - this.documentList.sortingSubject.pipe(takeUntil(this.onDestroy$)).subscribe((sorting: DataSorting[]) => { + this.documentList.sortingSubject.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((sorting: DataSorting[]) => { this.searchFilterQueryBuilder.setSorting(sorting); }); } @@ -110,9 +123,4 @@ export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy { }); } } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 9f46d70c89..ed63f70978 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 @@ -15,15 +15,24 @@ * limitations under the License. */ -import { Component, ChangeDetectionStrategy, ViewEncapsulation, OnInit, Input, ElementRef, OnDestroy } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + DestroyRef, + ElementRef, + inject, + Input, + OnInit, + ViewEncapsulation +} from '@angular/core'; import { NodeEntry, Site } from '@alfresco/js-api'; import { ShareDataRow } from '../../data/share-data-row.model'; import { NodesApiService } from '../../../common/services/nodes-api.service'; -import { BehaviorSubject, Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { BehaviorSubject } from 'rxjs'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-library-name-column', @@ -54,7 +63,7 @@ import { TranslateModule } from '@ngx-translate/core'; class: 'adf-datatable-content-cell adf-datatable-link adf-library-name-column' } }) -export class LibraryNameColumnComponent implements OnInit, OnDestroy { +export class LibraryNameColumnComponent implements OnInit { @Input() context: any; @@ -62,14 +71,14 @@ export class LibraryNameColumnComponent implements OnInit, OnDestroy { displayText$ = new BehaviorSubject(''); node: NodeEntry; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private element: ElementRef, private nodesApiService: NodesApiService) {} ngOnInit() { this.updateValue(); - this.nodesApiService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node) => { + this.nodesApiService.nodeUpdated.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((node) => { const row: ShareDataRow = this.context.row; if (row) { const { entry } = row.node; @@ -121,8 +130,4 @@ export class LibraryNameColumnComponent implements OnInit, OnDestroy { return isDuplicate ? `${title} (${id})` : `${title}`; } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 18913e3850..f050ec1269 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 @@ -15,14 +15,22 @@ * limitations under the License. */ -import { Component, OnInit, Input, ChangeDetectionStrategy, ViewEncapsulation, OnDestroy } from '@angular/core'; -import { BehaviorSubject, Subject } from 'rxjs'; -import { SiteEntry, Site } from '@alfresco/js-api'; +import { + ChangeDetectionStrategy, + Component, + DestroyRef, + inject, + Input, + OnInit, + ViewEncapsulation +} from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; +import { Site, SiteEntry } from '@alfresco/js-api'; import { ShareDataRow } from '../../data/share-data-row.model'; -import { takeUntil } from 'rxjs/operators'; import { NodesApiService } from '../../../common/services/nodes-api.service'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-library-role-column', @@ -37,20 +45,20 @@ import { TranslateModule } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-library-role-column adf-datatable-content-cell' } }) -export class LibraryRoleColumnComponent implements OnInit, OnDestroy { +export class LibraryRoleColumnComponent implements OnInit { @Input() context: any; displayText$ = new BehaviorSubject(''); - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private nodesApiService: NodesApiService) {} ngOnInit() { this.updateValue(); - this.nodesApiService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node) => { + this.nodesApiService.nodeUpdated.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((node) => { const row: ShareDataRow = this.context.row; if (row) { const { entry } = row.node; @@ -86,9 +94,4 @@ export class LibraryRoleColumnComponent implements OnInit, OnDestroy { } } } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 e5ed8ffb15..3fa3ffae92 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 @@ -15,14 +15,14 @@ * limitations under the License. */ -import { Component, Input, OnInit, OnDestroy } from '@angular/core'; +import { Component, DestroyRef, inject, Input, OnInit } from '@angular/core'; import { NodesApiService } from '../../../common/services/nodes-api.service'; -import { BehaviorSubject, Subject } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; import { Site, SiteEntry } from '@alfresco/js-api'; import { ShareDataRow } from '../../data/share-data-row.model'; -import { takeUntil } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-library-status-column', @@ -35,20 +35,20 @@ import { TranslateModule } from '@ngx-translate/core'; `, host: { class: 'adf-library-status-column adf-datatable-content-cell' } }) -export class LibraryStatusColumnComponent implements OnInit, OnDestroy { +export class LibraryStatusColumnComponent implements OnInit { @Input() context: any; displayText$ = new BehaviorSubject(''); - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private nodesApiService: NodesApiService) {} ngOnInit() { this.updateValue(); - this.nodesApiService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node) => { + this.nodesApiService.nodeUpdated.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((node) => { const row: ShareDataRow = this.context.row; if (row) { const { entry } = row.node; @@ -83,8 +83,4 @@ export class LibraryStatusColumnComponent implements OnInit, OnDestroy { } } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 636db2aeb9..665a43d833 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 @@ -15,15 +15,24 @@ * limitations under the License. */ -import { Component, Input, OnInit, ChangeDetectionStrategy, ViewEncapsulation, ElementRef, OnDestroy } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + DestroyRef, + ElementRef, + inject, + Input, + OnInit, + ViewEncapsulation +} from '@angular/core'; import { NodeEntry } from '@alfresco/js-api'; -import { BehaviorSubject, Subject } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; import { NodesApiService } from '../../../common/services/nodes-api.service'; import { ShareDataRow } from '../../data/share-data-row.model'; -import { takeUntil } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { NodeNameTooltipPipe } from '../../../pipes/node-name-tooltip.pipe'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-name-column', @@ -52,7 +61,7 @@ import { NodeNameTooltipPipe } from '../../../pipes/node-name-tooltip.pipe'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-datatable-content-cell adf-datatable-link adf-name-column' } }) -export class NameColumnComponent implements OnInit, OnDestroy { +export class NameColumnComponent implements OnInit { @Input() context: any; @@ -62,14 +71,14 @@ export class NameColumnComponent implements OnInit, OnDestroy { displayText$ = new BehaviorSubject(''); node: NodeEntry; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private element: ElementRef, private nodesApiService: NodesApiService) {} ngOnInit() { this.updateValue(); - this.nodesApiService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node) => { + this.nodesApiService.nodeUpdated.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((node) => { const row: ShareDataRow = this.context.row; if (row) { const { entry } = row.node; @@ -102,8 +111,4 @@ export class NameColumnComponent implements OnInit, OnDestroy { ); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 76ef650ba2..3104b95f0e 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 @@ -15,12 +15,23 @@ * limitations under the License. */ -import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnDestroy, OnInit, TemplateRef, ViewContainerRef } from '@angular/core'; +import { + AfterViewInit, + DestroyRef, + Directive, + ElementRef, + HostListener, + inject, + Input, + OnDestroy, + OnInit, + TemplateRef, + ViewContainerRef +} from '@angular/core'; import { ConnectionPositionPair, Overlay, OverlayRef } from '@angular/cdk/overlay'; import { TemplatePortal } from '@angular/cdk/portal'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Directive({ selector: '[adf-pop-over]', @@ -38,11 +49,11 @@ export class PopOverDirective implements OnInit, OnDestroy, AfterViewInit { @Input() autofocusedElementSelector: string; private _open = false; - private destroy$ = new Subject(); private overlayRef!: OverlayRef; - private focusTrap: ConfigurableFocusTrap; + private readonly destroyRef = inject(DestroyRef); + constructor( private element: ElementRef, private overlay: Overlay, @@ -62,8 +73,6 @@ export class PopOverDirective implements OnInit, OnDestroy, AfterViewInit { ngOnDestroy(): void { this.element.nativeElement.removeEventListener('keydown', this.preventDefaultForEnter); this.detachOverlay(); - this.destroy$.next(undefined); - this.destroy$.complete(); } private createOverlay(): void { @@ -87,7 +96,7 @@ export class PopOverDirective implements OnInit, OnDestroy, AfterViewInit { this.overlayRef .backdropClick() - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.detachOverlay(); }); 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 9b0f8fc63b..26fdca5235 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 @@ -15,19 +15,20 @@ * limitations under the License. */ -import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox'; import { SearchWidget } from '../../models/search-widget.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; import { SearchFilterList } from '../../models/search-filter-list.model'; import { TranslationService } from '@alfresco/adf-core'; -import { ReplaySubject, Subject } from 'rxjs'; -import { map, takeUntil } from 'rxjs/operators'; +import { ReplaySubject } from 'rxjs'; +import { map } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface SearchListOption { name: string; @@ -44,7 +45,7 @@ export interface SearchListOption { encapsulation: ViewEncapsulation.None, host: { class: 'adf-search-check-list' } }) -export class SearchCheckListComponent implements SearchWidget, OnInit, OnDestroy { +export class SearchCheckListComponent implements SearchWidget, OnInit { id: string; settings?: SearchWidgetSettings; context?: SearchQueryBuilderService; @@ -56,7 +57,7 @@ export class SearchCheckListComponent implements SearchWidget, OnInit, OnDestroy enableChangeUpdate = true; displayValue$ = new ReplaySubject(1); - private readonly destroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private translationService: TranslationService) { this.options = new SearchFilterList(); @@ -84,7 +85,7 @@ export class SearchCheckListComponent implements SearchWidget, OnInit, OnDestroy .asObservable() .pipe( map((filtersQueries) => filtersQueries[this.id]), - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((filterQuery) => { if (filterQuery) { @@ -102,11 +103,6 @@ export class SearchCheckListComponent implements SearchWidget, OnInit, OnDestroy }); } - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - clear() { this.isActive = false; this.clearOptions(); diff --git a/lib/content-services/src/lib/search/components/search-chip-autocomplete-input/search-chip-autocomplete-input.component.ts b/lib/content-services/src/lib/search/components/search-chip-autocomplete-input/search-chip-autocomplete-input.component.ts index 2a3f3a24ed..16c5bd6355 100644 --- a/lib/content-services/src/lib/search/components/search-chip-autocomplete-input/search-chip-autocomplete-input.component.ts +++ b/lib/content-services/src/lib/search/components/search-chip-autocomplete-input/search-chip-autocomplete-input.component.ts @@ -17,28 +17,30 @@ import { Component, - ViewEncapsulation, + DestroyRef, ElementRef, - ViewChild, - OnInit, - OnDestroy, - Input, - Output, EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, SimpleChanges, - OnChanges + ViewChild, + ViewEncapsulation } from '@angular/core'; import { ENTER } from '@angular/cdk/keycodes'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips'; -import { EMPTY, Observable, Subject, timer } from 'rxjs'; -import { debounce, startWith, takeUntil, tap } from 'rxjs/operators'; +import { EMPTY, Observable, timer } from 'rxjs'; +import { debounce, startWith, tap } from 'rxjs/operators'; import { AutocompleteOption } from '../../models/autocomplete-option.interface'; import { CommonModule } from '@angular/common'; import { MatFormFieldModule } from '@angular/material/form-field'; import { TranslateModule } from '@ngx-translate/core'; import { MatIconModule } from '@angular/material/icon'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-search-chip-autocomplete-input', @@ -48,7 +50,7 @@ import { MatIconModule } from '@angular/material/icon'; styleUrls: ['./search-chip-autocomplete-input.component.scss'], encapsulation: ViewEncapsulation.None }) -export class SearchChipAutocompleteInputComponent implements OnInit, OnDestroy, OnChanges { +export class SearchChipAutocompleteInputComponent implements OnInit, OnChanges { @ViewChild('optionInput') optionInput: ElementRef; @@ -89,9 +91,11 @@ export class SearchChipAutocompleteInputComponent implements OnInit, OnDestroy, formCtrl = new FormControl(''); filteredOptions: AutocompleteOption[] = []; selectedOptions: AutocompleteOption[] = []; - private onDestroy$ = new Subject(); + private _activeAnyOption = false; + private readonly destroyRef = inject(DestroyRef); + set activeAnyOption(active: boolean) { this._activeAnyOption = active; } @@ -102,13 +106,13 @@ export class SearchChipAutocompleteInputComponent implements OnInit, OnDestroy, startWith(''), tap(() => (this.activeAnyOption = false)), debounce((value: string) => (value ? timer(300) : EMPTY)), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((value: string) => { this.filteredOptions = value ? this.filter(this.autocompleteOptions, value) : []; this.inputChanged.emit(value); }); - this.onReset$?.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.reset()); + this.onReset$?.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.reset()); this.selectedOptions = this.preselectedOptions ?? []; } @@ -121,11 +125,6 @@ export class SearchChipAutocompleteInputComponent implements OnInit, OnDestroy, } } - ngOnDestroy() { - this.onDestroy$.next(); - this.onDestroy$.complete(); - } - add(event: MatChipInputEvent) { if (!this._activeAnyOption) { let value = (event.value || '').trim(); 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 5ef36ec7ba..1bb6c44e9f 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 @@ -15,19 +15,18 @@ * limitations under the License. */ -import { AuthenticationService, ThumbnailService, SearchTextInputComponent, HighlightPipe } from '@alfresco/adf-core'; +import { AuthenticationService, HighlightPipe, SearchTextInputComponent, ThumbnailService } from '@alfresco/adf-core'; import { Component, + ContentChild, EventEmitter, Input, - OnDestroy, Output, QueryList, - ViewEncapsulation, + TemplateRef, ViewChild, ViewChildren, - TemplateRef, - ContentChild + ViewEncapsulation } from '@angular/core'; import { NodeEntry } from '@alfresco/js-api'; import { Subject } from 'rxjs'; @@ -47,7 +46,7 @@ import { TranslateModule } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-search-control' } }) -export class SearchControlComponent implements OnDestroy { +export class SearchControlComponent { /** Toggles highlighting of the search term in the results. */ @Input() highlight: boolean = false; @@ -111,19 +110,11 @@ export class SearchControlComponent implements OnDestroy { noSearchResultTemplate: TemplateRef = null; searchTerm: string = ''; - private onDestroy$ = new Subject(); - constructor(public authService: AuthenticationService, private thumbnailService: ThumbnailService) {} isNoSearchTemplatePresent(): boolean { return !!this.emptySearchTemplate; } - - ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - isLoggedIn(): boolean { return this.authService.isEcmLoggedIn(); } 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 37b870cbfc..3c8e214515 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 @@ -15,9 +15,9 @@ * limitations under the License. */ -import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; -import { ReplaySubject, Subject } from 'rxjs'; -import { map, takeUntil } from 'rxjs/operators'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { ReplaySubject } from 'rxjs'; +import { map } from 'rxjs/operators'; import { DateRangeType } from './search-date-range/date-range-type'; import { SearchDateRange } from './search-date-range/search-date-range'; import { SearchWidget } from '../../models/search-widget.interface'; @@ -30,6 +30,7 @@ import { CommonModule } from '@angular/common'; import { SearchFilterTabbedComponent } from '../search-filter-tabbed/search-filter-tabbed.component'; import { SearchDateRangeComponent } from './search-date-range/search-date-range.component'; import { SearchFilterTabDirective } from '../search-filter-tabbed/search-filter-tab.directive'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const DEFAULT_DATE_DISPLAY_FORMAT = 'dd-MMM-yy'; @@ -41,7 +42,7 @@ const DEFAULT_DATE_DISPLAY_FORMAT = 'dd-MMM-yy'; styleUrls: ['./search-date-range-tabbed.component.scss'], encapsulation: ViewEncapsulation.None }) -export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit, OnDestroy { +export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit { displayValue$ = new ReplaySubject(1); id: string; startValue: SearchDateRange = { @@ -62,7 +63,8 @@ export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit, OnD private value: { [key: string]: Partial } = {}; private queryMapByField: Map = new Map(); private displayValueMapByField: Map = new Map(); - private readonly destroy$ = new Subject(); + + private readonly destroyRef = inject(DestroyRef); constructor(private translateService: TranslationService) {} @@ -73,7 +75,7 @@ export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit, OnD .asObservable() .pipe( map((filtersQueries) => filtersQueries[this.id]), - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((filterQuery) => { if (filterQuery) { @@ -94,12 +96,6 @@ export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit, OnD this.context.filterLoaded.next(); }); } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - private setDefaultDateFormatSettings() { if (this.settings && !this.settings.dateFormat) { this.settings.dateFormat = DEFAULT_DATE_DISPLAY_FORMAT; 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 0a8fb591fb..e65478c094 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 @@ -15,17 +15,15 @@ * limitations under the License. */ -import { Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core'; -import { Subject } from 'rxjs'; -import { endOfDay, parse, isValid, isBefore, isAfter } from 'date-fns'; +import { Component, DestroyRef, EventEmitter, inject, Inject, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { endOfDay, isAfter, isBefore, isValid, parse } from 'date-fns'; import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE, MatDateFormats } from '@angular/material/core'; import { DateFnsAdapter, MAT_DATE_FNS_FORMATS } from '@angular/material-date-fns-adapter'; import { InLastDateType } from './in-last-date-type'; import { DateRangeType } from './date-range-type'; import { SearchDateRange } from './search-date-range'; import { FormBuilder, ReactiveFormsModule, UntypedFormControl, Validators } from '@angular/forms'; -import { takeUntil } from 'rxjs/operators'; -import { UserPreferencesService, UserPreferenceValues, DateFnsUtils } from '@alfresco/adf-core'; +import { DateFnsUtils, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core'; import { CommonModule } from '@angular/common'; import { MatRadioModule } from '@angular/material/radio'; import { TranslateModule } from '@ngx-translate/core'; @@ -33,6 +31,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatDatepickerModule } from '@angular/material/datepicker'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const DEFAULT_DATE_DISPLAY_FORMAT = 'dd-MMM-yy'; @@ -58,7 +57,7 @@ const DEFAULT_DATE_DISPLAY_FORMAT = 'dd-MMM-yy'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-search-date-range' } }) -export class SearchDateRangeComponent implements OnInit, OnDestroy { +export class SearchDateRangeComponent implements OnInit { @Input() dateFormat = DEFAULT_DATE_DISPLAY_FORMAT; @Input() @@ -87,11 +86,12 @@ export class SearchDateRangeComponent implements OnInit, OnDestroy { betweenStartDateFormControl = this.form.controls.betweenStartDate; betweenEndDateFormControl = this.form.controls.betweenEndDate; convertedMaxDate: Date; - private readonly destroy$ = new Subject(); readonly DateRangeType = DateRangeType; readonly InLastDateType = InLastDateType; + private readonly destroyRef = inject(DestroyRef); + constructor( private formBuilder: FormBuilder, private userPreferencesService: UserPreferencesService, @@ -113,19 +113,13 @@ export class SearchDateRangeComponent implements OnInit, OnDestroy { this.convertedMaxDate = endOfDay(this.maxDate && this.maxDate !== 'today' ? parse(this.maxDate, this.dateFormat, new Date()) : new Date()); this.userPreferencesService .select(UserPreferenceValues.Locale) - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((locale) => this.dateAdapter.setLocale(DateFnsUtils.getLocaleFromString(locale))); this.form.controls.dateRangeType.valueChanges - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((dateRangeType) => this.updateValidators(dateRangeType)); - this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => this.onChange()); + this.form.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.onChange()); } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - private updateValidators(dateRangeType: DateRangeType) { switch (dateRangeType) { case DateRangeType.BETWEEN: 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 38910c9fc2..2421c8a32b 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 @@ -15,22 +15,34 @@ * limitations under the License. */ -import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; -import { ADF_DATE_FORMATS, ADF_DATETIME_FORMATS, AdfDateFnsAdapter, AdfDateTimeFnsAdapter, DateFnsUtils } from '@alfresco/adf-core'; +import { + ADF_DATE_FORMATS, + ADF_DATETIME_FORMATS, + AdfDateFnsAdapter, + AdfDateTimeFnsAdapter, + DateFnsUtils +} from '@alfresco/adf-core'; import { SearchWidget } from '../../models/search-widget.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; import { LiveErrorStateMatcher } from '../../forms/live-error-state-matcher'; -import { ReplaySubject, Subject } from 'rxjs'; -import { map, takeUntil } from 'rxjs/operators'; -import { DatetimeAdapter, MAT_DATETIME_FORMATS, MatDatetimepickerInputEvent, MatDatetimepickerModule } from '@mat-datetimepicker/core'; +import { ReplaySubject } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { + DatetimeAdapter, + MAT_DATETIME_FORMATS, + MatDatetimepickerInputEvent, + MatDatetimepickerModule +} from '@mat-datetimepicker/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; -import { isValid, isBefore, startOfMinute, endOfMinute, parseISO } from 'date-fns'; +import { endOfMinute, isBefore, isValid, parseISO, startOfMinute } from 'date-fns'; import { CommonModule } from '@angular/common'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { TranslateModule } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface DatetimeRangeValue { from: string; @@ -59,7 +71,7 @@ export const DEFAULT_DATETIME_FORMAT: string = 'dd/MM/yyyy HH:mm'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-search-date-range' } }) -export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDestroy { +export class SearchDatetimeRangeComponent implements SearchWidget, OnInit { from: FormControl; to: FormControl; @@ -77,7 +89,7 @@ export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDes enableChangeUpdate: boolean; displayValue$ = new ReplaySubject(1); - private readonly destroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private dateAdapter: DateAdapter, private dateTimeAdapter: DatetimeAdapter) {} @@ -140,7 +152,7 @@ export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDes .asObservable() .pipe( map((filtersQueries) => filtersQueries[this.id]), - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((filterQuery) => { if (filterQuery) { @@ -156,11 +168,6 @@ export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDes }); } - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - apply(model: Partial<{ from: Date; to: Date }>, isValidValue: boolean, updateContext = true) { if (isValidValue && this.id && this.context && this.settings && this.settings.field) { this.isActive = true; 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 6fd4f72a71..392f082497 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 @@ -15,9 +15,9 @@ * limitations under the License. */ -import { Component, ViewEncapsulation, OnInit, OnDestroy } from '@angular/core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { BehaviorSubject, Observable, ReplaySubject, Subject } from 'rxjs'; -import { map, takeUntil } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; import { SearchWidget } from '../../models/search-widget.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; @@ -29,6 +29,7 @@ import { CommonModule } from '@angular/common'; import { SearchChipAutocompleteInputComponent } from '../search-chip-autocomplete-input'; import { TranslateModule } from '@ngx-translate/core'; import { MatButtonModule } from '@angular/material/button'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-search-filter-autocomplete-chips', @@ -37,7 +38,7 @@ import { MatButtonModule } from '@angular/material/button'; templateUrl: './search-filter-autocomplete-chips.component.html', encapsulation: ViewEncapsulation.None }) -export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnInit, OnDestroy { +export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnInit { id: string; settings?: SearchWidgetSettings; context?: SearchQueryBuilderService; @@ -51,7 +52,8 @@ export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnI reset$: Observable = this.resetSubject$.asObservable(); private autocompleteOptionsSubject$ = new BehaviorSubject([]); autocompleteOptions$: Observable = this.autocompleteOptionsSubject$.asObservable(); - private readonly destroy$ = new Subject(); + + private readonly destroyRef = inject(DestroyRef); constructor(private tagService: TagService, private categoryService: CategoryService) { this.options = new SearchFilterList(); @@ -69,7 +71,7 @@ export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnI .asObservable() .pipe( map((filterQueries) => filterQueries[this.id]), - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((filterQuery) => { if (filterQuery) { @@ -82,11 +84,6 @@ export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnI }); } - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - reset(updateContext = true) { this.selectedOptions = []; this.context.filterRawParams[this.id] = undefined; 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 57df84c675..17655eab82 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 @@ -15,19 +15,19 @@ * limitations under the License. */ -import { Component, EventEmitter, inject, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; import { Observable, Subject } from 'rxjs'; import { SearchQueryBuilderService } from '../../../services/search-query-builder.service'; import { FacetWidget } from '../../../models/facet-widget.interface'; import { TranslationService } from '@alfresco/adf-core'; import { AutocompleteOption } from '../../../models/autocomplete-option.interface'; -import { takeUntil } from 'rxjs/operators'; import { TabbedFacetField } from '../../../models/tabbed-facet-field.interface'; import { SearchFacetFiltersService } from '../../../services/search-facet-filters.service'; import { CommonModule } from '@angular/common'; import { SearchChipAutocompleteInputComponent } from '../../search-chip-autocomplete-input'; import { SearchFilterTabbedComponent } from '../../search-filter-tabbed/search-filter-tabbed.component'; import { SearchFilterTabDirective } from '../../search-filter-tabbed'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-search-facet-tabbed-content', @@ -36,7 +36,7 @@ import { SearchFilterTabDirective } from '../../search-filter-tabbed'; templateUrl: './search-facet-tabbed-content.component.html', encapsulation: ViewEncapsulation.None }) -export class SearchFacetTabbedContentComponent implements OnInit, OnDestroy, OnChanges, FacetWidget { +export class SearchFacetTabbedContentComponent implements OnInit, OnChanges, FacetWidget { private queryBuilder = inject(SearchQueryBuilderService); private translationService = inject(TranslationService); private searchFacetFiltersService = inject(SearchFacetFiltersService); @@ -57,13 +57,14 @@ export class SearchFacetTabbedContentComponent implements OnInit, OnDestroy, OnC displayValue$ = new EventEmitter(); private resetSubject$ = new Subject(); - private onDestroy$ = new Subject(); reset$ = this.resetSubject$.asObservable(); chipIcon = 'keyboard_arrow_down'; autocompleteOptions = {}; selectedOptions = {}; + private readonly destroyRef = inject(DestroyRef); + ngOnInit() { this.tabbedFacet.fields.forEach((field) => { Object.defineProperty(this.selectedOptions, field, { @@ -72,13 +73,8 @@ export class SearchFacetTabbedContentComponent implements OnInit, OnDestroy, OnC }); }); - this.onReset$?.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.reset()); - this.onApply$?.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.submitValues()); - } - - ngOnDestroy() { - this.onDestroy$.next(); - this.onDestroy$.complete(); + this.onReset$?.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.reset()); + this.onApply$?.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.submitValues()); } ngOnChanges(changes: SimpleChanges) { 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 ca8d08a991..d2f92d1de3 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 @@ -15,11 +15,9 @@ * limitations under the License. */ -import { Component, inject, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { SearchFacetFiltersService } from '../../services/search-facet-filters.service'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { FacetField, SearchCategory, TabbedFacetField } from '../../models'; import { CommonModule } from '@angular/common'; import { MatChipsModule } from '@angular/material/chips'; @@ -27,6 +25,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { SearchFacetChipTabbedComponent } from './search-facet-chip-tabbed/search-facet-chip-tabbed.component'; import { SearchFacetChipComponent } from './search-facet-chip/search-facet-chip.component'; import { SearchWidgetChipComponent } from './search-widget-chip/search-widget-chip.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-search-filter-chips', @@ -36,11 +35,11 @@ import { SearchWidgetChipComponent } from './search-widget-chip/search-widget-ch styleUrls: ['./search-filter-chips.component.scss'], encapsulation: ViewEncapsulation.None }) -export class SearchFilterChipsComponent implements OnInit, OnDestroy { +export class SearchFilterChipsComponent implements OnInit { private queryBuilder = inject(SearchQueryBuilderService); private facetFiltersService = inject(SearchFacetFiltersService); - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); /** Toggles whether to show or not the context facet filters. */ @Input() @@ -63,12 +62,7 @@ export class SearchFilterChipsComponent implements OnInit, OnDestroy { ngOnInit() { this.queryBuilder.executed .asObservable() - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => (this.facetChipTabbedId = 'search-fact-chip-tabbed-' + this.facetFiltersService.tabbedFacet?.fields.join('-'))); } - - ngOnDestroy() { - this.onDestroy$.next(); - this.onDestroy$.complete(); - } } 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 b74f61c105..223c373c86 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,13 +15,21 @@ * limitations under the License. */ -import { Component, Input, Output, OnInit, EventEmitter, ViewEncapsulation, ViewChild, OnDestroy, ElementRef } from '@angular/core'; -import { ConfigurableFocusTrapFactory, ConfigurableFocusTrap } from '@angular/cdk/a11y'; +import { + Component, + ElementRef, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y'; import { DataColumn, IconComponent, TranslationService } from '@alfresco/adf-core'; import { SearchWidgetContainerComponent } from '../search-widget-container/search-widget-container.component'; import { SearchHeaderQueryBuilderService } from '../../services/search-header-query-builder.service'; import { SearchCategory } from '../../models/search-category.interface'; -import { Subject } from 'rxjs'; import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu'; import { FilterSearch } from '../../models/filter-search.interface'; import { CommonModule } from '@angular/common'; @@ -47,7 +55,7 @@ import { MatDialogModule } from '@angular/material/dialog'; styleUrls: ['./search-filter-container.component.scss'], encapsulation: ViewEncapsulation.None }) -export class SearchFilterContainerComponent implements OnInit, OnDestroy { +export class SearchFilterContainerComponent implements OnInit { /** The column the filter will be applied on. */ @Input() col: DataColumn; @@ -70,8 +78,6 @@ export class SearchFilterContainerComponent implements OnInit, OnDestroy { focusTrap: ConfigurableFocusTrap; initialValue: any; - private onDestroy$ = new Subject(); - constructor( private searchFilterQueryBuilder: SearchHeaderQueryBuilderService, private translationService: TranslationService, @@ -83,11 +89,6 @@ export class SearchFilterContainerComponent implements OnInit, OnDestroy { this.initialValue = this.value?.[this.col.key] ? this.value[this.col.key] : undefined; } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - onKeyPressed(event: KeyboardEvent, menuTrigger: MatMenuTrigger) { if (event.key === 'Enter' && this.widgetContainer.selector !== 'check-list') { this.onApply(); 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 a10123dc07..03c4af97bc 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 @@ -15,17 +15,18 @@ * limitations under the License. */ -import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { SearchWidget } from '../../models/search-widget.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; -import { ReplaySubject, Subject } from 'rxjs'; -import { map, takeUntil } from 'rxjs/operators'; +import { ReplaySubject } from 'rxjs'; +import { map } from 'rxjs/operators'; import { TranslationService } from '@alfresco/adf-core'; import { CommonModule } from '@angular/common'; import { MatFormFieldModule } from '@angular/material/form-field'; import { TranslateModule } from '@ngx-translate/core'; import { FormsModule } from '@angular/forms'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export enum LogicalSearchFields { MATCH_ALL = 'matchAll', @@ -46,7 +47,7 @@ export interface LogicalSearchCondition extends LogicalSearchConditionEnumValued styleUrls: ['./search-logical-filter.component.scss'], encapsulation: ViewEncapsulation.None }) -export class SearchLogicalFilterComponent implements SearchWidget, OnInit, OnDestroy { +export class SearchLogicalFilterComponent implements SearchWidget, OnInit { id: string; settings?: SearchWidgetSettings; context?: SearchQueryBuilderService; @@ -56,7 +57,7 @@ export class SearchLogicalFilterComponent implements SearchWidget, OnInit, OnDes LogicalSearchFields = LogicalSearchFields; displayValue$ = new ReplaySubject(1); - private readonly destroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private translationService: TranslationService) {} @@ -66,7 +67,7 @@ export class SearchLogicalFilterComponent implements SearchWidget, OnInit, OnDes .asObservable() .pipe( map((filtersQueries) => filtersQueries[this.id]), - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((filterQuery) => { if (filterQuery) { @@ -79,11 +80,6 @@ export class SearchLogicalFilterComponent implements SearchWidget, OnInit, OnDes }); } - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - submitValues(updateContext = true) { if (this.hasValidValue() && this.id && this.context && this.settings && this.settings.field) { this.updateDisplayValue(); 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 ee34979d41..a04601b09e 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 @@ -15,7 +15,7 @@ * limitations under the License. */ -import { AfterViewChecked, Component, ElementRef, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { AfterViewChecked, Component, DestroyRef, ElementRef, inject, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; import { FileSizeCondition } from './file-size-condition'; import { FileSizeOperator } from './file-size-operator.enum'; @@ -31,7 +31,8 @@ import { CommonModule } from '@angular/common'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; import { SearchChipAutocompleteInputComponent } from '../search-chip-autocomplete-input'; -import { map, takeUntil } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-search-properties', @@ -41,7 +42,7 @@ import { map, takeUntil } from 'rxjs/operators'; styleUrls: ['./search-properties.component.scss'], encapsulation: ViewEncapsulation.None }) -export class SearchPropertiesComponent implements OnInit, AfterViewChecked, OnDestroy, SearchWidget { +export class SearchPropertiesComponent implements OnInit, AfterViewChecked, SearchWidget { id: string; settings?: SearchWidgetSettings; context?: SearchQueryBuilderService; @@ -95,7 +96,7 @@ export class SearchPropertiesComponent implements OnInit, AfterViewChecked, OnDe this._selectedExtensions = this.parseFromAutocompleteOptions(extensions); } - private readonly destroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private formBuilder: FormBuilder, private translateService: TranslateService) {} @@ -114,7 +115,7 @@ export class SearchPropertiesComponent implements OnInit, AfterViewChecked, OnDe .asObservable() .pipe( map((filtersQueries) => filtersQueries[this.id]), - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((filterQuery) => { if (filterQuery) { @@ -149,11 +150,6 @@ export class SearchPropertiesComponent implements OnInit, AfterViewChecked, OnDe } } - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - narrowDownAllowedCharacters(event: Event) { const value = (event.target as HTMLInputElement).value; if (!(event.target as HTMLInputElement).value) { diff --git a/lib/content-services/src/lib/search/components/search-slider/search-slider.component.ts b/lib/content-services/src/lib/search/components/search-slider/search-slider.component.ts index b3d733d5d2..614c5520e2 100644 --- a/lib/content-services/src/lib/search/components/search-slider/search-slider.component.ts +++ b/lib/content-services/src/lib/search/components/search-slider/search-slider.component.ts @@ -15,17 +15,17 @@ * limitations under the License. */ -import { Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { SearchWidget } from '../../models/search-widget.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; -import { ReplaySubject, Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { ReplaySubject } from 'rxjs'; import { CommonModule } from '@angular/common'; import { MatSliderModule } from '@angular/material/slider'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { TranslateModule } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-search-slider', @@ -36,7 +36,7 @@ import { TranslateModule } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-search-slider' } }) -export class SearchSliderComponent implements SearchWidget, OnInit, OnDestroy { +export class SearchSliderComponent implements SearchWidget, OnInit { /** The numeric value represented by the slider. */ @Input() value: number | null; @@ -54,7 +54,7 @@ export class SearchSliderComponent implements SearchWidget, OnInit, OnDestroy { enableChangeUpdate: boolean; displayValue$ = new ReplaySubject(1); - private readonly destroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); ngOnInit() { if (this.settings) { @@ -79,7 +79,7 @@ export class SearchSliderComponent implements SearchWidget, OnInit, OnDestroy { } this.context.populateFilters .asObservable() - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((filtersQueries) => { if (filtersQueries[this.id]) { this.value = filtersQueries[this.id]; @@ -91,11 +91,6 @@ export class SearchSliderComponent implements SearchWidget, OnInit, OnDestroy { }); } - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - clear() { this.value = this.min || 0; if (this.enableChangeUpdate) { diff --git a/lib/content-services/src/lib/search/components/search-text/search-text.component.ts b/lib/content-services/src/lib/search/components/search-text/search-text.component.ts index e12aeed9a1..466764fc36 100644 --- a/lib/content-services/src/lib/search/components/search-text/search-text.component.ts +++ b/lib/content-services/src/lib/search/components/search-text/search-text.component.ts @@ -15,12 +15,12 @@ * limitations under the License. */ -import { Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { SearchWidget } from '../../models/search-widget.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; -import { ReplaySubject, Subject } from 'rxjs'; -import { map, takeUntil } from 'rxjs/operators'; +import { ReplaySubject } from 'rxjs'; +import { map } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; import { MatFormFieldModule } from '@angular/material/form-field'; import { TranslateModule } from '@ngx-translate/core'; @@ -28,6 +28,7 @@ import { MatInputModule } from '@angular/material/input'; import { MatButtonModule } from '@angular/material/button'; import { FormsModule } from '@angular/forms'; import { MatIconModule } from '@angular/material/icon'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-search-text', @@ -38,7 +39,7 @@ import { MatIconModule } from '@angular/material/icon'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-search-text' } }) -export class SearchTextComponent implements SearchWidget, OnInit, OnDestroy { +export class SearchTextComponent implements SearchWidget, OnInit { /** The content of the text box. */ @Input() value = ''; @@ -51,7 +52,7 @@ export class SearchTextComponent implements SearchWidget, OnInit, OnDestroy { enableChangeUpdate = true; displayValue$ = new ReplaySubject(1); - private readonly destroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); ngOnInit() { if (this.context && this.settings && this.settings.pattern) { @@ -77,7 +78,7 @@ export class SearchTextComponent implements SearchWidget, OnInit, OnDestroy { .asObservable() .pipe( map((filtersQueries) => filtersQueries[this.id]), - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((filterQuery) => { if (filterQuery) { @@ -89,12 +90,6 @@ export class SearchTextComponent implements SearchWidget, OnInit, OnDestroy { this.context.filterLoaded.next(); }); } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - clear() { this.isActive = false; this.value = ''; 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 078dde0f73..94f67f8b3b 100644 --- a/lib/content-services/src/lib/search/components/search.component.ts +++ b/lib/content-services/src/lib/search/components/search.component.ts @@ -25,18 +25,18 @@ import { Input, OnChanges, Output, + SimpleChanges, TemplateRef, ViewChild, - ViewEncapsulation, - OnDestroy, - SimpleChanges + ViewEncapsulation } from '@angular/core'; import { NodePaging, ResultSetPaging } from '@alfresco/js-api'; import { Subject } from 'rxjs'; -import { debounceTime, takeUntil } from 'rxjs/operators'; +import { debounceTime } from 'rxjs/operators'; import { SearchComponentInterface } from '@alfresco/adf-core'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-search', @@ -49,7 +49,7 @@ import { TranslateModule } from '@ngx-translate/core'; exportAs: 'searchAutocomplete', host: { class: 'adf-search' } }) -export class SearchComponent implements SearchComponentInterface, AfterContentInit, OnChanges, OnDestroy { +export class SearchComponent implements SearchComponentInterface, AfterContentInit, OnChanges { @ViewChild('panel', { static: true }) panel: ElementRef; @@ -107,14 +107,12 @@ export class SearchComponent implements SearchComponentInterface, AfterContentIn _isOpen: boolean = false; keyPressedStream = new Subject(); _classList: { [key: string]: boolean } = {}; - private onDestroy$ = new Subject(); - constructor(private searchService: SearchService, private _elementRef: ElementRef) { - this.keyPressedStream.pipe(debounceTime(200), takeUntil(this.onDestroy$)).subscribe((searchedWord) => { + this.keyPressedStream.pipe(debounceTime(200), takeUntilDestroyed()).subscribe((searchedWord) => { this.loadSearchResults(searchedWord); }); - searchService.dataLoaded.pipe(takeUntil(this.onDestroy$)).subscribe( + searchService.dataLoaded.pipe(takeUntilDestroyed()).subscribe( (nodePaging) => this.onSearchDataLoaded(nodePaging), (error) => this.onSearchDataError(error) ); @@ -129,12 +127,6 @@ export class SearchComponent implements SearchComponentInterface, AfterContentIn this.loadSearchResults(changes.searchTerm.currentValue); } } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - resetResults() { this.cleanResults(); this.setVisibility(); 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 e05081e667..7a6b7fbf25 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,18 +15,19 @@ * limitations under the License. */ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable } from '@angular/core'; import { FacetBucketSortBy, FacetBucketSortDirection, FacetField } from '../models/facet-field.interface'; -import { Subject, throwError } from 'rxjs'; +import { throwError } from 'rxjs'; import { SearchQueryBuilderService } from './search-query-builder.service'; import { TranslationService } from '@alfresco/adf-core'; import { SearchService } from './search.service'; -import { catchError, takeUntil } from 'rxjs/operators'; +import { catchError } from 'rxjs/operators'; import { GenericBucket, GenericFacetResponse, ResultSetContext, ResultSetPaging } from '@alfresco/js-api'; import { SearchFilterList } from '../models/search-filter-list.model'; import { FacetFieldBucket } from '../models/facet-field-bucket.interface'; import { CategoryService } from '../../category/services/category.service'; import { TabbedFacetField } from '../models/tabbed-facet-field.interface'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface SelectedBucket { field: FacetField; @@ -38,7 +39,7 @@ const DEFAULT_PAGE_SIZE: number = 5; @Injectable({ providedIn: 'root' }) -export class SearchFacetFiltersService implements OnDestroy { +export class SearchFacetFiltersService { /** * 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 @@ -52,8 +53,6 @@ export class SearchFacetFiltersService implements OnDestroy { selectedBuckets: SelectedBucket[] = []; private readonly facetQueriesPageSize = DEFAULT_PAGE_SIZE; - private readonly onDestroy$ = new Subject(); - constructor( private queryBuilder: SearchQueryBuilderService, private searchService: SearchService, @@ -64,14 +63,14 @@ export class SearchFacetFiltersService implements OnDestroy { this.facetQueriesPageSize = queryBuilder.config.facetQueries.pageSize || DEFAULT_PAGE_SIZE; } - this.queryBuilder.configUpdated.pipe(takeUntil(this.onDestroy$)).subscribe(() => { + this.queryBuilder.configUpdated.pipe(takeUntilDestroyed()).subscribe(() => { this.selectedBuckets = []; this.responseFacets = null; }); - this.queryBuilder.updated.pipe(takeUntil(this.onDestroy$)).subscribe((query) => this.queryBuilder.execute(true, query)); + this.queryBuilder.updated.pipe(takeUntilDestroyed()).subscribe((query) => this.queryBuilder.execute(true, query)); - this.queryBuilder.executed.pipe(takeUntil(this.onDestroy$)).subscribe((resultSetPaging: ResultSetPaging) => { + this.queryBuilder.executed.pipe(takeUntilDestroyed()).subscribe((resultSetPaging: ResultSetPaging) => { this.onDataLoaded(resultSetPaging); this.searchService.dataLoaded.next(resultSetPaging); }); @@ -420,11 +419,6 @@ export class SearchFacetFiltersService implements OnDestroy { } } - ngOnDestroy(): void { - this.onDestroy$.next(undefined); - this.onDestroy$.complete(); - } - resetAllSelectedBuckets() { this.responseFacets.forEach((facetField) => { if (facetField?.buckets) { 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 aa8aaf0b92..3da853279a 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 @@ -16,11 +16,19 @@ */ import { TranslationService } from '@alfresco/adf-core'; -import { Component, EventEmitter, Input, OnChanges, Output, ViewEncapsulation, OnDestroy, OnInit } from '@angular/core'; +import { + Component, + DestroyRef, + EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, + ViewEncapsulation +} from '@angular/core'; import { TagService } from '../services/tag.service'; -import { Subject } from 'rxjs'; import { TagPaging } from '@alfresco/js-api'; -import { takeUntil } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; import { MatListModule } from '@angular/material/list'; import { MatIconModule } from '@angular/material/icon'; @@ -29,6 +37,7 @@ import { MatInputModule } from '@angular/material/input'; import { TranslateModule } from '@ngx-translate/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; /** * @@ -44,7 +53,7 @@ import { MatButtonModule } from '@angular/material/button'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-tag-node-actions-list' } }) -export class TagActionsComponent implements OnChanges, OnInit, OnDestroy { +export class TagActionsComponent implements OnChanges, OnInit { /** The identifier of a node. */ @Input() nodeId: string; @@ -66,23 +75,18 @@ export class TagActionsComponent implements OnChanges, OnInit, OnDestroy { errorMsg: string; disableAddTag: boolean = true; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private tagService: TagService, private translateService: TranslationService) {} ngOnInit() { - this.tagService.refresh.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.refreshTag()); + this.tagService.refresh.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.refreshTag()); } ngOnChanges() { return this.refreshTag(); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - refreshTag() { if (this.nodeId) { this.tagService.getTagsByNodeId(this.nodeId).subscribe( 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 38e6e10631..ed34ab9c83 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,16 +15,15 @@ * limitations under the License. */ -import { Component, EventEmitter, OnInit, Output, ViewEncapsulation, OnDestroy } from '@angular/core'; +import { Component, EventEmitter, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { TagService } from '../services/tag.service'; import { PaginationModel } from '@alfresco/adf-core'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { TagEntry } from '@alfresco/js-api'; import { CommonModule } from '@angular/common'; import { MatChipsModule } from '@angular/material/chips'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; /** * This component provide a list of all the tag inside the ECM @@ -38,7 +37,7 @@ import { MatIconModule } from '@angular/material/icon'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-tag-list' } }) -export class TagListComponent implements OnInit, OnDestroy { +export class TagListComponent implements OnInit { /** Emitted when a tag is selected. */ @Output() result = new EventEmitter(); @@ -59,8 +58,6 @@ export class TagListComponent implements OnInit, OnDestroy { isLoading = false; isSizeMinimum = true; - private onDestroy$ = new Subject(); - constructor(private tagService: TagService) { this.defaultPagination = { skipCount: 0, @@ -70,7 +67,7 @@ export class TagListComponent implements OnInit, OnDestroy { this.pagination = this.defaultPagination; - this.tagService.refresh.pipe(takeUntil(this.onDestroy$)).subscribe(() => { + this.tagService.refresh.pipe(takeUntilDestroyed()).subscribe(() => { this.tagsEntries = []; this.refreshTag(this.defaultPagination); }); @@ -79,12 +76,6 @@ export class TagListComponent implements OnInit, OnDestroy { ngOnInit() { this.refreshTag(this.defaultPagination); } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - refreshTag(opts?: any) { this.tagService.getAllTheTags(opts).subscribe((tags) => { this.tagsEntries = this.tagsEntries.concat(tags.list.entries); 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 7f46be393f..54e3fba6a3 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 @@ -15,12 +15,11 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnChanges, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { TagService } from '../services/tag.service'; import { TagEntry } from '@alfresco/js-api'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { Chip, DynamicChipListComponent } from '@alfresco/adf-core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; /** * @@ -34,7 +33,7 @@ import { Chip, DynamicChipListComponent } from '@alfresco/adf-core'; templateUrl: './tag-node-list.component.html', encapsulation: ViewEncapsulation.None }) -export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit { +export class TagNodeListComponent implements OnChanges, OnInit { /** The identifier of a node. */ @Input() nodeId: string; @@ -51,13 +50,14 @@ export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit { @Output() results = new EventEmitter(); - private onDestroy$ = new Subject(); private _tagChips: Chip[] = []; get tagChips(): Chip[] { return this._tagChips; } + private readonly destroyRef = inject(DestroyRef); + constructor(private tagService: TagService) {} ngOnChanges(): void { @@ -65,12 +65,7 @@ export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit { } ngOnInit(): void { - this.tagService.refresh.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.refreshTag()); - } - - ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); + this.tagService.refresh.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.refreshTag()); } refreshTag(): void { 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 b1fb1bcf2a..2a97a40657 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 @@ -16,7 +16,20 @@ */ import { TagEntry, TagPaging } from '@alfresco/js-api'; -import { Component, ElementRef, EventEmitter, HostBinding, Input, OnDestroy, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; +import { + Component, + DestroyRef, + ElementRef, + EventEmitter, + HostBinding, + inject, + Input, + OnDestroy, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; import { debounce, distinctUntilChanged, finalize, first, map, takeUntil, tap } from 'rxjs/operators'; import { EMPTY, forkJoin, Observable, Subject, timer } from 'rxjs'; @@ -32,6 +45,7 @@ import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatListModule } from '@angular/material/list'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; interface TagNameControlErrors { duplicatedExistingTag?: boolean; @@ -162,7 +176,6 @@ export class TagsCreatorComponent implements OnInit, OnDestroy { private _tagNameControlVisible = false; private _existingTags: TagEntry[]; private _initialExistingTags: TagEntry[]; - private onDestroy$ = new Subject(); private _tagNameErrorMessageKey = ''; private _spinnerVisible = false; private _typing = false; @@ -176,6 +189,8 @@ export class TagsCreatorComponent implements OnInit, OnDestroy { @ViewChild('tagNameInput') private tagNameInputElement: ElementRef; + private readonly destroyRef = inject(DestroyRef); + constructor(private tagService: TagService, private notificationService: NotificationService) {} ngOnInit(): void { @@ -201,18 +216,16 @@ export class TagsCreatorComponent implements OnInit, OnDestroy { this._existingTags = null; }), debounce((name: string) => (name ? timer(300) : EMPTY)), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((name: string) => this.onTagNameControlValueChange(name)); - this.tagNameControl.statusChanges.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.setTagNameControlErrorMessageKey()); + this.tagNameControl.statusChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.setTagNameControlErrorMessageKey()); this.setTagNameControlErrorMessageKey(); } ngOnDestroy(): void { - this.onDestroy$.next(); - this.onDestroy$.complete(); this.cancelExistingTagsLoading$.next(); this.cancelExistingTagsLoading$.complete(); } diff --git a/lib/content-services/src/lib/upload/components/base-upload/upload-base.ts b/lib/content-services/src/lib/upload/components/base-upload/upload-base.ts index fd1aaac2d7..e7d2b20257 100644 --- a/lib/content-services/src/lib/upload/components/base-upload/upload-base.ts +++ b/lib/content-services/src/lib/upload/components/base-upload/upload-base.ts @@ -19,14 +19,13 @@ import { FileInfo, TranslationService } from '@alfresco/adf-core'; import { FileUploadErrorEvent } from '../../../common/events/file.event'; import { FileModel } from '../../../common/models/file.model'; import { UploadService } from '../../../common/services/upload.service'; -import { EventEmitter, Input, Output, OnInit, OnDestroy, NgZone, Directive, inject } from '@angular/core'; -import { Subject } from 'rxjs'; +import { DestroyRef, Directive, EventEmitter, inject, Input, NgZone, OnInit, Output } from '@angular/core'; import { UploadFilesEvent } from '../upload-files.event'; -import { takeUntil } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Directive() // eslint-disable-next-line @angular-eslint/directive-class-suffix -export abstract class UploadBase implements OnInit, OnDestroy { +export abstract class UploadBase implements OnInit { protected uploadService = inject(UploadService); protected translationService = inject(TranslationService); protected ngZone = inject(NgZone); @@ -85,19 +84,14 @@ export abstract class UploadBase implements OnInit, OnDestroy { @Output() updateFileVersion = new EventEmitter(); - protected onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); ngOnInit() { this.uploadService.fileUploadError - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(error => this.error.emit(error)); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - /** * Upload a list of file in the specified path * 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 63ccd75019..1284ef459c 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 @@ -19,29 +19,32 @@ import { UserPreferencesService } from '@alfresco/adf-core'; import { ChangeDetectorRef, Component, - Input, - Output, + DestroyRef, + ElementRef, EventEmitter, + HostBinding, + inject, + Input, OnDestroy, OnInit, + Output, ViewChild, - HostBinding, - ElementRef, ViewEncapsulation } from '@angular/core'; -import { Subscription, merge, Subject } from 'rxjs'; +import { merge, Subject } from 'rxjs'; import { FileUploadingListComponent } from './file-uploading-list.component'; import { Direction } from '@angular/cdk/bidi'; -import { takeUntil, delay } from 'rxjs/operators'; +import { delay } from 'rxjs/operators'; import { UploadService } from '../../common/services/upload.service'; import { FileModel, FileUploadStatus } from '../../common/models/file.model'; -import { FileUploadDeleteEvent, FileUploadCompleteEvent } from '../../common/events/file.event'; +import { FileUploadCompleteEvent, FileUploadDeleteEvent } from '../../common/events/file.event'; import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { TranslateModule } from '@ngx-translate/core'; import { MatIconModule } from '@angular/material/icon'; import { FileUploadingListRowComponent } from './file-uploading-list-row.component'; import { A11yModule } from '@angular/cdk/a11y'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-file-uploading-dialog', @@ -54,7 +57,6 @@ import { A11yModule } from '@angular/cdk/a11y'; export class FileUploadingDialogComponent implements OnInit, OnDestroy { /** Dialog direction. Can be 'ltr' or 'rtl. */ private direction: Direction = 'ltr'; - private onDestroy$ = new Subject(); @ViewChild('uploadList') uploadList: FileUploadingListComponent; @@ -87,12 +89,10 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy { isDialogMinimized: boolean = false; isConfirmation: boolean = false; - private listSubscription: Subscription; - private counterSubscription: Subscription; - private fileUploadSubscription: Subscription; - private errorSubscription: Subscription; private dialogActive = new Subject(); + private readonly destroyRef = inject(DestroyRef); + constructor( private uploadService: UploadService, private changeDetector: ChangeDetectorRef, @@ -101,14 +101,14 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy { ) {} ngOnInit() { - this.dialogActive.pipe(delay(100), takeUntil(this.onDestroy$)).subscribe(() => { + this.dialogActive.pipe(delay(100), takeUntilDestroyed(this.destroyRef)).subscribe(() => { const element: any = this.elementRef.nativeElement.querySelector('#upload-dialog'); if (element) { element.focus(); } }); - this.listSubscription = this.uploadService.queueChanged.pipe(takeUntil(this.onDestroy$)).subscribe((fileList) => { + this.uploadService.queueChanged.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((fileList) => { this.filesUploadingList = fileList; if (this.filesUploadingList.length && !this.isDialogActive) { @@ -119,23 +119,23 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy { } }); - this.counterSubscription = merge(this.uploadService.fileUploadComplete, this.uploadService.fileUploadDeleted) - .pipe(takeUntil(this.onDestroy$)) + merge(this.uploadService.fileUploadComplete, this.uploadService.fileUploadDeleted) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((event: FileUploadCompleteEvent | FileUploadDeleteEvent) => { this.totalCompleted = event.totalComplete; this.changeDetector.detectChanges(); }); - this.errorSubscription = this.uploadService.fileUploadError.pipe(takeUntil(this.onDestroy$)).subscribe((event) => { + this.uploadService.fileUploadError.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => { this.totalErrors = event.totalError; this.changeDetector.detectChanges(); }); - this.fileUploadSubscription = this.uploadService.fileUpload.pipe(takeUntil(this.onDestroy$)).subscribe(() => { + this.uploadService.fileUpload.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { this.changeDetector.detectChanges(); }); - this.uploadService.fileDeleted.pipe(takeUntil(this.onDestroy$)).subscribe((objId) => { + this.uploadService.fileDeleted.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((objId) => { if (this.filesUploadingList) { const uploadedFile = this.filesUploadingList.find((file) => (file.data ? file.data.entry.id === objId : false)); if (uploadedFile) { @@ -147,7 +147,7 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy { this.userPreferencesService .select('textOrientation') - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((textOrientation: Direction) => { this.direction = textOrientation; }); @@ -201,12 +201,6 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy { ngOnDestroy() { this.uploadService.clearQueue(); - this.listSubscription.unsubscribe(); - this.counterSubscription.unsubscribe(); - this.fileUploadSubscription.unsubscribe(); - this.errorSubscription.unsubscribe(); - this.onDestroy$.next(true); - this.onDestroy$.complete(); } canShowDialog(): boolean { 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 f28eb82218..dd44beb988 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 @@ -17,14 +17,25 @@ import { ConfirmDialogComponent } from '@alfresco/adf-core'; import { AlfrescoApiService } from '../services/alfresco-api.service'; -import { Component, Input, OnChanges, ViewEncapsulation, EventEmitter, Output, OnInit, OnDestroy, ViewChild } from '@angular/core'; -import { VersionsApi, Node, VersionEntry, NodesApi, NodeEntry, ContentApi, ContentPagingQuery } from '@alfresco/js-api'; +import { + Component, + DestroyRef, + EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { ContentApi, ContentPagingQuery, Node, NodeEntry, NodesApi, VersionEntry, VersionsApi } from '@alfresco/js-api'; import { MatDialog } from '@angular/material/dialog'; import { ContentVersionService } from './content-version.service'; import { ContentService } from '../common'; import { InfiniteScrollDatasource } from '../infinite-scroll-datasource'; -import { from, Observable, Subject } from 'rxjs'; -import { map, take, takeUntil } from 'rxjs/operators'; +import { from, Observable } from 'rxjs'; +import { map, take } from 'rxjs/operators'; import { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { CommonModule } from '@angular/common'; import { MatProgressBarModule } from '@angular/material/progress-bar'; @@ -34,6 +45,7 @@ import { MatMenuModule } from '@angular/material/menu'; import { TranslateModule } from '@ngx-translate/core'; import { MatButtonModule } from '@angular/material/button'; import { VersionCompatibilityDirective } from '../version-compatibility'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export class VersionListDataSource extends InfiniteScrollDatasource { constructor(private versionsApi: VersionsApi, private node: Node) { @@ -69,8 +81,7 @@ export class VersionListDataSource extends InfiniteScrollDatasource(); +export class VersionListComponent implements OnChanges, OnInit { private _contentApi: ContentApi; get contentApi(): ContentApi { this._contentApi = this._contentApi ?? new ContentApi(this.alfrescoApi.getInstance()); @@ -132,6 +143,8 @@ export class VersionListComponent implements OnChanges, OnInit, OnDestroy { @ViewChild('viewport') viewport: CdkVirtualScrollViewport; + private readonly destroyRef = inject(DestroyRef); + constructor( private alfrescoApi: AlfrescoApiService, private contentService: ContentService, @@ -141,7 +154,7 @@ export class VersionListComponent implements OnChanges, OnInit, OnDestroy { ngOnInit() { this.versionsDataSource = new VersionListDataSource(this.versionsApi, this.node); - this.versionsDataSource.isLoading.pipe(takeUntil(this.onDestroy$)).subscribe((isLoading) => { + this.versionsDataSource.isLoading.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((isLoading) => { this.isLoading = isLoading; this.latestVersion = this.versionsDataSource.firstItem; }); @@ -153,10 +166,6 @@ export class VersionListComponent implements OnChanges, OnInit, OnDestroy { } } - ngOnDestroy() { - this.onDestroy$.next(); - this.onDestroy$.complete(); - } canUpdate(): boolean { return this.contentService.hasAllowableOperations(this.node, 'update') && this.versionsDataSource.itemsCount > 1; @@ -188,7 +197,7 @@ export class VersionListComponent implements OnChanges, OnInit, OnDestroy { if (this.allowDownload) { this.contentVersionService .getVersionContentUrl(this.node.id, versionId, true) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((versionDownloadUrl) => this.downloadContent(versionDownloadUrl)); } } @@ -207,7 +216,7 @@ export class VersionListComponent implements OnChanges, OnInit, OnDestroy { dialogRef .afterClosed() - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((result) => { if (result) { this.versionsApi.deleteVersion(this.node.id, versionId).then(() => this.onVersionDeleted(this.node)); 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 fe0769e136..4d5da1dd39 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 @@ -15,10 +15,8 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { Node, Version } from '@alfresco/js-api'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { ContentService } from '../common/services/content.service'; import { UploadService } from '../common/services/upload.service'; import { FileUploadErrorEvent, FileUploadEvent } from '../common/events/file.event'; @@ -30,6 +28,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatButtonModule } from '@angular/material/button'; import { UploadVersionButtonComponent } from '../upload'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-version-upload', @@ -49,12 +48,11 @@ import { UploadVersionButtonComponent } from '../upload'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-version-upload' } }) -export class VersionUploadComponent implements OnInit, OnDestroy { +export class VersionUploadComponent implements OnInit { semanticVersion: string = 'minor'; comment: string; uploadVersion: boolean = false; disabled: boolean = false; - onDestroy$ = new Subject(); majorVersion = '2.0'; minorVersion = '1.1'; @@ -107,10 +105,12 @@ export class VersionUploadComponent implements OnInit, OnDestroy { @Output() uploadStarted = new EventEmitter(); + private readonly destroyRef = inject(DestroyRef); + constructor(private contentService: ContentService, private uploadService: UploadService) {} ngOnInit() { - this.uploadService.fileUploadStarting.pipe(takeUntil(this.onDestroy$)).subscribe((event: FileUploadEvent) => { + this.uploadService.fileUploadStarting.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event: FileUploadEvent) => { this.disabled = true; this.uploadStarted.emit(event); }); @@ -146,11 +146,6 @@ export class VersionUploadComponent implements OnInit, OnDestroy { this.error.emit(event); } - ngOnDestroy() { - this.onDestroy$.next(undefined); - this.onDestroy$.complete(); - } - getNextMinorVersion(version: string): string { const { major, minor } = this.getParsedVersion(version); return `${major}.${minor + 1}`; 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 365c66b2ed..99a56d33af 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 @@ -19,10 +19,11 @@ import { ChangeDetectorRef, Component, ContentChild, + DestroyRef, EventEmitter, + inject, Input, OnChanges, - OnDestroy, OnInit, Output, SimpleChanges, @@ -33,8 +34,8 @@ import { import { CloseButtonPosition, Track, - ViewerComponent, VIEWER_DIRECTIVES, + ViewerComponent, ViewerMoreActionsComponent, ViewerOpenWithComponent, ViewerSidebarComponent, @@ -43,11 +44,10 @@ import { ViewUtilService } from '@alfresco/adf-core'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; -import { Subject } from 'rxjs'; import { ContentApi, Node, NodeEntry, NodesApi, RenditionEntry, SharedlinksApi, Version, VersionEntry, VersionsApi } from '@alfresco/js-api'; import { RenditionService } from '../../common/services/rendition.service'; import { MatDialog } from '@angular/material/dialog'; -import { filter, takeUntil } from 'rxjs/operators'; +import { filter } from 'rxjs/operators'; import { ContentService } from '../../common/services/content.service'; import { NodesApiService } from '../../common/services/nodes-api.service'; import { UploadService } from '../../common/services/upload.service'; @@ -58,6 +58,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { NodeDownloadDirective } from '../../directives'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-alfresco-viewer', @@ -69,7 +70,7 @@ import { NodeDownloadDirective } from '../../directives'; encapsulation: ViewEncapsulation.None, providers: [ViewUtilService] }) -export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy { +export class AlfrescoViewerComponent implements OnChanges, OnInit { @ViewChild('adfViewer') adfViewer: ViewerComponent<{ node: Node }>; @@ -204,8 +205,6 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy { @Output() showViewerChange = new EventEmitter(); - private onDestroy$ = new Subject(); - private cacheBusterNumber: number; versionEntry: VersionEntry; @@ -247,6 +246,8 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy { return this._contentApi; } + private readonly destroyRef = inject(DestroyRef); + constructor( private apiService: AlfrescoApiService, private nodesApiService: NodesApiService, @@ -268,7 +269,7 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy { (node) => node && node.id === this.nodeId && this.getNodeVersionProperty(this.nodeEntry.entry) !== this.getNodeVersionProperty(node) ), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((node) => this.onNodeUpdated(node)); } @@ -458,11 +459,6 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy { } } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - onDownloadFile() { this.nodeActionsService.downloadNode(this.nodeEntry); } 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 884a3a0ac9..15e61d783d 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,11 +15,10 @@ * limitations under the License. */ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, OnDestroy, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, ViewEncapsulation } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FeaturesServiceToken, IDebugFeaturesService } from '../interfaces/features.interface'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-feature-flags-override-indicator', @@ -49,9 +48,8 @@ import { Subject } from 'rxjs'; encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush }) -export class FlagsOverrideComponent implements OnDestroy { +export class FlagsOverrideComponent { isEnabled = false; - destroy$ = new Subject(); @Input() size: 'small' | 'medium' | 'large' = 'medium'; @@ -64,16 +62,11 @@ export class FlagsOverrideComponent implements OnDestroy { if (this.featuresService.isEnabled$) { this.featuresService .isEnabled$() - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed()) .subscribe((isEnabled) => { this.isEnabled = isEnabled; changeDetectorRef.markForCheck(); }); } } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } } 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 fbdc644a06..0e02c9da83 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,18 +15,18 @@ * limitations under the License. */ -import { ChangeDetectionStrategy, Component, Inject, OnDestroy, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, ViewEncapsulation } from '@angular/core'; import { CommonModule } from '@angular/common'; import { - IWritableFeaturesService, FeaturesServiceToken, - WritableFeaturesServiceToken, IDebugFeaturesService, - WritableFlagChangeset, - IFeaturesService + IFeaturesService, + IWritableFeaturesService, + WritableFeaturesServiceToken, + WritableFlagChangeset } from '../../interfaces/features.interface'; -import { BehaviorSubject, Observable, Subject, combineLatest } from 'rxjs'; -import { debounceTime, map, take, takeUntil, tap } from 'rxjs/operators'; +import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; +import { debounceTime, map, take, tap } from 'rxjs/operators'; import { MatTableModule } from '@angular/material/table'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatToolbarModule } from '@angular/material/toolbar'; @@ -37,6 +37,7 @@ import { FormsModule } from '@angular/forms'; import { FlagsOverrideComponent } from '../feature-override-indicator.component'; import { MatDialogModule } from '@angular/material/dialog'; import { TranslateModule } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-feature-flags-overrides', @@ -59,17 +60,15 @@ import { TranslateModule } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush }) -export class FlagsComponent implements OnDestroy { +export class FlagsComponent { displayedColumns: string[] = ['icon', 'flag', 'value']; flags$: Observable<{ fictive: boolean; flag: string; value: any }[]>; isEnabled = false; - destroy$ = new Subject(); inputValue = ''; inputValue$ = new BehaviorSubject(''); showPlusButton$!: Observable; writableFlagChangeset: WritableFlagChangeset = {}; - constructor( @Inject(FeaturesServiceToken) private featuresService: IDebugFeaturesService & IFeaturesService, @@ -79,7 +78,7 @@ export class FlagsComponent implements OnDestroy { if (this.featuresService.isEnabled$) { this.featuresService .isEnabled$() - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed()) .subscribe((isEnabled) => { this.isEnabled = isEnabled; }); @@ -149,9 +148,4 @@ export class FlagsComponent implements OnDestroy { protected onDelete(flag: string) { this.writableFeaturesService.removeFlag(flag); } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } } 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 3a954f6bea..a8400351a4 100644 --- a/lib/core/feature-flags/src/lib/directives/features.directive.ts +++ b/lib/core/feature-flags/src/lib/directives/features.directive.ts @@ -15,21 +15,20 @@ * limitations under the License. */ -import { Directive, Inject, Input, OnDestroy, TemplateRef, ViewContainerRef } from '@angular/core'; -import { BehaviorSubject, Subject, combineLatest } from 'rxjs'; -import { IFeaturesService, FeaturesServiceToken, FlagChangeset } from '../interfaces/features.interface'; -import { takeUntil } from 'rxjs/operators'; +import { Directive, Inject, Input, TemplateRef, ViewContainerRef } from '@angular/core'; +import { BehaviorSubject, combineLatest } from 'rxjs'; +import { FeaturesServiceToken, FlagChangeset, IFeaturesService } from '../interfaces/features.interface'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Directive({ /* eslint-disable-next-line @angular-eslint/directive-selector */ selector: '[adfForFeatures]', standalone: true }) -export class FeaturesDirective implements OnDestroy { +export class FeaturesDirective { private hasView = false; private inputUpdate$ = new BehaviorSubject([] as string[]); - private destroy$ = new Subject(); - + @Input() set adfForFeatures(feature: string[] | string) { this.inputUpdate$.next(Array.isArray(feature) ? feature : [feature]); @@ -41,7 +40,7 @@ export class FeaturesDirective implements OnDestroy { private viewContainer: ViewContainerRef ) { combineLatest([this.featuresService.getFlags$(), this.inputUpdate$]) - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed()) .subscribe(([flags, features]: any) => this.updateView(flags, features)); } @@ -56,9 +55,4 @@ export class FeaturesDirective implements OnDestroy { this.hasView = false; } } - - ngOnDestroy() { - this.destroy$.next({}); - this.destroy$.complete(); - } } 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 a1435a671a..2cd840e008 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,20 +15,19 @@ * limitations under the License. */ -import { Directive, Inject, Input, OnDestroy, TemplateRef, ViewContainerRef } from '@angular/core'; -import { BehaviorSubject, Subject, combineLatest } from 'rxjs'; -import { IFeaturesService, FeaturesServiceToken, FlagChangeset } from '../interfaces/features.interface'; -import { takeUntil } from 'rxjs/operators'; +import { Directive, Inject, Input, TemplateRef, ViewContainerRef } from '@angular/core'; +import { BehaviorSubject, combineLatest } from 'rxjs'; +import { FeaturesServiceToken, FlagChangeset, IFeaturesService } from '../interfaces/features.interface'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Directive({ /* eslint-disable-next-line @angular-eslint/directive-selector */ selector: '[adfNotForFeatures]', standalone: true }) -export class NotFeaturesDirective implements OnDestroy { +export class NotFeaturesDirective { private hasView = false; private inputUpdate$ = new BehaviorSubject([] as string[]); - private destroy$ = new Subject(); @Input() set adfNotForFeatures(feature: string[] | string) { @@ -41,7 +40,7 @@ export class NotFeaturesDirective implements OnDestroy { private viewContainer: ViewContainerRef ) { combineLatest([this.featuresService.getFlags$(), this.inputUpdate$]) - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed()) .subscribe(([flags, features]: any) => this.updateView(flags, features)); } @@ -56,9 +55,4 @@ export class NotFeaturesDirective implements OnDestroy { this.hasView = false; } } - - ngOnDestroy() { - this.destroy$.next({}); - this.destroy$.complete(); - } } 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 dc7eb5ff3d..85c006d620 100644 --- a/lib/core/shell/src/lib/components/shell/shell.component.ts +++ b/lib/core/shell/src/lib/components/shell/shell.component.ts @@ -17,14 +17,20 @@ import { AppConfigService, SidenavLayoutComponent, SidenavLayoutModule } from '@alfresco/adf-core'; import { DynamicExtensionComponent } from '@alfresco/adf-extensions'; -import { Component, Inject, OnDestroy, OnInit, Optional, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, Inject, OnInit, Optional, ViewChild, ViewEncapsulation } from '@angular/core'; import { NavigationEnd, Router, RouterModule } from '@angular/router'; -import { Subject, Observable } from 'rxjs'; -import { filter, takeUntil, map, withLatestFrom } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { filter, map, withLatestFrom } from 'rxjs/operators'; import { BreakpointObserver } from '@angular/cdk/layout'; import { Directionality } from '@angular/cdk/bidi'; -import { SHELL_APP_SERVICE, ShellAppService, SHELL_NAVBAR_MIN_WIDTH, SHELL_NAVBAR_MAX_WIDTH } from '../../services/shell-app.service'; +import { + SHELL_APP_SERVICE, + SHELL_NAVBAR_MAX_WIDTH, + SHELL_NAVBAR_MIN_WIDTH, + ShellAppService +} from '../../services/shell-app.service'; import { CommonModule } from '@angular/common'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'app-shell', @@ -35,11 +41,10 @@ import { CommonModule } from '@angular/common'; encapsulation: ViewEncapsulation.None, host: { class: 'app-shell' } }) -export class ShellLayoutComponent implements OnInit, OnDestroy { +export class ShellLayoutComponent implements OnInit { @ViewChild('layout', { static: true }) layout: SidenavLayoutComponent; - onDestroy$: Subject = new Subject(); isSmallScreen$: Observable; expandedSidenav: boolean; @@ -49,6 +54,8 @@ export class ShellLayoutComponent implements OnInit, OnDestroy { sidenavMax: number; direction: Directionality; + private readonly destroyRef = inject(DestroyRef); + constructor( private router: Router, private appConfigService: AppConfigService, @@ -77,7 +84,7 @@ export class ShellLayoutComponent implements OnInit, OnDestroy { .pipe( withLatestFrom(this.isSmallScreen$), filter(([event, isSmallScreen]) => isSmallScreen && event instanceof NavigationEnd), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe(() => { this.layout.container.sidenav.close(); @@ -86,7 +93,7 @@ export class ShellLayoutComponent implements OnInit, OnDestroy { this.router.events .pipe( filter((event) => event instanceof NavigationEnd), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((event: NavigationEnd) => { this.minimizeSidenav = this.shellService.minimizeSidenavConditions.some((el) => event.urlAfterRedirects.includes(el)); @@ -95,12 +102,6 @@ export class ShellLayoutComponent implements OnInit, OnDestroy { this.updateState(); }); } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - hideMenu(event: Event) { if (this.layout.container.isMobileScreenSize) { event.preventDefault(); diff --git a/lib/core/src/lib/card-view/components/base-card-view.ts b/lib/core/src/lib/card-view/components/base-card-view.ts index 6ac45fcf82..063b713203 100644 --- a/lib/core/src/lib/card-view/components/base-card-view.ts +++ b/lib/core/src/lib/card-view/components/base-card-view.ts @@ -15,15 +15,14 @@ * limitations under the License. */ -import { Input, OnDestroy, Directive, inject } from '@angular/core'; +import { Directive, inject, Input } from '@angular/core'; import { CardViewUpdateService } from '../services/card-view-update.service'; import { CardViewItem } from '../interfaces/card-view.interfaces'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Directive() // eslint-disable-next-line @angular-eslint/directive-class-suffix -export abstract class BaseCardView implements OnDestroy { +export abstract class BaseCardView { protected cardViewUpdateService = inject(CardViewUpdateService); @Input() @@ -32,10 +31,8 @@ export abstract class BaseCardView implements OnDestroy @Input() property: T; - protected destroy$ = new Subject(); - constructor() { - this.cardViewUpdateService.updateItem$.pipe(takeUntil(this.destroy$)).subscribe((itemModel) => { + this.cardViewUpdateService.updateItem$.pipe(takeUntilDestroyed()).subscribe((itemModel) => { if (this.property.key === itemModel.key) { this.property.value = itemModel.value; } @@ -54,8 +51,4 @@ export abstract class BaseCardView implements OnDestroy return !!this.property.icon; } - ngOnDestroy(): void { - this.destroy$.next(true); - this.destroy$.complete(); - } } 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 09e236e1a7..c522a9a8cc 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, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { DatetimeAdapter, @@ -26,7 +26,6 @@ import { } from '@mat-datetimepicker/core'; import { CardViewDateItemModel } from '../../models/card-view-dateitem.model'; import { UserPreferencesService, UserPreferenceValues } from '../../../common/services/user-preferences.service'; -import { takeUntil } from 'rxjs/operators'; import { BaseCardView } from '../base-card-view'; import { ClipboardService } from '../../../clipboard/clipboard.service'; import { TranslationService } from '../../../translation/translation.service'; @@ -41,6 +40,7 @@ import { MatChipsModule } from '@angular/material/chips'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ providers: [ @@ -66,7 +66,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-card-view-dateitem' } }) -export class CardViewDateItemComponent extends BaseCardView implements OnInit, OnDestroy { +export class CardViewDateItemComponent extends BaseCardView implements OnInit { @Input() displayEmpty = true; @@ -78,6 +78,8 @@ export class CardViewDateItemComponent extends BaseCardView, private userPreferencesService: UserPreferencesService, @@ -90,7 +92,7 @@ export class CardViewDateItemComponent extends BaseCardView { this.property.locale = locale; }); @@ -104,10 +106,6 @@ export class CardViewDateItemComponent extends BaseCardView> implements OnInit, OnChanges { private appConfig = inject(AppConfigService); - private readonly destroyRef = inject(DestroyRef); static HIDE_FILTER_LIMIT = 5; @Input() options$: Observable[]>; @@ -71,6 +70,8 @@ export class CardViewSelectItemComponent extends BaseCardView(); term = ''; previousSelected: any[]; - private onDestroy$ = new Subject(); + + private readonly destroyRef = inject(DestroyRef); constructor(@Inject(MatSelect) private matSelect: MatSelect) {} @@ -51,9 +62,9 @@ export class SelectFilterInputComponent implements OnInit, OnDestroy { } ngOnInit() { - this.change.pipe(takeUntil(this.onDestroy$)).subscribe((val: string) => (this.term = val)); + this.change.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((val: string) => (this.term = val)); - this.matSelect.openedChange.pipe(takeUntil(this.onDestroy$)).subscribe((isOpened: boolean) => { + this.matSelect.openedChange.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((isOpened: boolean) => { if (isOpened) { this.selectFilterInput.nativeElement.focus(); } else { @@ -63,7 +74,7 @@ export class SelectFilterInputComponent implements OnInit, OnDestroy { if (this.matSelect.ngControl) { this.previousSelected = this.matSelect.ngControl.value; - this.matSelect.ngControl.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((values) => { + this.matSelect.ngControl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((values) => { let restoreSelection = false; if (this.matSelect.multiple && Array.isArray(this.previousSelected)) { if (!Array.isArray(values)) { @@ -109,9 +120,4 @@ export class SelectFilterInputComponent implements OnInit, OnDestroy { } } } - - ngOnDestroy() { - this.onDestroy$.next(); - this.onDestroy$.complete(); - } } 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 467238c0ce..c79e6ee51b 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 @@ -15,7 +15,16 @@ * limitations under the License. */ -import { ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + DestroyRef, + inject, + Input, + OnChanges, + SimpleChanges, + ViewEncapsulation +} from '@angular/core'; import { CardViewTextItemModel } from '../../models/card-view-textitem.model'; import { BaseCardView } from '../base-card-view'; import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips'; @@ -23,7 +32,7 @@ import { ClipboardService } from '../../../clipboard/clipboard.service'; import { TranslationService } from '../../../translation/translation.service'; import { CardViewItemValidator } from '../../interfaces/card-view-item-validator.interface'; import { FormsModule, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; -import { debounceTime, takeUntil, filter } from 'rxjs/operators'; +import { debounceTime, filter } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; import { MatFormFieldModule } from '@angular/material/form-field'; import { TranslateModule } from '@ngx-translate/core'; @@ -31,6 +40,7 @@ import { MatInputModule } from '@angular/material/input'; import { MatIconModule } from '@angular/material/icon'; import { MatButtonModule } from '@angular/material/button'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export const DEFAULT_SEPARATOR = ', '; const templateTypes = { @@ -61,7 +71,7 @@ const templateTypes = { encapsulation: ViewEncapsulation.None, host: { class: 'adf-card-view-textitem' } }) -export class CardViewTextItemComponent extends BaseCardView implements OnChanges, OnDestroy { +export class CardViewTextItemComponent extends BaseCardView implements OnChanges { @Input() displayEmpty = true; @@ -81,6 +91,8 @@ export class CardViewTextItemComponent extends BaseCardView textInputValue !== this.editedValue && textInputValue !== null), debounceTime(50), - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((textInputValue) => { this.editedValue = textInputValue; @@ -222,10 +234,6 @@ export class CardViewTextItemComponent extends BaseCardView { + this.value$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((value) => { this.boolValue = this.transformBoolean(value); }); } diff --git a/lib/core/src/lib/datatable/components/columns-selector/columns-selector.component.ts b/lib/core/src/lib/datatable/components/columns-selector/columns-selector.component.ts index c2b4e91f93..ba8b46261c 100644 --- a/lib/core/src/lib/datatable/components/columns-selector/columns-selector.component.ts +++ b/lib/core/src/lib/datatable/components/columns-selector/columns-selector.component.ts @@ -15,11 +15,10 @@ * limitations under the License. */ -import { Component, EventEmitter, inject, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; import { MatMenuTrigger } from '@angular/material/menu'; -import { Subject } from 'rxjs'; -import { debounceTime, takeUntil } from 'rxjs/operators'; +import { debounceTime } from 'rxjs/operators'; import { DataColumn } from '../../data/data-column.model'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; @@ -28,6 +27,7 @@ import { MatIconModule } from '@angular/material/icon'; import { MatDividerModule } from '@angular/material/divider'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { TranslationService } from '../../../translation'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-datatable-column-selector', @@ -37,7 +37,7 @@ import { TranslationService } from '../../../translation'; styleUrls: ['./columns-selector.component.scss'], encapsulation: ViewEncapsulation.None }) -export class ColumnsSelectorComponent implements OnInit, OnDestroy { +export class ColumnsSelectorComponent implements OnInit { private translationService = inject(TranslationService); @Input() @@ -55,21 +55,22 @@ export class ColumnsSelectorComponent implements OnInit, OnDestroy { @Output() submitColumnsVisibility = new EventEmitter(); - onDestroy$ = new Subject(); columnItems: DataColumn[] = []; searchInputControl = new UntypedFormControl(''); searchQuery = ''; + + private readonly destroyRef = inject(DestroyRef); ngOnInit(): void { - this.mainMenuTrigger.menuOpened.pipe(takeUntil(this.onDestroy$)).subscribe(() => { + this.mainMenuTrigger.menuOpened.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { this.updateColumnItems(); }); - this.mainMenuTrigger.menuClosed.pipe(takeUntil(this.onDestroy$)).subscribe(() => { + this.mainMenuTrigger.menuClosed.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { this.searchInputControl.setValue(''); }); - this.searchInputControl.valueChanges.pipe(debounceTime(300), takeUntil(this.onDestroy$)).subscribe((searchQuery) => { + this.searchInputControl.valueChanges.pipe(debounceTime(300), takeUntilDestroyed(this.destroyRef)).subscribe((searchQuery) => { this.searchQuery = searchQuery; this.updateColumnItems(); }); @@ -81,12 +82,6 @@ export class ColumnsSelectorComponent implements OnInit, OnDestroy { columns = this.sortColumns(columns); this.columnItems = columns; } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - closeMenu(): void { this.mainMenuTrigger.closeMenu(); } diff --git a/lib/core/src/lib/datatable/components/datatable-cell/datatable-cell.component.ts b/lib/core/src/lib/datatable/components/datatable-cell/datatable-cell.component.ts index f31ad460ee..88d54426b5 100644 --- a/lib/core/src/lib/datatable/components/datatable-cell/datatable-cell.component.ts +++ b/lib/core/src/lib/datatable/components/datatable-cell/datatable-cell.component.ts @@ -15,15 +15,23 @@ * limitations under the License. */ -import { ChangeDetectionStrategy, Component, Input, OnInit, ViewEncapsulation, OnDestroy, inject } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + DestroyRef, + inject, + Input, + OnInit, + ViewEncapsulation +} from '@angular/core'; import { DataColumn } from '../../data/data-column.model'; import { DataRow } from '../../data/data-row.model'; import { DataTableAdapter } from '../../data/datatable-adapter'; -import { BehaviorSubject, Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { BehaviorSubject } from 'rxjs'; import { DataTableService } from '../../services/datatable.service'; import { CommonModule } from '@angular/common'; import { ClipboardDirective } from '../../../clipboard/clipboard.directive'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-datatable-cell', @@ -49,7 +57,7 @@ import { ClipboardDirective } from '../../../clipboard/clipboard.directive'; encapsulation: ViewEncapsulation.None, host: { class: 'adf-datatable-content-cell' } }) -export class DataTableCellComponent implements OnInit, OnDestroy { +export class DataTableCellComponent implements OnInit { /** Data table adapter instance. */ @Input() data: DataTableAdapter; @@ -74,7 +82,7 @@ export class DataTableCellComponent implements OnInit, OnDestroy { @Input() resolverFn: (row: DataRow, col: DataColumn) => any = null; - protected onDestroy$ = new Subject(); + protected destroyRef = inject(DestroyRef); protected dataTableService = inject(DataTableService, { optional: true }); value$ = new BehaviorSubject(''); @@ -100,7 +108,7 @@ export class DataTableCellComponent implements OnInit, OnDestroy { return; } - this.dataTableService.rowUpdate.pipe(takeUntil(this.onDestroy$)).subscribe((data) => { + this.dataTableService.rowUpdate.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data) => { if (data?.id === this.row?.id && data.obj) { this.row.obj = data.obj; this.row['cache'][this.column.key] = this.getNestedPropertyValue(data.obj, this.column.key); @@ -113,9 +121,4 @@ export class DataTableCellComponent implements OnInit, OnDestroy { private getNestedPropertyValue(obj: any, path: string) { return path.split('.').reduce((source, key) => (source ? source[key] : ''), obj); } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 66ddc4a973..7967780f8c 100644 --- a/lib/core/src/lib/datatable/components/datatable/datatable.component.ts +++ b/lib/core/src/lib/datatable/components/datatable/datatable.component.ts @@ -22,10 +22,12 @@ import { AfterViewInit, Component, ContentChild, + DestroyRef, DoCheck, ElementRef, EventEmitter, HostListener, + inject, Input, IterableDiffers, OnChanges, @@ -79,6 +81,7 @@ import { BooleanCellComponent } from '../boolean-cell/boolean-cell.component'; import { JsonCellComponent } from '../json-cell/json-cell.component'; import { AmountCellComponent } from '../amount-cell/amount-cell.component'; import { NumberCellComponent } from '../number-cell/number-cell.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; // eslint-disable-next-line no-shadow export enum ShowHeaderMode { @@ -325,10 +328,10 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges, private differ: any; private rowMenuCache: any = {}; - private subscriptions: Subscription[] = []; private singleClickStreamSub: Subscription; private multiClickStreamSub: Subscription; - private dataRowsChanged: Subscription; + + private readonly destroyRef = inject(DestroyRef); @HostListener('keyup', ['$event']) onKeydown(event: KeyboardEvent): void { @@ -349,11 +352,9 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges, ngAfterContentInit() { if (this.columnList) { - this.subscriptions.push( - this.columnList.columns.changes.subscribe(() => { - this.setTableSchema(); - }) - ); + this.columnList.columns.changes.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { + this.setTableSchema(); + }); } this.setTableSchema(); } @@ -713,7 +714,7 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges, this.isSelectAllIndeterminate = false; if (this.multiselect) { - const selectableRows = this.data.getRows().filter(row => row?.isSelectable); + const selectableRows = this.data.getRows().filter((row) => row?.isSelectable); if (selectableRows && selectableRows.length > 0) { for (let i = 0; i < selectableRows.length; i++) { this.selectRow(selectableRows[i], matCheckboxChange.checked); @@ -954,14 +955,6 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges, ngOnDestroy() { this.unsubscribeClickStream(); - - this.subscriptions.forEach((s) => s.unsubscribe()); - this.subscriptions = []; - - if (this.dataRowsChanged) { - this.dataRowsChanged.unsubscribe(); - this.dataRowsChanged = null; - } } getNameColumnValue() { diff --git a/lib/core/src/lib/datatable/components/icon-cell/icon-cell.component.ts b/lib/core/src/lib/datatable/components/icon-cell/icon-cell.component.ts index aa21ee3237..77aae77cec 100644 --- a/lib/core/src/lib/datatable/components/icon-cell/icon-cell.component.ts +++ b/lib/core/src/lib/datatable/components/icon-cell/icon-cell.component.ts @@ -19,7 +19,7 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewEncapsulation } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import { DataTableCellComponent } from '../datatable-cell/datatable-cell.component'; -import { takeUntil } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ standalone: true, @@ -43,7 +43,7 @@ export class IconCellComponent extends DataTableCellComponent implements OnInit ngOnInit(): void { super.ngOnInit(); - this.value$.pipe(takeUntil(this.onDestroy$)).subscribe((value) => { + this.value$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((value) => { const newIcon = this.validateIconValue(value) ? value : ''; if (this.icon !== newIcon) { this.icon = newIcon; 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 db114314da..9188eef6e7 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 @@ -16,7 +16,7 @@ */ import { TestBed } from '@angular/core/testing'; -import { ElementRef, NgZone, Renderer2 } from '@angular/core'; +import { ElementRef, Injector, NgZone, Renderer2, runInInjectionContext } from '@angular/core'; import { ResizableDirective } from './resizable.directive'; describe('ResizableDirective', () => { @@ -64,9 +64,12 @@ describe('ResizableDirective', () => { element = TestBed.inject(ElementRef); renderer = TestBed.inject(Renderer2); ngZone = TestBed.inject(NgZone); + const injector = TestBed.inject(Injector); spyOn(ngZone, 'runOutsideAngular').and.callFake((fn) => fn()); spyOn(ngZone, 'run').and.callFake((fn) => fn()); - directive = new ResizableDirective(renderer, element, ngZone); + runInInjectionContext(injector, () => { + directive = new ResizableDirective(renderer, element, ngZone); + }); 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 adcfcc217e..373fb298ea 100644 --- a/lib/core/src/lib/datatable/directives/resizable/resizable.directive.ts +++ b/lib/core/src/lib/datatable/directives/resizable/resizable.directive.ts @@ -15,10 +15,11 @@ * limitations under the License. */ -import { Subject, Observable, Observer, merge } from 'rxjs'; -import { BoundingRectangle, ResizeEvent, IResizeMouseEvent, ICoordinateX } from './types'; -import { map, take, share, filter, pairwise, mergeMap, takeUntil } from 'rxjs/operators'; -import { OnInit, Output, NgZone, OnDestroy, Directive, Renderer2, ElementRef, EventEmitter, Input } from '@angular/core'; +import { merge, Observable, Observer, Subject } from 'rxjs'; +import { BoundingRectangle, ICoordinateX, IResizeMouseEvent, ResizeEvent } from './types'; +import { filter, map, mergeMap, pairwise, share, take, takeUntil } from 'rxjs/operators'; +import { DestroyRef, Directive, ElementRef, EventEmitter, inject, Input, NgZone, OnDestroy, OnInit, Output, Renderer2 } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Directive({ selector: '[adf-resizable]', @@ -64,7 +65,7 @@ export class ResizableDirective implements OnInit, OnDestroy { private unsubscribeMouseMove?: () => void; private unsubscribeMouseUp?: () => void; - private destroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private readonly renderer: Renderer2, private readonly element: ElementRef, private readonly zone: NgZone) { this.pointerDown = new Observable((observer: Observer) => { @@ -133,7 +134,7 @@ export class ResizableDirective implements OnInit, OnDestroy { .pipe( map(({ resize = false }) => resize), filter((resize) => resize), - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe(() => { const startingRect: BoundingRectangle = this.getElementRect(this.element); @@ -151,7 +152,7 @@ export class ResizableDirective implements OnInit, OnDestroy { } }); - mouseup$.pipe(takeUntil(this.destroy$)).subscribe(() => { + mouseup$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { if (this.currentRect) { this.renderer.setStyle(document.body, 'cursor', ''); if (this.resizeEnd.observers.length > 0) { @@ -172,7 +173,6 @@ export class ResizableDirective implements OnInit, OnDestroy { this.unsubscribeMouseDown?.(); this.unsubscribeMouseMove?.(); this.unsubscribeMouseUp?.(); - this.destroy$.next(); } private getNewBoundingRectangle({ top, bottom, left, right }: BoundingRectangle, clientX: number): BoundingRectangle { 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 01bbbdee45..de3308b443 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 @@ -15,9 +15,8 @@ * limitations under the License. */ -import { Subject } from 'rxjs'; import { ResizableDirective } from './resizable.directive'; -import { Input, OnInit, Directive, Renderer2, ElementRef, OnDestroy, NgZone } from '@angular/core'; +import { Directive, ElementRef, Input, NgZone, OnDestroy, OnInit, Renderer2 } from '@angular/core'; @Directive({ selector: '[adf-resize-handle]', @@ -32,9 +31,6 @@ export class ResizeHandleDirective implements OnInit, OnDestroy { private unlistenMouseDown?: () => void; private unlistenMouseMove?: () => void; private unlistenMouseUp?: () => void; - - private destroy$ = new Subject(); - constructor(private readonly renderer: Renderer2, private readonly element: ElementRef, private readonly zone: NgZone) {} ngOnInit(): void { @@ -49,7 +45,6 @@ export class ResizeHandleDirective implements OnInit, OnDestroy { this.unlistenMouseDown?.(); this.unlistenMouseMove?.(); this.unlistenMouseUp?.(); - this.destroy$.next(); } private onMousedown(event: MouseEvent): void { diff --git a/lib/core/src/lib/dialogs/dialog/dialog.component.ts b/lib/core/src/lib/dialogs/dialog/dialog.component.ts index 11d9c62b4a..888c03c69d 100644 --- a/lib/core/src/lib/dialogs/dialog/dialog.component.ts +++ b/lib/core/src/lib/dialogs/dialog/dialog.component.ts @@ -15,16 +15,16 @@ * limitations under the License. */ -import { Component, Inject, InjectionToken, Injector, OnDestroy, ViewEncapsulation } from '@angular/core'; +import { Component, Inject, InjectionToken, Injector, ViewEncapsulation } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { AdditionalDialogActionButton, DialogData } from './dialog-data.interface'; -import { BehaviorSubject, Subject } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; import { DialogSize, DialogSizes } from './dialog.model'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; -import { takeUntil } from 'rxjs/operators'; import { MatIconModule } from '@angular/material/icon'; import { MatButtonModule } from '@angular/material/button'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export const DIALOG_COMPONENT_DATA = new InjectionToken('dialog component data'); @@ -36,7 +36,7 @@ export const DIALOG_COMPONENT_DATA = new InjectionToken('dialog component d imports: [CommonModule, TranslateModule, MatIconModule, MatDialogModule, MatButtonModule], encapsulation: ViewEncapsulation.None }) -export class DialogComponent implements OnDestroy { +export class DialogComponent { isConfirmButtonDisabled$ = new BehaviorSubject(false); isCloseButtonHidden: boolean; isCancelButtonHidden: boolean; @@ -48,8 +48,6 @@ export class DialogComponent implements OnDestroy { dataInjector: Injector; - private onDestroy$ = new Subject(); - constructor( @Inject(MAT_DIALOG_DATA) public data: DialogData, @@ -68,11 +66,11 @@ export class DialogComponent implements OnDestroy { }); if (data.isConfirmButtonDisabled$) { - data.isConfirmButtonDisabled$.pipe(takeUntil(this.onDestroy$)).subscribe((value) => this.isConfirmButtonDisabled$.next(value)); + data.isConfirmButtonDisabled$.pipe(takeUntilDestroyed()).subscribe((value) => this.isConfirmButtonDisabled$.next(value)); } if (data.dataOnConfirm$) { - data.dataOnConfirm$.pipe(takeUntil(this.onDestroy$)).subscribe((value) => (this.dataOnConfirm = value)); + data.dataOnConfirm$.pipe(takeUntilDestroyed()).subscribe((value) => (this.dataOnConfirm = value)); } } } @@ -81,9 +79,4 @@ export class DialogComponent implements OnDestroy { this.isConfirmButtonDisabled$.next(true); this.dialogRef.close(this.dataOnConfirm || true); } - - ngOnDestroy() { - this.onDestroy$.next(); - this.onDestroy$.complete(); - } } 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 b4b3b84b91..77be13fd4c 100644 --- a/lib/core/src/lib/directives/infinite-select-scroll.directive.ts +++ b/lib/core/src/lib/directives/infinite-select-scroll.directive.ts @@ -15,10 +15,9 @@ * limitations under the License. */ -import { Inject, AfterViewInit, Directive, EventEmitter, OnDestroy, Output } from '@angular/core'; +import { AfterViewInit, DestroyRef, Directive, EventEmitter, inject, Inject, Output } from '@angular/core'; import { MatSelect } from '@angular/material/select'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const SELECT_ITEM_HEIGHT_EM = 3; @@ -26,19 +25,19 @@ const SELECT_ITEM_HEIGHT_EM = 3; selector: '[adf-infinite-select-scroll]', standalone: true }) -export class InfiniteSelectScrollDirective implements AfterViewInit, OnDestroy { +export class InfiniteSelectScrollDirective implements AfterViewInit { static readonly MAX_ITEMS = 50; /** Emitted when scroll reaches the last item. */ @Output() scrollEnd = new EventEmitter(); - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); private itemHeightToWaitBeforeLoadNext = 0; constructor(@Inject(MatSelect) private matSelect: MatSelect) {} ngAfterViewInit() { - this.matSelect.openedChange.pipe(takeUntil(this.onDestroy$)).subscribe((opened: boolean) => { + this.matSelect.openedChange.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((opened: boolean) => { if (opened) { this.itemHeightToWaitBeforeLoadNext = this.getItemHeight() * (InfiniteSelectScrollDirective.MAX_ITEMS / 2); this.matSelect.panel.nativeElement.addEventListener('scroll', (event: Event) => this.handleScrollEvent(event)); @@ -46,11 +45,6 @@ export class InfiniteSelectScrollDirective implements AfterViewInit, OnDestroy { }); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - private handleScrollEvent(event: Event) { if (this.isScrollInNextFetchArea(event)) { this.scrollEnd.emit(event); 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 57b830d1a0..bb60bf4764 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 @@ -38,7 +38,6 @@ import { MatButtonModule } from '@angular/material/button'; import { MatChip, MatChipsModule } from '@angular/material/chips'; import { MatIconModule } from '@angular/material/icon'; import { TranslateModule } from '@ngx-translate/core'; -import { Subject } from 'rxjs'; import { Chip } from './chip'; /** @@ -98,7 +97,6 @@ export class DynamicChipListComponent implements OnChanges, OnInit, AfterViewIni paginationData: Pagination; private initialChips: Chip[] = []; - private onDestroy$ = new Subject(); private initialLimitChipsDisplayed: boolean; private viewMoreButtonLeftOffsetBeforeFlexDirection: number; private requestedDisplayingAllChips = false; @@ -139,8 +137,6 @@ export class DynamicChipListComponent implements OnChanges, OnInit, AfterViewIni } ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); this.resizeObserver.unobserve(this.containerView.nativeElement); } diff --git a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts index a76849b45e..c33e16f3d5 100644 --- a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts +++ b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts @@ -18,20 +18,25 @@ /* eslint-disable @angular-eslint/component-selector */ import { NgIf } from '@angular/common'; -import { Component, inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { FormControl, ReactiveFormsModule, ValidationErrors, Validators } from '@angular/forms'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { DatetimeAdapter, MAT_DATETIME_FORMATS, MatDatetimepickerModule } from '@mat-datetimepicker/core'; import { TranslateModule } from '@ngx-translate/core'; -import { ADF_DATE_FORMATS, ADF_DATETIME_FORMATS, AdfDateFnsAdapter, AdfDateTimeFnsAdapter, DateFnsUtils } from '../../../../common'; +import { + ADF_DATE_FORMATS, + ADF_DATETIME_FORMATS, + AdfDateFnsAdapter, + AdfDateTimeFnsAdapter, + DateFnsUtils +} from '../../../../common'; import { FormService } from '../../../services/form.service'; import { ErrorWidgetComponent } from '../error/error.component'; import { WidgetComponent } from '../widget.component'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { ErrorMessageModel } from '../core/error-message.model'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'date-time-widget', @@ -47,14 +52,15 @@ import { ErrorMessageModel } from '../core/error-message.model'; imports: [NgIf, TranslateModule, MatFormFieldModule, MatInputModule, MatDatetimepickerModule, ReactiveFormsModule, ErrorWidgetComponent], encapsulation: ViewEncapsulation.None }) -export class DateTimeWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { +export class DateTimeWidgetComponent extends WidgetComponent implements OnInit { minDate: Date; maxDate: Date; datetimeInputControl: FormControl = new FormControl(null); - private onDestroy$ = new Subject(); public readonly formService = inject(FormService); + + private readonly destroyRef = inject(DestroyRef); private readonly dateAdapter = inject(DateAdapter); private readonly dateTimeAdapter = inject(DatetimeAdapter); @@ -82,7 +88,7 @@ export class DateTimeWidgetComponent extends WidgetComponent implements OnInit, } private subscribeToDateChanges(): void { - this.datetimeInputControl.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((newDate: Date) => { + this.datetimeInputControl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((newDate: Date) => { this.field.value = newDate; this.updateField(); }); @@ -151,9 +157,4 @@ export class DateTimeWidgetComponent extends WidgetComponent implements OnInit, this.maxDate = DateFnsUtils.getDate(this.field.maxValue); } } - - ngOnDestroy(): void { - this.onDestroy$.next(); - this.onDestroy$.complete(); - } } diff --git a/lib/core/src/lib/form/components/widgets/date/date.widget.ts b/lib/core/src/lib/form/components/widgets/date/date.widget.ts index 918d44b2d2..52b972586d 100644 --- a/lib/core/src/lib/form/components/widgets/date/date.widget.ts +++ b/lib/core/src/lib/form/components/widgets/date/date.widget.ts @@ -18,21 +18,20 @@ /* eslint-disable @angular-eslint/component-selector */ import { NgIf } from '@angular/common'; -import { Component, inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { FormControl, ReactiveFormsModule, ValidationErrors, Validators } from '@angular/forms'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { TranslateModule } from '@ngx-translate/core'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { ADF_DATE_FORMATS, AdfDateFnsAdapter, DateFnsUtils, DEFAULT_DATE_FORMAT } from '../../../../common'; import { FormService } from '../../../services/form.service'; import { ErrorWidgetComponent } from '../error/error.component'; import { WidgetComponent } from '../widget.component'; import { ErrorMessageModel } from '../core/error-message.model'; import { parseISO } from 'date-fns'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'date-widget', @@ -56,17 +55,17 @@ import { parseISO } from 'date-fns'; imports: [MatFormFieldModule, TranslateModule, MatInputModule, MatDatepickerModule, ReactiveFormsModule, ErrorWidgetComponent, NgIf], encapsulation: ViewEncapsulation.None }) -export class DateWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { +export class DateWidgetComponent extends WidgetComponent implements OnInit { minDate: Date; maxDate: Date; startAt: Date; dateInputControl: FormControl = new FormControl(null); - private onDestroy$ = new Subject(); - public readonly formService = inject(FormService); + private readonly dateAdapter = inject(DateAdapter); + private readonly destroyRef = inject(DestroyRef); ngOnInit(): void { this.patchFormControl(); @@ -92,7 +91,7 @@ export class DateWidgetComponent extends WidgetComponent implements OnInit, OnDe } private subscribeToDateChanges(): void { - this.dateInputControl.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((newDate: Date) => { + this.dateInputControl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((newDate: Date) => { this.field.value = newDate; this.updateField(); }); @@ -164,9 +163,4 @@ export class DateWidgetComponent extends WidgetComponent implements OnInit, OnDe this.startAt = this.dateAdapter.parse(this.field.value, DEFAULT_DATE_FORMAT); } } - - ngOnDestroy(): void { - this.onDestroy$.next(); - this.onDestroy$.complete(); - } } diff --git a/lib/core/src/lib/identity-user-info/identity-user-info.component.ts b/lib/core/src/lib/identity-user-info/identity-user-info.component.ts index ecb1218dcd..2fdcb231bc 100644 --- a/lib/core/src/lib/identity-user-info/identity-user-info.component.ts +++ b/lib/core/src/lib/identity-user-info/identity-user-info.component.ts @@ -15,10 +15,9 @@ * limitations under the License. */ -import { Component, Input, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, Input, ViewChild, ViewEncapsulation } from '@angular/core'; import { MatMenuModule, MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu'; import { IdentityUserModel } from '../auth/models/identity-user.model'; -import { Subject } from 'rxjs'; import { CommonModule } from '@angular/common'; import { FullNamePipe, InitialUsernamePipe } from '../pipes'; import { MatButtonModule } from '@angular/material/button'; @@ -33,7 +32,7 @@ import { TranslateModule } from '@ngx-translate/core'; styleUrls: ['./identity-user-info.component.scss'], encapsulation: ViewEncapsulation.None }) -export class IdentityUserInfoComponent implements OnDestroy { +export class IdentityUserInfoComponent { @ViewChild(MatMenuTrigger) trigger: MatMenuTrigger; /** Is the user logged in */ @@ -67,13 +66,6 @@ export class IdentityUserInfoComponent implements OnDestroy { @Input() namePosition: 'right' | 'left' = 'right'; - private destroy$ = new Subject(); - - ngOnDestroy(): void { - this.destroy$.next(true); - this.destroy$.complete(); - } - onKeyPress(event: KeyboardEvent) { this.closeUserModal(event); } 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 c57637119e..2f84c74564 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 @@ -16,29 +16,31 @@ */ import { + AfterViewInit, + ChangeDetectorRef, Component, ContentChild, - Input, - Output, - OnInit, - AfterViewInit, - ViewChild, - OnDestroy, - TemplateRef, + DestroyRef, EventEmitter, - ViewEncapsulation, - ChangeDetectorRef + inject, + Input, + OnDestroy, + OnInit, + Output, + TemplateRef, + ViewChild, + ViewEncapsulation } from '@angular/core'; import { MediaMatcher } from '@angular/cdk/layout'; import { UserPreferencesService } from '../../../common/services/user-preferences.service'; import { SidenavLayoutContentDirective } from '../../directives/sidenav-layout-content.directive'; import { SidenavLayoutHeaderDirective } from '../../directives/sidenav-layout-header.directive'; import { SidenavLayoutNavigationDirective } from '../../directives/sidenav-layout-navigation.directive'; -import { BehaviorSubject, Subject } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; import { Direction } from '@angular/cdk/bidi'; -import { takeUntil } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; import { LayoutContainerComponent } from '../layout-container/layout-container.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-sidenav-layout', @@ -99,7 +101,7 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy isMenuMinimized: () => this.isMenuMinimized }; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor( private readonly mediaMatcher: MediaMatcher, @@ -120,7 +122,7 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy this.userPreferencesService .select('textOrientation') - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((direction: Direction) => { this.dir = direction; }); @@ -132,8 +134,6 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy ngOnDestroy(): void { this.mediaQueryList.removeListener(this.onMediaQueryChange); - this.onDestroy$.next(true); - this.onDestroy$.complete(); } toggleMenu() { 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 5e05209be0..abc844f002 100644 --- a/lib/core/src/lib/login/components/login/login.component.ts +++ b/lib/core/src/lib/login/components/login/login.component.ts @@ -16,7 +16,7 @@ */ import { CommonModule } from '@angular/common'; -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, TemplateRef, ViewEncapsulation } from '@angular/core'; import { AbstractControl, ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -27,8 +27,6 @@ import { MatInputModule } from '@angular/material/input'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { AppConfigService, AppConfigValues } from '../../../app-config'; import { AuthenticationService, BasicAlfrescoAuthService } from '../../../auth'; import { OidcAuthenticationService } from '../../../auth/oidc/oidc-authentication.service'; @@ -38,6 +36,7 @@ import { TranslationService } from '../../../translation'; import { LoginErrorEvent } from '../../models/login-error.event'; import { LoginSubmitEvent } from '../../models/login-submit.event'; import { LoginSuccessEvent } from '../../models/login-success.event'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; // eslint-disable-next-line no-shadow enum LoginSteps { @@ -76,7 +75,7 @@ interface LoginFormValues { ], host: { class: 'adf-login' } }) -export class LoginComponent implements OnInit, OnDestroy { +export class LoginComponent implements OnInit { isPasswordShow: boolean = false; /** @@ -146,7 +145,8 @@ export class LoginComponent implements OnInit, OnDestroy { data: any; private _message: { [id: string]: { [id: string]: ValidationMessage } }; - private onDestroy$ = new Subject(); + + private readonly destroyRef = inject(DestroyRef); constructor( private _fb: UntypedFormBuilder, @@ -191,12 +191,7 @@ export class LoginComponent implements OnInit, OnDestroy { this.form = this._fb.group(this.fieldsValidation); } - this.form.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((data) => this.onValueChanged(data)); - } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); + this.form.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data) => this.onValueChanged(data)); } submit() { 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 24483a1cc5..8e0dc1fa0d 100644 --- a/lib/core/src/lib/notifications/components/notification-history.component.ts +++ b/lib/core/src/lib/notifications/components/notification-history.component.ts @@ -15,12 +15,20 @@ * limitations under the License. */ -import { AfterViewInit, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectorRef, + Component, + DestroyRef, + inject, + Input, + OnInit, + ViewChild, + ViewEncapsulation +} from '@angular/core'; import { NotificationService } from '../services/notification.service'; import { NOTIFICATION_TYPE, NotificationModel } from '../models/notification.model'; import { MatMenuModule, MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; import { StorageService } from '../../common/services/storage.service'; import { PaginationModel } from '../../models/pagination.model'; import { MatButtonModule } from '@angular/material/button'; @@ -31,6 +39,7 @@ import { MatListModule } from '@angular/material/list'; import { NgForOf, NgIf } from '@angular/common'; import { InitialUsernamePipe, TimeAgoPipe } from '../../pipes'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-notification-history', @@ -52,7 +61,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar'; ], encapsulation: ViewEncapsulation.None }) -export class NotificationHistoryComponent implements OnDestroy, OnInit, AfterViewInit { +export class NotificationHistoryComponent implements OnInit, AfterViewInit { public static MAX_NOTIFICATION_STACK_LENGTH = 100; public static NOTIFICATION_STORAGE = 'notification-history'; @@ -71,11 +80,12 @@ export class NotificationHistoryComponent implements OnDestroy, OnInit, AfterVie @Input() maxNotifications: number = 5; - onDestroy$ = new Subject(); notifications: NotificationModel[] = []; paginatedNotifications: NotificationModel[] = []; pagination: PaginationModel; + private readonly destroyRef = inject(DestroyRef); + constructor(private notificationService: NotificationService, public storageService: StorageService, public cd: ChangeDetectorRef) {} ngOnInit() { @@ -83,17 +93,12 @@ export class NotificationHistoryComponent implements OnDestroy, OnInit, AfterVie } ngAfterViewInit(): void { - this.notificationService.notifications$.pipe(takeUntil(this.onDestroy$)).subscribe((notification: NotificationModel) => { + this.notificationService.notifications$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((notification: NotificationModel) => { this.addNewNotification(notification); this.cd.detectChanges(); }); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - addNewNotification(notification: NotificationModel) { this.notifications.unshift(notification); diff --git a/lib/core/src/lib/pagination/infinite-pagination.component.ts b/lib/core/src/lib/pagination/infinite-pagination.component.ts index 67d50aba67..5915b9f926 100644 --- a/lib/core/src/lib/pagination/infinite-pagination.component.ts +++ b/lib/core/src/lib/pagination/infinite-pagination.component.ts @@ -22,25 +22,25 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + DestroyRef, EventEmitter, + inject, Input, OnInit, Output, - OnDestroy, ViewEncapsulation } from '@angular/core'; import { PaginatedComponent } from './paginated-component.interface'; -import { Subject } from 'rxjs'; import { PaginationComponentInterface } from './pagination-component.interface'; import { RequestPaginationModel } from '../models/request-pagination.model'; import { UserPreferencesService, UserPreferenceValues } from '../common/services/user-preferences.service'; import { PaginationModel } from '../models/pagination.model'; -import { takeUntil } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { TranslateModule } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-infinite-pagination', @@ -52,7 +52,7 @@ import { TranslateModule } from '@ngx-translate/core'; standalone: true, imports: [CommonModule, MatButtonModule, MatProgressBarModule, TranslateModule] }) -export class InfinitePaginationComponent implements OnInit, OnDestroy, PaginationComponentInterface { +export class InfinitePaginationComponent implements OnInit, PaginationComponentInterface { static DEFAULT_PAGINATION: PaginationModel = new PaginationModel({ skipCount: 0, maxItems: 25, @@ -60,14 +60,13 @@ export class InfinitePaginationComponent implements OnInit, OnDestroy, Paginatio }); _target: PaginatedComponent; - private onDestroy$ = new Subject(); /** Component that provides custom pagination support. */ @Input() set target(target: PaginatedComponent) { if (target) { this._target = target; - target.pagination.pipe(takeUntil(this.onDestroy$)).subscribe((pagination) => { + target.pagination.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((pagination) => { this.isLoading = false; this.pagination = pagination; @@ -103,12 +102,14 @@ export class InfinitePaginationComponent implements OnInit, OnDestroy, Paginatio merge: true }; + private readonly destroyRef = inject(DestroyRef); + constructor(private cdr: ChangeDetectorRef, private userPreferencesService: UserPreferencesService) {} ngOnInit() { this.userPreferencesService .select(UserPreferenceValues.PaginationSize) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((pageSize: number) => { this.pageSize = this.pageSize || pageSize; this.requestPaginationModel.maxItems = this.pageSize; @@ -137,9 +138,4 @@ export class InfinitePaginationComponent implements OnInit, OnDestroy, Paginatio this._target.updatePagination(this.pagination); } } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } diff --git a/lib/core/src/lib/pagination/pagination.component.ts b/lib/core/src/lib/pagination/pagination.component.ts index 9d1e238ed0..91943f1c9d 100644 --- a/lib/core/src/lib/pagination/pagination.component.ts +++ b/lib/core/src/lib/pagination/pagination.component.ts @@ -16,29 +16,29 @@ */ import { + ChangeDetectionStrategy, + ChangeDetectorRef, Component, + DestroyRef, + ElementRef, EventEmitter, + inject, Input, OnInit, Output, - ViewEncapsulation, - OnDestroy, - ElementRef, - ChangeDetectionStrategy, - ChangeDetectorRef, - Renderer2 + Renderer2, + ViewEncapsulation } from '@angular/core'; import { PaginatedComponent } from './paginated-component.interface'; import { PaginationComponentInterface } from './pagination-component.interface'; -import { Subject } from 'rxjs'; import { PaginationModel } from '../models/pagination.model'; import { UserPreferencesService, UserPreferenceValues } from '../common/services/user-preferences.service'; -import { takeUntil } from 'rxjs/operators'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatMenuModule } from '@angular/material/menu'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export type PaginationAction = 'NEXT_PAGE' | 'PREV_PAGE' | 'CHANGE_PAGE_SIZE' | 'CHANGE_PAGE_NUMBER'; @@ -60,7 +60,7 @@ export const DEFAULT_PAGINATION: PaginationModel = { standalone: true, imports: [CommonModule, TranslateModule, MatButtonModule, MatIconModule, MatMenuModule] }) -export class PaginationComponent implements OnInit, OnDestroy, PaginationComponentInterface { +export class PaginationComponent implements OnInit, PaginationComponentInterface { private _pagination: PaginationModel; private _isEmpty = true; private _hasItems = false; @@ -117,7 +117,7 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone @Output() prevPage = new EventEmitter(); - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor( private elementRef: ElementRef, @@ -130,7 +130,7 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone ngOnInit() { this.userPreferencesService .select(UserPreferenceValues.PaginationSize) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((maxItems) => { this.pagination = { ...DEFAULT_PAGINATION, @@ -144,7 +144,7 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone } if (this.target) { - this.target.pagination.pipe(takeUntil(this.onDestroy$)).subscribe((pagination) => { + this.target.pagination.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((pagination) => { if (pagination.count === 0 && !this.isFirstPage) { this.goPrevious(); } @@ -303,12 +303,6 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone this.userPreferencesService.paginationSize = maxItems; this.handlePaginationEvent('CHANGE_PAGE_SIZE'); } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - handlePaginationEvent(action: PaginationAction) { const paginationModel = { ...this.pagination }; 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 f87610ce5e..edd447a056 100644 --- a/lib/core/src/lib/pipes/decimal-number.pipe.spec.ts +++ b/lib/core/src/lib/pipes/decimal-number.pipe.spec.ts @@ -16,11 +16,12 @@ */ 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 { CoreTestingModule } from '../testing/core.testing.module'; import { DecimalNumberPipe } from './decimal-number.pipe'; +import { Injector, runInInjectionContext } from '@angular/core'; +import { AppConfigService } from '@alfresco/adf-core'; describe('DecimalNumberPipe', () => { let pipe: DecimalNumberPipe; @@ -31,8 +32,11 @@ describe('DecimalNumberPipe', () => { imports: [CoreTestingModule] }); userPreferences = TestBed.inject(UserPreferencesService); + const injector = TestBed.inject(Injector); spyOn(userPreferences, 'select').and.returnValue(of('')); - pipe = new DecimalNumberPipe(userPreferences, TestBed.inject(AppConfigService)); + runInInjectionContext(injector, () => { + pipe = new DecimalNumberPipe(userPreferences, TestBed.inject(AppConfigService)); + }); }); 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 2e823ceb14..809086f049 100644 --- a/lib/core/src/lib/pipes/decimal-number.pipe.ts +++ b/lib/core/src/lib/pipes/decimal-number.pipe.ts @@ -16,19 +16,18 @@ */ import { DecimalPipe } from '@angular/common'; -import { Pipe, PipeTransform, OnDestroy } from '@angular/core'; +import { Pipe, PipeTransform } from '@angular/core'; import { AppConfigService } from '../app-config/app-config.service'; import { UserPreferencesService, UserPreferenceValues } from '../common/services/user-preferences.service'; import { DecimalNumberModel } from '../models/decimal-number.model'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Pipe({ name: 'adfDecimalNumber', pure: false, standalone: true }) -export class DecimalNumberPipe implements PipeTransform, OnDestroy { +export class DecimalNumberPipe implements PipeTransform { static DEFAULT_LOCALE = 'en-US'; static DEFAULT_MIN_INTEGER_DIGITS = 1; static DEFAULT_MIN_FRACTION_DIGITS = 0; @@ -38,14 +37,11 @@ export class DecimalNumberPipe implements PipeTransform, OnDestroy { defaultMinIntegerDigits: number = DecimalNumberPipe.DEFAULT_MIN_INTEGER_DIGITS; defaultMinFractionDigits: number = DecimalNumberPipe.DEFAULT_MIN_FRACTION_DIGITS; defaultMaxFractionDigits: number = DecimalNumberPipe.DEFAULT_MAX_FRACTION_DIGITS; - - onDestroy$: Subject = new Subject(); - constructor(public userPreferenceService?: UserPreferencesService, public appConfig?: AppConfigService) { if (this.userPreferenceService) { this.userPreferenceService .select(UserPreferenceValues.Locale) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed()) .subscribe((locale) => { if (locale) { this.defaultLocale = locale; @@ -82,9 +78,4 @@ export class DecimalNumberPipe implements PipeTransform, OnDestroy { return decimalPipe.transform(value, actualDigitsInfo); } } - - ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } diff --git a/lib/core/src/lib/pipes/localized-date.pipe.ts b/lib/core/src/lib/pipes/localized-date.pipe.ts index 0592125fe2..0364941dbd 100644 --- a/lib/core/src/lib/pipes/localized-date.pipe.ts +++ b/lib/core/src/lib/pipes/localized-date.pipe.ts @@ -28,7 +28,6 @@ import { takeUntil } from 'rxjs/operators'; pure: false }) export class LocalizedDatePipe implements PipeTransform, OnDestroy { - static DEFAULT_LOCALE = 'en-US'; static DEFAULT_DATE_FORMAT = 'mediumDate'; @@ -37,14 +36,12 @@ export class LocalizedDatePipe implements PipeTransform, OnDestroy { private onDestroy$ = new Subject(); - constructor(public userPreferenceService?: UserPreferencesService, - public appConfig?: AppConfigService) { - + constructor(public userPreferenceService?: UserPreferencesService, public appConfig?: AppConfigService) { if (this.userPreferenceService) { this.userPreferenceService .select(UserPreferenceValues.Locale) .pipe(takeUntil(this.onDestroy$)) - .subscribe(locale => { + .subscribe((locale) => { if (locale) { this.defaultLocale = locale; } @@ -67,5 +64,4 @@ export class LocalizedDatePipe implements PipeTransform, OnDestroy { this.onDestroy$.next(true); this.onDestroy$.complete(); } - } 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 87fc122a5c..854c56422b 100644 --- a/lib/core/src/lib/pipes/time-ago.pipe.spec.ts +++ b/lib/core/src/lib/pipes/time-ago.pipe.spec.ts @@ -21,6 +21,7 @@ import { AppConfigService } from '../app-config/app-config.service'; import { UserPreferencesService } from '../common/services/user-preferences.service'; import { CoreTestingModule } from '../testing/core.testing.module'; import { of } from 'rxjs'; +import { Injector, runInInjectionContext } from '@angular/core'; describe('TimeAgoPipe', () => { let pipe: TimeAgoPipe; @@ -31,8 +32,11 @@ describe('TimeAgoPipe', () => { imports: [CoreTestingModule] }); userPreferences = TestBed.inject(UserPreferencesService); + const injector = TestBed.inject(Injector); spyOn(userPreferences, 'select').and.returnValue(of('')); - pipe = new TimeAgoPipe(userPreferences, TestBed.inject(AppConfigService)); + runInInjectionContext(injector, () => { + pipe = new TimeAgoPipe(userPreferences, TestBed.inject(AppConfigService)); + }); }); 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 df0eae8c34..4ce1919260 100644 --- a/lib/core/src/lib/pipes/time-ago.pipe.ts +++ b/lib/core/src/lib/pipes/time-ago.pipe.ts @@ -15,58 +15,46 @@ * limitations under the License. */ -import { Pipe, PipeTransform, OnDestroy } from '@angular/core'; +import { Pipe, PipeTransform } from '@angular/core'; import { AppConfigService } from '../app-config/app-config.service'; -import { UserPreferenceValues, UserPreferencesService } from '../common/services/user-preferences.service'; +import { UserPreferencesService, UserPreferenceValues } from '../common/services/user-preferences.service'; import { DatePipe } from '@angular/common'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { differenceInDays, formatDistance } from 'date-fns'; import * as Locales from 'date-fns/locale'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Pipe({ standalone: true, name: 'adfTimeAgo' }) -export class TimeAgoPipe implements PipeTransform, OnDestroy { - +export class TimeAgoPipe implements PipeTransform { static DEFAULT_LOCALE = 'en-US'; static DEFAULT_DATE_TIME_FORMAT = 'dd/MM/yyyy HH:mm'; defaultLocale: string; defaultDateTimeFormat: string; - private onDestroy$ = new Subject(); - - constructor( - public userPreferenceService: UserPreferencesService, - public appConfig: AppConfigService - ) { + constructor(public userPreferenceService: UserPreferencesService, public appConfig: AppConfigService) { this.userPreferenceService .select(UserPreferenceValues.Locale) - .pipe(takeUntil(this.onDestroy$)) - .subscribe(locale => { + .pipe(takeUntilDestroyed()) + .subscribe((locale) => { this.defaultLocale = locale || TimeAgoPipe.DEFAULT_LOCALE; }); this.defaultDateTimeFormat = this.appConfig.get('dateValues.defaultDateTimeFormat', TimeAgoPipe.DEFAULT_DATE_TIME_FORMAT); } transform(value: Date, locale?: string) { - if (value !== null && value !== undefined ) { + if (value !== null && value !== undefined) { const actualLocale = locale || this.defaultLocale; const diff = differenceInDays(new Date(), new Date(value)); - if ( diff > 7) { + if (diff > 7) { const datePipe: DatePipe = new DatePipe(actualLocale); return datePipe.transform(value, this.defaultDateTimeFormat); } else { - return formatDistance(new Date(value) , new Date(), { addSuffix: true , locale: Locales[actualLocale] }); + return formatDistance(new Date(value), new Date(), { addSuffix: true, locale: Locales[actualLocale] }); } } return ''; } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 25316f9faa..3e1011a381 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 @@ -17,7 +17,19 @@ import { Direction } from '@angular/cdk/bidi'; import { NgClass, NgIf } from '@angular/common'; -import { Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; +import { + Component, + DestroyRef, + ElementRef, + EventEmitter, + inject, + Input, + OnDestroy, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; @@ -25,11 +37,12 @@ import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { TranslateModule } from '@ngx-translate/core'; import { Observable, Subject, Subscription } from 'rxjs'; -import { debounceTime, filter, takeUntil } from 'rxjs/operators'; +import { debounceTime, filter } from 'rxjs/operators'; import { UserPreferencesService } from '../common'; import { searchAnimation } from './animations'; import { SearchAnimationDirection, SearchAnimationState, SearchTextStateEnum } from './models/search-text-input.model'; import { SearchTriggerDirective } from './search-trigger.directive'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-search-text-input', @@ -162,7 +175,6 @@ export class SearchTextInputComponent implements OnInit, OnDestroy { }; private dir = 'ltr'; - private onDestroy$ = new Subject(); private toggleSearch = new Subject(); private focusSubscription: Subscription; private valueChange = new Subject(); @@ -170,8 +182,10 @@ export class SearchTextInputComponent implements OnInit, OnDestroy { toggle$ = this.toggleSearch.asObservable(); + private readonly destroyRef = inject(DestroyRef); + constructor(private userPreferencesService: UserPreferencesService) { - this.toggleSubscription = this.toggle$.pipe(debounceTime(200), takeUntil(this.onDestroy$)).subscribe(() => { + this.toggleSubscription = this.toggle$.pipe(debounceTime(200), takeUntilDestroyed()).subscribe(() => { if (this.expandable) { this.subscriptAnimationState = this.toggleAnimation(); if (this.subscriptAnimationState.value === 'inactive') { @@ -189,7 +203,7 @@ export class SearchTextInputComponent implements OnInit, OnDestroy { ngOnInit() { this.userPreferencesService .select('textOrientation') - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((direction: Direction) => { this.dir = direction; this.subscriptAnimationState = this.getDefaultState(this.dir); @@ -246,7 +260,7 @@ export class SearchTextInputComponent implements OnInit, OnDestroy { filter( ($event: any) => this.isSearchBarActive() && ($event.type === 'blur' || $event.type === 'focusout' || $event.type === 'focus') ), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ); this.focusSubscription = focusEvents.subscribe((event: FocusEvent) => { @@ -260,7 +274,7 @@ export class SearchTextInputComponent implements OnInit, OnDestroy { } private setValueChangeHandler() { - this.valueChange.pipe(debounceTime(this.debounceTime), takeUntil(this.onDestroy$)).subscribe((value: string) => { + this.valueChange.pipe(debounceTime(this.debounceTime), takeUntilDestroyed(this.destroyRef)).subscribe((value: string) => { this.searchChange.emit(value); }); } @@ -315,9 +329,6 @@ export class SearchTextInputComponent implements OnInit, OnDestroy { this.focusSubscription = null; this.focusListener = null; } - - this.onDestroy$.next(true); - this.onDestroy$.complete(); } canShowClearSearch(): boolean { 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 aa5c916e18..d33cbb1199 100644 --- a/lib/core/src/lib/search-text/search-trigger.directive.ts +++ b/lib/core/src/lib/search-text/search-trigger.directive.ts @@ -18,12 +18,13 @@ /* 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, Directive, ElementRef, forwardRef, Inject, Input, NgZone, OnDestroy, Optional } from '@angular/core'; +import { ChangeDetectorRef, DestroyRef, Directive, ElementRef, forwardRef, inject, Inject, Input, NgZone, OnDestroy, Optional } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { DOCUMENT } from '@angular/common'; -import { Observable, Subject, Subscription, merge, of, fromEvent } from 'rxjs'; -import { filter, switchMap, takeUntil } from 'rxjs/operators'; +import { fromEvent, merge, Observable, of, Subject, Subscription } from 'rxjs'; +import { filter, switchMap } from 'rxjs/operators'; import { SearchComponentInterface } from '../common/interface/search-component.interface'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export const SEARCH_AUTOCOMPLETE_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, @@ -50,8 +51,6 @@ export const SEARCH_AUTOCOMPLETE_VALUE_ACCESSOR: any = { providers: [SEARCH_AUTOCOMPLETE_VALUE_ACCESSOR] }) export class SearchTriggerDirective implements ControlValueAccessor, OnDestroy { - private onDestroy$: Subject = new Subject(); - @Input('searchAutocomplete') searchPanel: SearchComponentInterface; @@ -66,6 +65,8 @@ export class SearchTriggerDirective implements ControlValueAccessor, OnDestroy { onTouched = () => {}; + private readonly destroyRef = inject(DestroyRef); + constructor( private element: ElementRef, private ngZone: NgZone, @@ -74,9 +75,6 @@ export class SearchTriggerDirective implements ControlValueAccessor, OnDestroy { ) {} ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - if (this.escapeEventStream) { this.escapeEventStream = null; } @@ -118,7 +116,7 @@ export class SearchTriggerDirective implements ControlValueAccessor, OnDestroy { const clickTarget = event.target as HTMLElement; return this._panelOpen && clickTarget !== this.element.nativeElement; }), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ); } @@ -183,7 +181,7 @@ export class SearchTriggerDirective implements ControlValueAccessor, OnDestroy { this.searchPanel.setVisibility(); return this.panelClosingActions; }), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((event) => this.setValueAndClose(event)); } 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 1014f9ad19..7bfc29b2ec 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 @@ -134,7 +134,6 @@ export class PdfViewerComponent implements OnChanges, OnDestroy { cMapPacked: true }; private pdfjsWorkerDestroy$ = new Subject(); - private onDestroy$ = new Subject(); constructor(private dialog: MatDialog, private renderingQueueServices: RenderingQueueServices, private appConfigService: AppConfigService) { // needed to preserve "this" context @@ -276,9 +275,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy { if (this.loadingTask) { this.pdfjsWorkerDestroy$.next(true); } - this.onDestroy$.next(true); this.pdfjsWorkerDestroy$.complete(); - this.onDestroy$.complete(); } private destroyPdJsWorker() { 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 e3b49764de..97e0e81ac9 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 @@ -15,13 +15,22 @@ * limitations under the License. */ -import { AppExtensionService, ViewerExtensionRef, ExtensionsModule } from '@alfresco/adf-extensions'; +import { AppExtensionService, ExtensionsModule, ViewerExtensionRef } from '@alfresco/adf-extensions'; import { NgForOf, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet } from '@angular/common'; -import { Component, EventEmitter, Injector, Input, OnChanges, OnDestroy, OnInit, Output, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { + Component, + EventEmitter, + Injector, + Input, + OnChanges, + OnInit, + Output, + TemplateRef, + ViewEncapsulation +} from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { TranslateModule } from '@ngx-translate/core'; -import { Subject } from 'rxjs'; import { Track } from '../../models/viewer.model'; import { ViewUtilService } from '../../services/view-util.service'; import { ImgViewerComponent } from '../img-viewer/img-viewer.component'; @@ -55,7 +64,7 @@ import { UnknownFormatComponent } from '../unknown-format/unknown-format.compone ], providers: [ViewUtilService] }) -export class ViewerRenderComponent implements OnChanges, OnInit, OnDestroy { +export class ViewerRenderComponent implements OnChanges, OnInit { /** * 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. @@ -169,8 +178,6 @@ export class ViewerRenderComponent implements OnChanges, OnInit, OnDestroy { cacheTypeForContent = 'no-cache'; - private onDestroy$ = new Subject(); - constructor( private viewUtilService: ViewUtilService, private extensionService: AppExtensionService, @@ -182,11 +189,6 @@ export class ViewerRenderComponent implements OnChanges, OnInit, OnDestroy { this.cacheTypeForContent = 'no-cache'; } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - ngOnChanges() { this.isLoading = !this.blobFile && !this.urlFile; diff --git a/lib/core/src/lib/viewer/components/viewer.component.ts b/lib/core/src/lib/viewer/components/viewer.component.ts index 6dd9508df5..1becfddf80 100644 --- a/lib/core/src/lib/viewer/components/viewer.component.ts +++ b/lib/core/src/lib/viewer/components/viewer.component.ts @@ -20,6 +20,7 @@ import { NgIf, NgTemplateOutlet } from '@angular/common'; import { Component, ContentChild, + DestroyRef, ElementRef, EventEmitter, HostListener, @@ -38,8 +39,8 @@ import { MatDialog } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; import { MatMenuModule } from '@angular/material/menu'; import { TranslateModule } from '@ngx-translate/core'; -import { fromEvent, Subject } from 'rxjs'; -import { filter, first, skipWhile, takeUntil } from 'rxjs/operators'; +import { fromEvent } from 'rxjs'; +import { filter, first, skipWhile } from 'rxjs/operators'; import { AppConfigService } from '../../app-config'; import { ToolbarComponent, ToolbarDividerComponent, ToolbarTitleComponent } from '../../toolbar'; import { DownloadPromptActions } from '../models/download-prompt.actions'; @@ -55,6 +56,7 @@ import { ViewerToolbarActionsComponent } from './viewer-toolbar-actions.componen import { ViewerToolbarCustomActionsComponent } from './viewer-toolbar-custom-actions.component'; import { IconComponent } from '../../icon'; import { ThumbnailService } from '../../common'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const DEFAULT_NON_PREVIEW_CONFIG = { enableDownloadPrompt: false, @@ -284,8 +286,6 @@ export class ViewerComponent implements OnDestroy, OnInit, OnChanges { @Output() submitFile = new EventEmitter(); - private onDestroy$ = new Subject(); - private closeViewer = true; private keyDown$ = fromEvent(document, 'keydown'); private isDialogVisible: boolean = false; @@ -293,6 +293,8 @@ export class ViewerComponent implements OnDestroy, OnInit, OnChanges { public downloadPromptReminderTimer: number; public mimeTypeIconUrl: string; + private readonly destroyRef = inject(DestroyRef); + constructor( private el: ElementRef, public dialog: MatDialog, @@ -326,14 +328,14 @@ export class ViewerComponent implements OnDestroy, OnInit, OnChanges { this.dialog.afterOpened .pipe( skipWhile(() => !this.overlayMode), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe(() => (this.closeViewer = false)); this.dialog.afterAllClosed .pipe( skipWhile(() => !this.overlayMode), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe(() => (this.closeViewer = true)); @@ -341,7 +343,7 @@ export class ViewerComponent implements OnDestroy, OnInit, OnChanges { .pipe( skipWhile(() => !this.overlayMode), filter((e: KeyboardEvent) => e.keyCode === 27), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((event: KeyboardEvent) => { event.preventDefault(); @@ -429,8 +431,6 @@ export class ViewerComponent implements OnDestroy, OnInit, OnChanges { ngOnDestroy() { this.clearDownloadPromptTimeouts(); - this.onDestroy$.next(true); - this.onDestroy$.complete(); } private configureAndInitDownloadPrompt() { 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 74db01c363..1bd480f1e5 100644 --- a/lib/core/src/lib/viewer/directives/viewer-extension.directive.ts +++ b/lib/core/src/lib/viewer/directives/viewer-extension.directive.ts @@ -15,16 +15,15 @@ * limitations under the License. */ -import { AfterContentInit, ContentChild, Directive, Input, TemplateRef, OnDestroy } from '@angular/core'; +import { AfterContentInit, ContentChild, DestroyRef, Directive, inject, Input, TemplateRef } from '@angular/core'; import { ViewerRenderComponent } from '../components/viewer-render/viewer-render.component'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Directive({ selector: 'adf-viewer-extension', standalone: true }) -export class ViewerExtensionDirective implements AfterContentInit, OnDestroy { +export class ViewerExtensionDirective implements AfterContentInit { @ContentChild(TemplateRef) template: any; @@ -39,7 +38,7 @@ export class ViewerExtensionDirective implements AfterContentInit, OnDestroy { templateModel: any; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private viewerComponent: ViewerRenderComponent) {} @@ -48,16 +47,11 @@ export class ViewerExtensionDirective implements AfterContentInit, OnDestroy { this.viewerComponent.extensionsSupportedByTemplates.push(...this.supportedExtensions); this.viewerComponent.extensionTemplates.push(this.templateModel); - this.viewerComponent.extensionChange.pipe(takeUntil(this.onDestroy$)).subscribe((fileExtension) => { + this.viewerComponent.extensionChange.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((fileExtension) => { this.templateModel.isVisible = this.isVisible(fileExtension); }); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - /** * Check if the current extension in the viewer is compatible with this extension checking against `supportedExtensions` * 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 8b11914ff8..0789cb19ae 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 @@ -19,10 +19,11 @@ import { ADF_DATE_FORMATS, AdfDateFnsAdapter, DownloadService, ToolbarComponent, import { AfterContentChecked, Component, + DestroyRef, EventEmitter, + inject, Input, OnChanges, - OnDestroy, OnInit, Output, SimpleChanges, @@ -35,8 +36,6 @@ import { ReportParameterDetailsModel } from '../../diagram/models/report/report- import { ReportParametersModel } from '../../diagram/models/report/report-parameters.model'; import { ReportQuery } from '../../diagram/models/report/report-query.model'; import { AnalyticsService } from '../services/analytics.service'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { MatIconModule } from '@angular/material/icon'; @@ -47,6 +46,7 @@ import { WIDGET_DIRECTIVES } from './widgets'; import { MatButtonModule } from '@angular/material/button'; import { ButtonsMenuComponent } from './buttons-menu/buttons-menu.component'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const FORMAT_DATE_ACTIVITI = 'YYYY-MM-DD'; @@ -133,7 +133,7 @@ export interface ReportFormValues { styleUrls: ['./analytics-report-parameters.component.scss'], encapsulation: ViewEncapsulation.None }) -export class AnalyticsReportParametersComponent implements OnInit, OnChanges, OnDestroy, AfterContentChecked { +export class AnalyticsReportParametersComponent implements OnInit, OnChanges, AfterContentChecked { /** appId ID of the target app. */ @Input() appId: number; @@ -186,7 +186,8 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On formValidState: boolean = false; private hideParameters: boolean = true; - private onDestroy$ = new Subject(); + + private readonly destroyRef = inject(DestroyRef); constructor( private analyticsService: AnalyticsService, @@ -197,14 +198,14 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On ) {} ngOnInit() { - this.onDropdownChanged.pipe(takeUntil(this.onDestroy$)).subscribe((field: any) => { + this.onDropdownChanged.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((field: any) => { const paramDependOn = this.reportParameters.definition.parameters.find((param) => param.dependsOn === field.id); if (paramDependOn) { this.retrieveParameterOptions(this.reportParameters.definition.parameters, this.appId, this.reportId, field.value); } }); - this.successReportParams.pipe(takeUntil(this.onDestroy$)).subscribe((report) => { + this.successReportParams.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((report) => { if (report.hasParameters()) { this.retrieveParameterOptions(report.definition.parameters, this.appId); this.generateFormGroupFromParameter(report.definition.parameters); @@ -310,11 +311,6 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On return reportParamQuery; } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - editEnable() { this.isEditable = true; } 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 676e546782..e5054d83e4 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 @@ -16,35 +16,35 @@ */ import { + ChangeDetectorRef, Component, + DestroyRef, EventEmitter, + HostListener, + inject, Input, OnChanges, - Output, - SimpleChanges, - OnDestroy, - HostListener, OnInit, - ChangeDetectorRef, - inject + Output, + SimpleChanges } from '@angular/core'; -import { Observable, of, forkJoin, Subject, Subscription } from 'rxjs'; -import { switchMap, takeUntil, map, filter } from 'rxjs/operators'; +import { forkJoin, Observable, of, Subscription } from 'rxjs'; +import { filter, map, switchMap } from 'rxjs/operators'; import { + ConfirmDialogComponent, + ContentLinkModel, + FORM_FIELD_VALIDATORS, FormBaseComponent, + FormEvent, FormFieldModel, + FormFieldValidator, + FormModel, FormOutcomeEvent, FormOutcomeModel, - WidgetVisibilityService, FormService, - FORM_FIELD_VALIDATORS, - FormFieldValidator, FormValues, - FormModel, - ContentLinkModel, UploadWidgetContentLinkModel, - FormEvent, - ConfirmDialogComponent + WidgetVisibilityService } from '@alfresco/adf-core'; import { FormCloudService } from '../services/form-cloud.service'; import { TaskVariableCloud } from '../models/task-variable-cloud.model'; @@ -54,13 +54,14 @@ import { v4 as uuidGeneration } from 'uuid'; import { FormCloudDisplayMode, FormCloudDisplayModeConfiguration } from '../../services/form-fields.interfaces'; import { FormCloudSpinnerService } from '../services/spinner/form-cloud-spinner.service'; import { DisplayModeService } from '../services/display-mode.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-cloud-form', templateUrl: './form-cloud.component.html', styleUrls: ['./form-cloud.component.scss'] }) -export class FormCloudComponent extends FormBaseComponent implements OnChanges, OnInit, OnDestroy { +export class FormCloudComponent extends FormBaseComponent implements OnChanges, OnInit { /** App name to fetch corresponding form and values. */ @Input() appName: string = ''; @@ -131,8 +132,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, nodeId: string; formCloudRepresentationJSON: any; - protected onDestroy$ = new Subject(); - readonly id: string; displayMode: string; displayConfiguration: FormCloudDisplayModeConfiguration = DisplayModeService.DEFAULT_DISPLAY_MODE_CONFIGURATIONS[0]; @@ -146,14 +145,16 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, protected displayModeService = inject(DisplayModeService); protected changeDetector = inject(ChangeDetectorRef); + private readonly destroyRef = inject(DestroyRef); + constructor() { super(); - this.spinnerService.initSpinnerHandling(this.onDestroy$); + this.spinnerService.initSpinnerHandling(this.destroyRef); this.id = uuidGeneration(); - this.formService.formContentClicked.pipe(takeUntil(this.onDestroy$)).subscribe((content) => { + this.formService.formContentClicked.pipe(takeUntilDestroyed()).subscribe((content) => { if (content instanceof UploadWidgetContentLinkModel) { this.form.setNodeIdValueForViewersLinkedToUploadWidget(content); this.onFormDataRefreshed(this.form); @@ -163,12 +164,12 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, } }); - this.formService.updateFormValuesRequested.pipe(takeUntil(this.onDestroy$)).subscribe((valuesToSetIfNotPresent) => { + this.formService.updateFormValuesRequested.pipe(takeUntilDestroyed()).subscribe((valuesToSetIfNotPresent) => { this.form.addValuesNotPresent(valuesToSetIfNotPresent); this.onFormDataRefreshed(this.form); }); - this.formService.formFieldValueChanged.pipe(takeUntil(this.onDestroy$)).subscribe(() => { + this.formService.formFieldValueChanged.pipe(takeUntilDestroyed()).subscribe(() => { if (this.disableSaveButton) { this.disableSaveButton = false; } @@ -222,7 +223,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, DisplayModeService.displayMode$ .pipe( filter((change) => change.id === this.id), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((displayModeChange) => { const oldDisplayMode = this.displayMode; @@ -281,7 +282,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, getFormByTaskId(appName: string, taskId: string, version?: number): Promise { return new Promise((resolve) => { forkJoin(this.formCloudService.getTaskForm(appName, taskId, version), this.formCloudService.getTaskVariables(appName, taskId)) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe( (data) => { this.formCloudRepresentationJSON = data[0]; @@ -314,7 +315,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, delete flattenForm.formDefinition; return flattenForm; }), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe( (form) => { @@ -337,7 +338,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, if (this.form && this.appName && this.taskId) { this.formCloudService .saveTaskForm(this.appName, this.taskId, this.processInstanceId, `${this.form.id}`, this.form.values) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe( () => { this.onTaskSaved(this.form); @@ -372,7 +373,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, if (this.form && this.appName && this.taskId) { this.formCloudService .completeTaskForm(this.appName, this.taskId, this.processInstanceId, `${this.form.id}`, this.form.values, outcome, this.appVersion) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe( () => { this.onTaskCompleted(this.form); @@ -478,11 +479,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, protected storeFormAsMetadata() {} - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - switchToDisplayMode(newDisplayMode?: string) { this.displayModeService.switchToDisplayMode(this.id, FormCloudDisplayMode[newDisplayMode], this.displayMode, this.displayModeConfigurations); } diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.spec.ts index 1fc5ef56af..1c6fc7ad22 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.spec.ts @@ -21,47 +21,47 @@ import { ContentCloudNodeSelectorService } from '../../../services/content-cloud import { ProcessCloudContentService } from '../../../services/process-cloud-content.service'; import { AttachFileCloudWidgetComponent } from './attach-file-cloud-widget.component'; import { - FormFieldModel, - FormModel, - FormFieldTypes, - FormService, - DownloadService, AppConfigService, - UploadWidgetContentLinkModel, + ContentLinkModel, + DownloadService, + FormFieldModel, + FormFieldTypes, + FormModel, + FormService, LocalizedDatePipe, NotificationService, - ContentLinkModel + UploadWidgetContentLinkModel } from '@alfresco/adf-core'; import { allSourceParams, - contentSourceParam, - fakeNode, - mockNodeId, - fakeLocalPngResponse, - onlyLocalParams, - allSourceWithWrongAliasParams, - allSourceWithNoAliasParams, - fakeNodeWithProperties, - menuTestSourceParam, - expectedValues, - fakeLocalPngAnswer, - allSourceWithStringTypeEmptyValue, - mockNodeIdBasedOnStringVariableValue, - mockAllFileSourceWithStringVariablePathType, - mockAllFileSourceWithFolderVariablePathType, - mockContentFileSource, - mockAllFileSourceWithStaticPathType, - formVariables, - processVariables, - mockAllFileSourceWithRenamedFolderVariablePathType, allSourceParamsWithRelativePath, - fakeLocalPhysicalRecordResponse, + allSourceWithNoAliasParams, + allSourceWithStringTypeEmptyValue, + allSourceWithWrongAliasParams, + contentSourceParam, displayableCMParams, + expectedValues, + fakeLocalPhysicalRecordResponse, + fakeLocalPngAnswer, fakeLocalPngHavingCMProperties, - mockMyNodeId + fakeLocalPngResponse, + fakeNode, + fakeNodeWithProperties, + formVariables, + menuTestSourceParam, + mockAllFileSourceWithFolderVariablePathType, + mockAllFileSourceWithRenamedFolderVariablePathType, + mockAllFileSourceWithStaticPathType, + mockAllFileSourceWithStringVariablePathType, + mockContentFileSource, + mockMyNodeId, + mockNodeId, + mockNodeIdBasedOnStringVariableValue, + onlyLocalParams, + processVariables } from '../../../mocks/attach-file-cloud-widget.mock'; import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CUSTOM_ELEMENTS_SCHEMA, Injector, runInInjectionContext } from '@angular/core'; import { ContentModule, ContentNodeSelectorPanelService, @@ -162,9 +162,12 @@ describe('AttachFileCloudWidgetComponent', () => { contentCloudNodeSelectorService = TestBed.inject(ContentCloudNodeSelectorService); appConfigService = TestBed.inject(AppConfigService); formService = TestBed.inject(FormService); + const injector = TestBed.inject(Injector); contentNodeSelectorPanelService = TestBed.inject(ContentNodeSelectorPanelService); openUploadFileDialogSpy = spyOn(contentCloudNodeSelectorService, 'openUploadFileDialog').and.returnValue(of([fakeNode])); - localizedDataPipe = new LocalizedDatePipe(); + runInInjectionContext(injector, () => { + localizedDataPipe = new LocalizedDatePipe(); + }); }); afterEach(() => { diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.ts index b90066be3e..a1313d93ff 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.ts @@ -17,19 +17,17 @@ /* eslint-disable @angular-eslint/component-selector */ -import { Component, OnInit, ViewEncapsulation, OnDestroy, inject } from '@angular/core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { - WidgetComponent, - FormService, + ADF_DATE_FORMATS, AdfDateFnsAdapter, DateFnsUtils, - ADF_DATE_FORMATS, - ErrorWidgetComponent, + DEFAULT_DATE_FORMAT, ErrorMessageModel, - DEFAULT_DATE_FORMAT + ErrorWidgetComponent, + FormService, + WidgetComponent } from '@alfresco/adf-core'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { addDays, parseISO } from 'date-fns'; @@ -38,6 +36,7 @@ import { NgIf } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'date-widget', @@ -62,7 +61,7 @@ import { MatInputModule } from '@angular/material/input'; }, encapsulation: ViewEncapsulation.None }) -export class DateCloudWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { +export class DateCloudWidgetComponent extends WidgetComponent implements OnInit { typeId = 'DateCloudWidgetComponent'; minDate: Date = null; @@ -71,9 +70,9 @@ export class DateCloudWidgetComponent extends WidgetComponent implements OnInit, dateInputControl: FormControl = new FormControl(null); - private onDestroy$ = new Subject(); - public readonly formService = inject(FormService); + + private readonly destroyRef = inject(DestroyRef); private readonly dateAdapter = inject(DateAdapter); ngOnInit(): void { @@ -101,7 +100,7 @@ export class DateCloudWidgetComponent extends WidgetComponent implements OnInit, } private subscribeToDateChanges(): void { - this.dateInputControl.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((newDate: Date) => { + this.dateInputControl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((newDate: Date) => { this.field.value = newDate; this.updateField(); }); @@ -198,9 +197,4 @@ export class DateCloudWidgetComponent extends WidgetComponent implements OnInit, this.maxDate = parseISO(this.field.maxValue); } } - - ngOnDestroy(): void { - this.onDestroy$.next(); - this.onDestroy$.complete(); - } } 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 018ee94176..f12a170cbf 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 @@ -29,15 +29,16 @@ import { WidgetComponent } from '@alfresco/adf-core'; import { AsyncPipe, NgFor, NgIf } from '@angular/common'; -import { Component, inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; import { TranslateModule } from '@ngx-translate/core'; -import { BehaviorSubject, Subject } from 'rxjs'; -import { filter, map, takeUntil } from 'rxjs/operators'; +import { BehaviorSubject } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; import { TaskVariableCloud } from '../../../models/task-variable-cloud.model'; import { FormCloudService } from '../../../services/form-cloud.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export const DEFAULT_OPTION = { id: 'empty', @@ -65,7 +66,7 @@ export const HIDE_FILTER_LIMIT = 5; SelectFilterInputComponent ] }) -export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { +export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit { public formService = inject(FormService); private formCloudService = inject(FormCloudService); private appConfig = inject(AppConfigService); @@ -84,8 +85,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI private readonly defaultVariableOptionId = 'id'; private readonly defaultVariableOptionLabel = 'name'; private readonly defaultVariableOptionPath = 'data'; - - protected onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); get showRequiredMessage(): boolean { return this.dropdownControl.touched && this.dropdownControl.errors?.required && !this.isRestApiFailed && !this.variableOptionsFailed; @@ -133,11 +133,6 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI }); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - compareDropdownValues(opt1: FormFieldOption | string, opt2: FormFieldOption | string): boolean { if (!opt1 || !opt2) { return false; @@ -186,7 +181,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI this.dropdownControl.valueChanges .pipe( filter(() => !!this.field), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((value) => { this.setOptionValue(value, this.field); @@ -216,7 +211,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI map((search) => search ? this.field.options.filter(({ name }) => name.toLowerCase().includes(search.toLowerCase())) : this.field.options ), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((result) => this.list$.next(result)); } @@ -333,7 +328,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI const bodyParam = this.buildBodyParam(); this.formCloudService .getRestWidgetData(this.field.form.id, this.field.id, bodyParam) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: (result: FormFieldOption[]) => { this.resetRestApiErrorMessage(); @@ -366,7 +361,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI this.formService.formFieldValueChanged .pipe( filter((event: FormFieldEvent) => this.isFormFieldEventOfTypeDropdown(event) && this.isParentFormFieldEvent(event)), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((event: FormFieldEvent) => { const valueOfParentWidget = event.field.value; diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/group/group-cloud.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/group/group-cloud.widget.ts index d022b94271..1284df4ba3 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/group/group-cloud.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/group/group-cloud.widget.ts @@ -15,13 +15,13 @@ * limitations under the License. */ -import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; -import { WidgetComponent, FormService } from '@alfresco/adf-core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormService, WidgetComponent } from '@alfresco/adf-core'; import { UntypedFormControl } from '@angular/forms'; -import { filter, takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; +import { filter } from 'rxjs/operators'; import { ComponentSelectionMode } from '../../../../types'; import { IdentityGroupModel } from '../../../../group/models/identity-group.model'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; /* eslint-disable @angular-eslint/component-selector */ @@ -41,9 +41,7 @@ import { IdentityGroupModel } from '../../../../group/models/identity-group.mode }, encapsulation: ViewEncapsulation.None }) -export class GroupCloudWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { - - private onDestroy$ = new Subject(); +export class GroupCloudWidgetComponent extends WidgetComponent implements OnInit { typeId = 'GroupCloudWidgetComponent'; roles: string[]; @@ -53,6 +51,8 @@ export class GroupCloudWidgetComponent extends WidgetComponent implements OnInit search: UntypedFormControl; validate = false; + private readonly destroyRef = inject(DestroyRef); + constructor(formService: FormService) { super(formService); } @@ -71,7 +71,7 @@ export class GroupCloudWidgetComponent extends WidgetComponent implements OnInit this.search.statusChanges .pipe( filter((value: string) => value === 'INVALID'), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe(() => { this.field.markAsInvalid(); @@ -81,19 +81,13 @@ export class GroupCloudWidgetComponent extends WidgetComponent implements OnInit this.search.statusChanges .pipe( filter((value: string) => value === 'VALID'), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe(() => { this.field.validate(); this.field.form.validateForm(); }); } - - ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - onChangedGroup(groups: IdentityGroupModel[]): void { this.field.value = groups?.length ? [...groups] : null; this.onFieldChanged(this.field); 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 5d3f924673..d68a89a3a0 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 @@ -15,14 +15,14 @@ * limitations under the License. */ -import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; -import { WidgetComponent, FormService } from '@alfresco/adf-core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormService, WidgetComponent } from '@alfresco/adf-core'; import { UntypedFormControl } from '@angular/forms'; -import { filter, takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; +import { filter } from 'rxjs/operators'; import { ComponentSelectionMode } from '../../../../types'; import { IdentityUserModel } from '../../../../people/models/identity-user.model'; import { IdentityUserService } from '../../../../people/services/identity-user.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; /* eslint-disable @angular-eslint/component-selector */ @@ -42,9 +42,7 @@ import { IdentityUserService } from '../../../../people/services/identity-user.s }, encapsulation: ViewEncapsulation.None }) -export class PeopleCloudWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { - - private onDestroy$ = new Subject(); +export class PeopleCloudWidgetComponent extends WidgetComponent implements OnInit { typeId = 'PeopleCloudWidgetComponent'; appName: string; @@ -56,6 +54,8 @@ export class PeopleCloudWidgetComponent extends WidgetComponent implements OnIni groupsRestriction: string[]; validate = false; + private readonly destroyRef = inject(DestroyRef); + constructor(formService: FormService, private identityUserService: IdentityUserService) { super(formService); } @@ -75,7 +75,7 @@ export class PeopleCloudWidgetComponent extends WidgetComponent implements OnIni this.search.statusChanges .pipe( filter((value: string) => value === 'INVALID'), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe(() => { this.field.markAsInvalid(); @@ -85,7 +85,7 @@ export class PeopleCloudWidgetComponent extends WidgetComponent implements OnIni this.search.statusChanges .pipe( filter((value: string) => value === 'VALID'), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe(() => { this.field.validate(); @@ -99,11 +99,6 @@ export class PeopleCloudWidgetComponent extends WidgetComponent implements OnIni } } - ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - onChangedUser(users: IdentityUserModel[]): void { this.field.value = users?.length ? [...users] : null; this.onFieldChanged(this.field); 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 5fcb8d07c9..67e65118e4 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 @@ -17,12 +17,11 @@ /* eslint-disable @angular-eslint/component-selector */ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; -import { WidgetComponent, FormService, FormFieldOption, ErrorMessageModel } from '@alfresco/adf-core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { ErrorMessageModel, FormFieldOption, FormService, WidgetComponent } from '@alfresco/adf-core'; import { FormCloudService } from '../../../services/form-cloud.service'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'radio-buttons-cloud-widget', @@ -45,7 +44,7 @@ export class RadioButtonsCloudWidgetComponent extends WidgetComponent implements typeId = 'RadioButtonsCloudWidgetComponent'; restApiError: ErrorMessageModel; - protected onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(public formService: FormService, private formCloudService: FormCloudService, private translateService: TranslateService) { super(formService); @@ -60,7 +59,7 @@ export class RadioButtonsCloudWidgetComponent extends WidgetComponent implements getValuesFromRestApi() { this.formCloudService .getRestWidgetData(this.field.form.id, this.field.id) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe( (result: FormFieldOption[]) => { this.field.options = result; diff --git a/lib/process-services-cloud/src/lib/form/services/spinner/form-cloud-spinner.service.spec.ts b/lib/process-services-cloud/src/lib/form/services/spinner/form-cloud-spinner.service.spec.ts index 7a6b029e0a..e8574be918 100644 --- a/lib/process-services-cloud/src/lib/form/services/spinner/form-cloud-spinner.service.spec.ts +++ b/lib/process-services-cloud/src/lib/form/services/spinner/form-cloud-spinner.service.spec.ts @@ -21,7 +21,7 @@ import { OverlayModule } from '@angular/cdk/overlay'; import { FormService, FormSpinnerEvent } from '@alfresco/adf-core'; import { Subject } from 'rxjs'; import { TranslateModule } from '@ngx-translate/core'; -import { Component } from '@angular/core'; +import { Component, DestroyRef, inject } from '@angular/core'; import { FormSpinnerComponent } from '../../components/spinner/form-spinner.component'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; @@ -33,18 +33,20 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; selector: 'adf-cloud-overlay-test', template: `
adf-cloud-overlay-test
` }) -class SpinnerTestComponent {} +class SpinnerTestComponent { + destroyRef = inject(DestroyRef) +} describe('FormCloudSpinnerService', () => { let fixture: ComponentFixture; let rootLoader: HarnessLoader; let spinnerService: FormCloudSpinnerService; let formService: FormService; + let destroyRef: DestroyRef; const showSpinnerEvent = new FormSpinnerEvent('toggle-spinner', { showSpinner: true, message: 'LOAD_SPINNER_MESSAGE' }); const hideSpinnerEvent = new FormSpinnerEvent('toggle-spinner', { showSpinner: false }); - const onDestroy$ = new Subject(); beforeEach(() => { TestBed.configureTestingModule({ @@ -65,10 +67,11 @@ describe('FormCloudSpinnerService', () => { rootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture); spinnerService = TestBed.inject(FormCloudSpinnerService); formService = TestBed.inject(FormService); + destroyRef = fixture.componentInstance.destroyRef }); it('should toggle spinner', async () => { - spinnerService.initSpinnerHandling(onDestroy$); + spinnerService.initSpinnerHandling(destroyRef); formService.toggleFormSpinner.next(showSpinnerEvent); fixture.detectChanges(); 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 7722b9afed..8a37197818 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 @@ -15,13 +15,12 @@ * limitations under the License. */ -import { Injectable, inject } from '@angular/core'; +import { DestroyRef, inject, Injectable } from '@angular/core'; import { Overlay, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; import { FormService, FormSpinnerEvent } from '@alfresco/adf-core'; -import { Observable } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { FormSpinnerComponent } from '../../components/spinner/form-spinner.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Injectable() export class FormCloudSpinnerService { @@ -30,8 +29,8 @@ export class FormCloudSpinnerService { private overlayRef?: OverlayRef; - initSpinnerHandling(onDestroy$: Observable): void { - this.formService.toggleFormSpinner.pipe(takeUntil(onDestroy$)).subscribe((event: FormSpinnerEvent) => { + initSpinnerHandling(destroyRef: DestroyRef): void { + this.formService.toggleFormSpinner.pipe(takeUntilDestroyed(destroyRef)).subscribe((event: FormSpinnerEvent) => { if (event?.payload.showSpinner) { this.overlayRef = this.overlay.create({ hasBackdrop: true 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 6284142fa7..fd98e26abf 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 @@ -17,26 +17,28 @@ import { Component, + DestroyRef, ElementRef, + EventEmitter, + Inject, + inject, + Input, + OnChanges, OnInit, Output, - EventEmitter, - ViewChild, - ViewEncapsulation, - Input, SimpleChanges, - OnChanges, - OnDestroy, - Inject + ViewChild, + ViewEncapsulation } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; -import { trigger, state, style, transition, animate } from '@angular/animations'; -import { BehaviorSubject, Observable, Subject } from 'rxjs'; -import { distinctUntilChanged, switchMap, mergeMap, filter, tap, takeUntil, debounceTime } from 'rxjs/operators'; +import { animate, state, style, transition, trigger } from '@angular/animations'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { debounceTime, distinctUntilChanged, filter, mergeMap, switchMap, tap } from 'rxjs/operators'; import { ComponentSelectionMode } from '../../types'; import { IdentityGroupModel } from '../models/identity-group.model'; import { IdentityGroupServiceInterface } from '../services/identity-group.service.interface'; import { IDENTITY_GROUP_SERVICE_TOKEN } from '../services/identity-group-service.token'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-cloud-group', @@ -50,7 +52,7 @@ import { IDENTITY_GROUP_SERVICE_TOKEN } from '../services/identity-group-service ], encapsulation: ViewEncapsulation.None }) -export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy { +export class GroupCloudComponent implements OnInit, OnChanges { /** Name of the application. If specified this shows the groups who have access to the app. */ @Input() appName: string; @@ -119,7 +121,6 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy { private groupInput: ElementRef; private searchGroups: IdentityGroupModel[] = []; - private onDestroy$ = new Subject(); selectedGroups: IdentityGroupModel[] = []; invalidGroups: IdentityGroupModel[] = []; @@ -137,6 +138,8 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy { typingUniqueValueNotEmpty$: Observable; + private readonly destroyRef = inject(DestroyRef); + constructor( @Inject(IDENTITY_GROUP_SERVICE_TOKEN) private identityGroupService: IdentityGroupServiceInterface @@ -172,7 +175,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy { return groups; }), filter((group) => !this.isGroupAlreadySelected(group)), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((searchedGroup: IdentityGroupModel) => { this.searchGroups.push(searchedGroup); @@ -467,9 +470,4 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy { getValidationMinLength(): string { return this.searchGroupsControl.errors.minlength.requiredLength; } - - ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 c02496be0d..bde0e1d1a7 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 @@ -17,29 +17,31 @@ import { UntypedFormControl } from '@angular/forms'; import { + AfterViewInit, Component, + DestroyRef, + ElementRef, + EventEmitter, + Inject, + inject, + Input, + OnChanges, OnInit, Output, - EventEmitter, - ViewEncapsulation, - Input, SimpleChanges, - OnChanges, - OnDestroy, ViewChild, - ElementRef, - Inject, - AfterViewInit + ViewEncapsulation } from '@angular/core'; -import { BehaviorSubject, Observable, Subject } from 'rxjs'; -import { switchMap, debounceTime, distinctUntilChanged, mergeMap, tap, filter, takeUntil } from 'rxjs/operators'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { debounceTime, distinctUntilChanged, filter, mergeMap, switchMap, tap } from 'rxjs/operators'; import { FullNamePipe } from '@alfresco/adf-core'; -import { trigger, state, style, transition, animate } from '@angular/animations'; +import { animate, state, style, transition, trigger } from '@angular/animations'; import { ComponentSelectionMode } from '../../types'; import { IdentityUserModel } from '../models/identity-user.model'; import { IdentityUserServiceInterface } from '../services/identity-user.service.interface'; import { IDENTITY_USER_SERVICE_TOKEN } from '../services/identity-user-service.token'; import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-cloud-people', @@ -54,7 +56,7 @@ import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form- providers: [FullNamePipe], encapsulation: ViewEncapsulation.None }) -export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit { +export class PeopleCloudComponent implements OnInit, OnChanges, AfterViewInit { /** Name of the application. If specified, this shows the users who have access to the app. */ @Input() appName: string; @@ -175,7 +177,6 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy, After private userInput: ElementRef; private searchUsers: IdentityUserModel[] = []; - private onDestroy$ = new Subject(); selectedUsers: IdentityUserModel[] = []; invalidUsers: IdentityUserModel[] = []; @@ -193,6 +194,8 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy, After typingUniqueValueNotEmpty$: Observable; + private readonly destroyRef = inject(DestroyRef); + constructor( @Inject(IDENTITY_USER_SERVICE_TOKEN) private identityUserService: IdentityUserServiceInterface @@ -245,7 +248,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy, After return users; }), filter((user) => !this.isUserAlreadySelected(user) && !this.isExcludedUser(user)), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((user: IdentityUserModel) => { this.searchUsers.push(user); @@ -563,9 +566,4 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy, After getValidationMinLength(): string { return this.searchUserCtrl.errors.minlength.requiredLength; } - - ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } diff --git a/lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.ts b/lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.ts index 6084ebdbfa..df7d9418c4 100644 --- a/lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.ts +++ b/lib/process-services-cloud/src/lib/process/directives/cancel-process.directive.ts @@ -15,18 +15,17 @@ * limitations under the License. */ -import { Directive, HostListener, Output, EventEmitter, OnInit, OnDestroy, ElementRef } from '@angular/core'; +import { DestroyRef, Directive, ElementRef, EventEmitter, HostListener, inject, OnInit, Output } from '@angular/core'; import { ProcessCloudService } from '../services/process-cloud.service'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; import { ProcessInstanceCloud } from '../start-process/models/process-instance-cloud.model'; import { IdentityUserService } from '../../people/services/identity-user.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector selector: '[adf-cloud-cancel-process]' }) -export class CancelProcessDirective implements OnInit, OnDestroy { +export class CancelProcessDirective implements OnInit { /** Emitted when the process is cancelled. */ @Output() @@ -40,7 +39,7 @@ export class CancelProcessDirective implements OnInit, OnDestroy { canCancelProcess = false; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor( private elementRef: ElementRef, @@ -49,7 +48,7 @@ export class CancelProcessDirective implements OnInit, OnDestroy { ngOnInit() { this.processCloudService.dataChangesDetected - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((processDetails) => { this.processInstanceDetails = processDetails; this.canCancelProcess = this.checkCanCancelProcess(); @@ -82,9 +81,4 @@ export class CancelProcessDirective implements OnInit, OnDestroy { this.error.emit('Permission denied, only process initiator can cancel the process'); } } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } diff --git a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts index 3000825c23..4c045f4633 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts @@ -15,18 +15,18 @@ * limitations under the License. */ -import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, OnDestroy, ViewEncapsulation } from '@angular/core'; -import { FormBuilder, AbstractControl, FormGroup, FormControl } from '@angular/forms'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { AbstractControl, FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { DateAdapter } from '@angular/material/core'; import { MatDialog } from '@angular/material/dialog'; -import { debounceTime, filter, takeUntil, finalize, switchMap, tap } from 'rxjs/operators'; -import { Subject, Observable, Subscription } from 'rxjs'; +import { debounceTime, filter, finalize, switchMap, tap } from 'rxjs/operators'; +import { Observable, Subscription } from 'rxjs'; import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service'; import { - ProcessFilterCloudModel, - ProcessFilterProperties, ProcessFilterAction, + ProcessFilterCloudModel, ProcessFilterOptions, + ProcessFilterProperties, ProcessSortFilterProperty } from '../models/process-filter-cloud.model'; import { DateFnsUtils, TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core'; @@ -37,6 +37,7 @@ import { DateCloudFilterType, DateRangeFilter } from '../../../models/date-cloud import { IdentityUserModel } from '../../../people/models/identity-user.model'; import { Environment } from '../../../common/interface/environment.interface'; import { endOfDay, isValid, startOfDay } from 'date-fns'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export const PROCESS_FILTER_ACTION_SAVE = 'save'; export const PROCESS_FILTER_ACTION_SAVE_AS = 'saveAs'; @@ -71,7 +72,7 @@ interface ProcessFilterFormProps { styleUrls: ['./edit-process-filter-cloud.component.scss'], encapsulation: ViewEncapsulation.None }) -export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDestroy { +export class EditProcessFilterCloudComponent implements OnInit, OnChanges { /** The name of the application. */ @Input() appName: string = ''; @@ -184,10 +185,11 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes appVersionOptions: ProcessFilterOptions[] = []; initiatorOptions: IdentityUserModel[] = []; - private onDestroy$ = new Subject(); isLoading: boolean = false; private filterChangeSub: Subscription; + private readonly destroyRef = inject(DestroyRef); + constructor( private formBuilder: FormBuilder, public dialog: MatDialog, @@ -202,7 +204,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes ngOnInit() { this.userPreferencesService .select(UserPreferenceValues.Locale) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((locale) => this.dateAdapter.setLocale(locale)); } @@ -212,12 +214,6 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes this.retrieveProcessFilterAndBuildForm(); } } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - filterTracker(_index: number, item: ProcessFilterProperties) { return item.key; } @@ -284,7 +280,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes .pipe( debounceTime(500), filter(() => this.isFormValid()), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((formValues: Partial) => { this.setLastModifiedFromFilter(formValues); diff --git a/lib/process-services-cloud/src/lib/process/process-filters/components/process-filters-cloud.component.ts b/lib/process-services-cloud/src/lib/process/process-filters/components/process-filters-cloud.component.ts index 78eb2f5de7..8bf7cc2b06 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/components/process-filters-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/process-filters/components/process-filters-cloud.component.ts @@ -15,16 +15,28 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, OnDestroy, OnInit, ViewEncapsulation, inject } from '@angular/core'; -import { Observable, Subject } from 'rxjs'; +import { + Component, + DestroyRef, + EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges, + ViewEncapsulation +} from '@angular/core'; +import { Observable } from 'rxjs'; import { ProcessFilterCloudService } from '../services/process-filter-cloud.service'; import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model'; import { AppConfigService, TranslationService } from '@alfresco/adf-core'; import { FilterParamsModel } from '../../../task/task-filters/models/filter-cloud.model'; -import { debounceTime, takeUntil, tap } from 'rxjs/operators'; +import { debounceTime, tap } from 'rxjs/operators'; import { ProcessListCloudService } from '../../../process/process-list/services/process-list-cloud.service'; import { PROCESS_SEARCH_API_METHOD_TOKEN } from '../../../services/cloud-token.service'; import { ProcessFilterCloudAdapter } from '../../process-list/models/process-cloud-query-request.model'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-cloud-process-filters', @@ -32,7 +44,7 @@ import { ProcessFilterCloudAdapter } from '../../process-list/models/process-clo styleUrls: ['./process-filters-cloud.component.scss'], encapsulation: ViewEncapsulation.None }) -export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestroy { +export class ProcessFiltersCloudComponent implements OnInit, OnChanges { /** (required) The application name */ @Input() appName: string = ''; @@ -73,8 +85,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro currentFiltersValues: { [key: string]: number } = {}; updatedFiltersSet = new Set(); - private onDestroy$ = new Subject(); - + private readonly destroyRef = inject(DestroyRef); private readonly processFilterCloudService = inject(ProcessFilterCloudService); private readonly translationService = inject(TranslationService); private readonly appConfigService = inject(AppConfigService); @@ -108,7 +119,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro getFilters(appName: string): void { this.filters$ = this.processFilterCloudService.getProcessFilters(appName); - this.filters$.pipe(takeUntil(this.onDestroy$)).subscribe({ + this.filters$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({ next: (res) => { this.resetFilter(); this.filters = res || []; @@ -240,11 +251,6 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro this.currentFilter = undefined; } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - isActiveFilter(filter: ProcessFilterCloudModel): boolean { return this.currentFilter.name === filter.name; } @@ -253,7 +259,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro if (this.appName && this.enableNotifications) { this.processFilterCloudService .getProcessNotificationSubscription(this.appName) - .pipe(debounceTime(1000), takeUntil(this.onDestroy$)) + .pipe(debounceTime(1000), takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.updateFilterCounters(); }); @@ -310,7 +316,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro * */ getFilterKeysAfterExternalRefreshing(): void { - this.processFilterCloudService.filterKeyToBeRefreshed$.pipe(takeUntil(this.onDestroy$)).subscribe((filterKey: string) => { + this.processFilterCloudService.filterKeyToBeRefreshed$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((filterKey: string) => { this.updatedFiltersSet.delete(filterKey); }); } 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 a9506efc59..95c0290fe7 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 @@ -15,19 +15,28 @@ * limitations under the License. */ -import { Component, Input, OnChanges, OnInit, OnDestroy, EventEmitter, ViewEncapsulation, Output } from '@angular/core'; import { + Component, + DestroyRef, + EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, + ViewEncapsulation +} from '@angular/core'; +import { + AppConfigService, + CardViewBaseItemModel, + CardViewDateItemModel, CardViewItem, CardViewTextItemModel, - TranslationService, - AppConfigService, - CardViewDateItemModel, - CardViewBaseItemModel + TranslationService } from '@alfresco/adf-core'; import { ProcessInstanceCloud } from '../../start-process/models/process-instance-cloud.model'; import { ProcessCloudService } from '../../services/process-cloud.service'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-cloud-process-header', @@ -36,7 +45,7 @@ import { Subject } from 'rxjs'; styleUrls: ['./process-header-cloud.component.scss'], host: { class: 'adf-cloud-process-header' } }) -export class ProcessHeaderCloudComponent implements OnChanges, OnInit, OnDestroy { +export class ProcessHeaderCloudComponent implements OnChanges, OnInit { /** (Required) The name of the application. */ @Input() appName: string = ''; @@ -54,7 +63,7 @@ export class ProcessHeaderCloudComponent implements OnChanges, OnInit, OnDestroy dateFormat: string; dateLocale: string; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor( private processCloudService: ProcessCloudService, @@ -66,7 +75,7 @@ export class ProcessHeaderCloudComponent implements OnChanges, OnInit, OnDestroy this.dateFormat = this.appConfig.get('adf-cloud-process-header.defaultDateFormat'); this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale'); - this.processCloudService.dataChangesDetected.pipe(takeUntil(this.onDestroy$)).subscribe((processDetails) => this.onLoaded(processDetails)); + this.processCloudService.dataChangesDetected.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((processDetails) => this.onLoaded(processDetails)); } ngOnChanges() { @@ -152,9 +161,4 @@ export class ProcessHeaderCloudComponent implements OnChanges, OnInit, OnDestroy private isValidSelection(filteredProperties: string[], cardItem: CardViewBaseItemModel): boolean { return filteredProperties ? filteredProperties.indexOf(cardItem.key) >= 0 : true; } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 4d71306210..96f852d245 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 @@ -16,47 +16,53 @@ */ import { - Component, - ViewEncapsulation, - OnChanges, AfterContentInit, + Component, ContentChild, - Output, EventEmitter, - SimpleChanges, - Input, - ViewChild, Inject, - OnDestroy, - Optional + Input, + OnChanges, + Optional, + Output, + SimpleChanges, + ViewChild, + ViewEncapsulation } from '@angular/core'; import { - DataTableSchema, - PaginatedComponent, - CustomEmptyContentTemplateDirective, AppConfigService, - UserPreferencesService, - PaginationModel, - UserPreferenceValues, - DataRowEvent, + CustomEmptyContentTemplateDirective, CustomLoadingContentTemplateDirective, DataCellEvent, + DataColumn, DataRowActionEvent, + DataRowEvent, DataTableComponent, - DataColumn + DataTableSchema, + PaginatedComponent, + PaginationModel, + UserPreferencesService, + UserPreferenceValues } from '@alfresco/adf-core'; import { ProcessListCloudService } from '../services/process-list-cloud.service'; import { BehaviorSubject, combineLatest, Subject } from 'rxjs'; import { processCloudPresetsDefaultModel } from '../models/process-cloud-preset.model'; import { ProcessListRequestModel, ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model'; import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model'; -import { filter, map, switchMap, take, takeUntil } from 'rxjs/operators'; +import { filter, map, switchMap, take } from 'rxjs/operators'; import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface'; -import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN, PROCESS_SEARCH_API_METHOD_TOKEN } from '../../../services/cloud-token.service'; +import { + PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN, + PROCESS_SEARCH_API_METHOD_TOKEN +} from '../../../services/cloud-token.service'; import { ProcessListCloudPreferences } from '../models/process-cloud-preferences'; import { ProcessListDatatableAdapter } from '../datatable/process-list-datatable-adapter'; -import { ProcessListDataColumnCustomData, PROCESS_LIST_CUSTOM_VARIABLE_COLUMN } from '../../../models/data-column-custom-data'; +import { + PROCESS_LIST_CUSTOM_VARIABLE_COLUMN, + ProcessListDataColumnCustomData +} from '../../../models/data-column-custom-data'; import { VariableMapperService } from '../../../services/variable-mapper.sevice'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const PRESET_KEY = 'adf-cloud-process-list.presets'; @@ -66,7 +72,9 @@ const PRESET_KEY = 'adf-cloud-process-list.presets'; styleUrls: ['./process-list-cloud.component.scss'], encapsulation: ViewEncapsulation.None }) -export class ProcessListCloudComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent, OnDestroy { +export class ProcessListCloudComponent + extends DataTableSchema + implements OnChanges, AfterContentInit, PaginatedComponent { @ViewChild(DataTableComponent) dataTable: DataTableComponent; @@ -255,8 +263,6 @@ export class ProcessListCloudComponent extends DataTableSchema = new EventEmitter(); - private onDestroy$ = new Subject(); - pagination: BehaviorSubject; size: number; skipCount: number = 0; @@ -299,9 +305,7 @@ export class ProcessListCloudComponent extends DataTableSchema { - return isColumnSchemaCreated; - }), + filter(([isColumnSchemaCreated]) => isColumnSchemaCreated), switchMap(() => { if (this.searchMethod === 'POST') { const requestNode = this.createProcessListRequestNode(); @@ -313,7 +317,7 @@ export class ProcessListCloudComponent extends DataTableSchema { this.rows = this.variableMapperService.mapVariablesByColumnTitle(processes.list.entries, this.columns); @@ -367,12 +371,6 @@ export class ProcessListCloudComponent extends DataTableSchema(); isProcessStarting = false; isFormCloudLoaded = false; @@ -154,6 +160,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy processDefinition: new FormControl('', [Validators.required, this.processDefinitionNameValidator()]) }); + private readonly destroyRef = inject(DestroyRef); private readonly startProcessCloudService = inject(StartProcessCloudService); private readonly processNameCloudPipe = inject(ProcessNameCloudPipe); @@ -204,7 +211,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy this.processDefinition.setValue(this.processDefinitionName); this.processDefinition.valueChanges .pipe(debounceTime(PROCESS_DEFINITION_DEBOUNCE)) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((processDefinitionName) => { this.selectProcessDefinitionByProcessDefinitionName(processDefinitionName); }); @@ -327,7 +334,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy this.startProcessCloudService .getProcessDefinitions(this.appName) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe( (processDefinitionRepresentations: ProcessDefinitionCloud[]) => { this.processDefinitionList = processDefinitionRepresentations; @@ -498,9 +505,4 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy this.processInstanceName.markAsDirty(); this.processInstanceName.markAsTouched(); } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } diff --git a/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.ts b/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.ts index 5247bbf2b0..ab09cfa5fe 100644 --- a/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.ts @@ -15,21 +15,21 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, OnDestroy, ViewChild } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; -import { Observable, Subject } from 'rxjs'; -import { UntypedFormBuilder, Validators, UntypedFormGroup, UntypedFormControl } from '@angular/forms'; +import { Observable } from 'rxjs'; +import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { DateFnsUtils, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core'; import { PeopleCloudComponent } from '../../../people/components/people-cloud.component'; import { GroupCloudComponent } from '../../../group/components/group-cloud.component'; import { TaskCloudService } from '../../services/task-cloud.service'; import { StartTaskCloudRequestModel } from '../models/start-task-cloud-request.model'; -import { takeUntil } from 'rxjs/operators'; import { TaskPriorityOption } from '../../models/task.model'; import { IdentityUserService } from '../../../people/services/identity-user.service'; import { IdentityUserModel } from '../../../people/models/identity-user.model'; import { DateFnsAdapter, MAT_DATE_FNS_FORMATS } from '@angular/material-date-fns-adapter'; import { isValid, parse } from 'date-fns'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const MAX_NAME_LENGTH = 255; const DATE_FORMAT: string = 'dd/MM/yyyy'; @@ -44,7 +44,7 @@ const DATE_FORMAT: string = 'dd/MM/yyyy'; ], encapsulation: ViewEncapsulation.None }) -export class StartTaskCloudComponent implements OnInit, OnDestroy { +export class StartTaskCloudComponent implements OnInit { /** (required) Name of the app. */ @Input() appName: string = ''; @@ -99,7 +99,8 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy { private assigneeForm = new UntypedFormControl(''); private groupForm = new UntypedFormControl(''); - private onDestroy$ = new Subject(); + + private readonly destroyRef = inject(DestroyRef); constructor( private taskService: TaskCloudService, @@ -112,18 +113,13 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy { ngOnInit() { this.userPreferencesService .select(UserPreferenceValues.Locale) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((locale) => this.dateAdapter.setLocale(DateFnsUtils.getLocaleFromString(locale))); this.loadCurrentUser(); this.buildForm(); this.loadDefaultPriorities(); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - buildForm() { this.taskForm = this.formBuilder.group({ name: new UntypedFormControl(this.name, [Validators.required, Validators.maxLength(this.getMaxNameLength()), this.whitespaceValidator]), diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.ts index 22e3678864..00a9b442a5 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.ts @@ -15,13 +15,12 @@ * limitations under the License. */ -import { Directive, EventEmitter, Input, OnDestroy, Output } from '@angular/core'; -import { Subject } from 'rxjs'; +import { DestroyRef, Directive, EventEmitter, inject, Input, Output } from '@angular/core'; import { FilterParamsModel } from '../models/filter-cloud.model'; @Directive() // eslint-disable-next-line @angular-eslint/directive-class-suffix -export abstract class BaseTaskFiltersCloudComponent implements OnDestroy { +export abstract class BaseTaskFiltersCloudComponent { /** Display filters available to the current user for the application with the specified name. */ @Input() appName: string = ''; @@ -48,12 +47,7 @@ export abstract class BaseTaskFiltersCloudComponent implements OnDestroy { counters: { [key: string]: number } = {}; updatedCountersSet = new Set(); - protected onDestroy$ = new Subject(); - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } + protected destroyRef = inject(DestroyRef); wasFilterUpdated(filterKey: string): boolean { return this.updatedCountersSet.has(filterKey); diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.ts index c93162d833..33b137b144 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.ts @@ -15,14 +15,14 @@ * limitations under the License. */ -import { OnChanges, SimpleChanges, OnInit, OnDestroy, Directive, Input, Output, EventEmitter, inject } from '@angular/core'; +import { DestroyRef, Directive, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; import { AssignmentType, FilterOptions, TaskFilterAction, TaskFilterProperties, TaskStatusFilter } from '../../models/filter-cloud.model'; import { TaskCloudService } from './../../../services/task-cloud.service'; import { AppsProcessCloudService } from './../../../../app/services/apps-process-cloud.service'; import { DateCloudFilterType, DateRangeFilter } from '../../../../models/date-cloud-filter.model'; import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { debounceTime, filter, finalize, switchMap, takeUntil } from 'rxjs/operators'; -import { Observable, Subject } from 'rxjs'; +import { debounceTime, filter, finalize, switchMap } from 'rxjs/operators'; +import { Observable } from 'rxjs'; import { DateAdapter } from '@angular/material/core'; import { DateFnsUtils, TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core'; import { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filter-dialog-cloud.component'; @@ -32,6 +32,7 @@ import { IdentityGroupModel } from '../../../../group/models/identity-group.mode import { MatSelectChange } from '@angular/material/select'; import { Environment } from '../../../../common/interface/environment.interface'; import { isValid } from 'date-fns'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; /* eslint-disable @typescript-eslint/naming-convention */ @@ -54,7 +55,7 @@ const ORDER_PROPERTY = 'order'; @Directive() // eslint-disable-next-line @angular-eslint/directive-class-suffix -export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestroy { +export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnChanges { public static ACTIONS_DISABLED_BY_DEFAULT = [ACTION_SAVE, ACTION_DELETE]; /** (required) Name of the app. */ @@ -133,9 +134,9 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC @Output() filterChange = new EventEmitter(); - protected onDestroy$ = new Subject(); isLoading: boolean = false; + protected destroyRef = inject(DestroyRef); protected translateService = inject(TranslationService); protected taskCloudService = inject(TaskCloudService); protected userPreferencesService = inject(UserPreferencesService); @@ -147,7 +148,7 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC ngOnInit() { this.userPreferencesService .select(UserPreferenceValues.Locale) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((locale) => this.dateAdapter.setLocale(locale)); } @@ -158,11 +159,6 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC } } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - createFilterActions(): TaskFilterAction[] { return [ { @@ -430,7 +426,7 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC .pipe( debounceTime(500), filter(() => this.isFormValid()), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((formValues) => { this.assignNewFilter(formValues); @@ -471,7 +467,7 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC this.getTaskFilterById(this.appName, this.id) .pipe( finalize(() => (this.isLoading = false)), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((response) => { this.taskFilter = response; @@ -490,14 +486,14 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC return filters.length === 0; }), switchMap(() => this.restoreDefaultTaskFilters()), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe(() => {}); } save(saveAction: TaskFilterAction): void { this.updateFilter(this.changedTaskFilter) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { saveAction.filter = this.changedTaskFilter; this.action.emit(saveAction); diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/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-cloud.component.ts index deebc8a348..efcf6c47ad 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/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-cloud.component.ts @@ -15,12 +15,12 @@ * limitations under the License. */ -import { Component, ViewEncapsulation, inject } from '@angular/core'; -import { takeUntil } from 'rxjs/operators'; +import { Component, inject, ViewEncapsulation } from '@angular/core'; import { Observable } from 'rxjs'; -import { TaskFilterProperties, TaskFilterAction, ServiceTaskFilterCloudModel } from '../../models/filter-cloud.model'; +import { ServiceTaskFilterCloudModel, TaskFilterAction, TaskFilterProperties } from '../../models/filter-cloud.model'; import { ServiceTaskFilterCloudService } from '../../services/service-task-filter-cloud.service'; import { BaseEditTaskFilterCloudComponent, DropdownOption } from './base-edit-task-filter-cloud.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-cloud-edit-service-task-filter', @@ -64,7 +64,7 @@ export class EditServiceTaskFilterCloudComponent extends BaseEditTaskFilterCloud protected addFilter(filterToAdd: ServiceTaskFilterCloudModel): Observable { return this.serviceTaskFilterCloudService .addFilter(filterToAdd) - .pipe(takeUntil(this.onDestroy$)); + .pipe(takeUntilDestroyed(this.destroyRef)); } isDisabledForDefaultFilters(action: TaskFilterAction): boolean { diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts index d4196ac102..13d20a6fc1 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts @@ -15,14 +15,20 @@ * limitations under the License. */ -import { Component, ViewEncapsulation, inject } from '@angular/core'; -import { takeUntil, map } from 'rxjs/operators'; +import { Component, inject, ViewEncapsulation } from '@angular/core'; +import { map } from 'rxjs/operators'; import { Observable } from 'rxjs'; -import { TaskFilterCloudModel, TaskFilterProperties, TaskFilterAction, TaskStatusFilter } from '../../models/filter-cloud.model'; +import { + TaskFilterAction, + TaskFilterCloudModel, + TaskFilterProperties, + TaskStatusFilter +} from '../../models/filter-cloud.model'; import { TaskFilterCloudService } from '../../services/task-filter-cloud.service'; import { DateCloudFilterType } from '../../../../models/date-cloud-filter.model'; import { BaseEditTaskFilterCloudComponent, DropdownOption } from './base-edit-task-filter-cloud.component'; import { set } from 'date-fns'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-cloud-edit-task-filter', @@ -98,7 +104,7 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone protected addFilter(filterToAdd: TaskFilterCloudModel): Observable { return this.taskFilterCloudService .addFilter(filterToAdd) - .pipe(takeUntil(this.onDestroy$)); + .pipe(takeUntilDestroyed(this.destroyRef)); } isDisabledForDefaultFilters(action: TaskFilterAction): boolean { diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/service-task-filters-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/service-task-filters-cloud.component.ts index 5a704369dc..7b95849f46 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/service-task-filters-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/service-task-filters-cloud.component.ts @@ -15,13 +15,22 @@ * limitations under the License. */ -import { Component, EventEmitter, OnChanges, Output, SimpleChanges, OnInit, ViewEncapsulation, inject } from '@angular/core'; +import { + Component, + EventEmitter, + inject, + OnChanges, + OnInit, + Output, + SimpleChanges, + ViewEncapsulation +} from '@angular/core'; import { Observable } from 'rxjs'; import { FilterParamsModel, ServiceTaskFilterCloudModel } from '../models/filter-cloud.model'; -import { takeUntil } from 'rxjs/operators'; import { BaseTaskFiltersCloudComponent } from './base-task-filters-cloud.component'; import { ServiceTaskFilterCloudService } from '../services/service-task-filter-cloud.service'; import { TranslationService } from '@alfresco/adf-core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-cloud-service-task-filters', @@ -67,7 +76,7 @@ export class ServiceTaskFiltersCloudComponent extends BaseTaskFiltersCloudCompon getFilters(appName: string): void { this.filters$ = this.serviceTaskFilterCloudService.getTaskListFilters(appName); - this.filters$.pipe(takeUntil(this.onDestroy$)).subscribe( + this.filters$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe( (res: ServiceTaskFilterCloudModel[]) => { this.resetFilter(); this.filters = res || []; diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.ts index 5ef4d3c3cb..cc4281533d 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.ts @@ -15,18 +15,28 @@ * limitations under the License. */ -import { Component, EventEmitter, OnChanges, Output, SimpleChanges, OnInit, ViewEncapsulation, inject } from '@angular/core'; +import { + Component, + EventEmitter, + inject, + OnChanges, + OnInit, + Output, + SimpleChanges, + ViewEncapsulation +} from '@angular/core'; import { Observable } from 'rxjs'; import { TaskFilterCloudService } from '../services/task-filter-cloud.service'; -import { TaskFilterCloudModel, FilterParamsModel } from '../models/filter-cloud.model'; +import { FilterParamsModel, TaskFilterCloudModel } from '../models/filter-cloud.model'; import { AppConfigService, TranslationService } from '@alfresco/adf-core'; -import { debounceTime, takeUntil, tap } from 'rxjs/operators'; +import { debounceTime, tap } from 'rxjs/operators'; import { BaseTaskFiltersCloudComponent } from './base-task-filters-cloud.component'; import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model'; import { TaskCloudEngineEvent } from '../../../models/engine-event-cloud.model'; import { TaskListCloudService } from '../../task-list/services/task-list-cloud.service'; import { TaskFilterCloudAdapter } from '../../../models/filter-cloud-model'; import { TASK_SEARCH_API_METHOD_TOKEN } from '../../../services/cloud-token.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-cloud-task-filters', @@ -88,7 +98,7 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp getFilters(appName: string): void { this.filters$ = this.taskFilterCloudService.getTaskListFilters(appName); - this.filters$.pipe(takeUntil(this.onDestroy$)).subscribe( + this.filters$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe( (res) => { this.resetFilter(); this.filters = res || []; @@ -267,7 +277,7 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp * */ getFilterKeysAfterExternalRefreshing(): void { - this.taskFilterCloudService.filterKeyToBeRefreshed$.pipe(takeUntil(this.onDestroy$)).subscribe((filterKey: string) => { + this.taskFilterCloudService.filterKeyToBeRefreshed$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((filterKey: string) => { this.updatedCountersSet.delete(filterKey); }); } diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.ts index 5a3097c4dd..fbf86f0384 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.ts @@ -15,17 +15,28 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, OnInit, ViewEncapsulation, OnDestroy, ViewChild } from '@angular/core'; +import { + Component, + DestroyRef, + EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges, + ViewChild, + ViewEncapsulation +} from '@angular/core'; import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model'; import { TaskCloudService } from '../../services/task-cloud.service'; -import { FormRenderingService, FormModel, ContentLinkModel, FormOutcomeEvent, FormFieldValidator, FORM_FIELD_VALIDATORS } from '@alfresco/adf-core'; +import { ContentLinkModel, FORM_FIELD_VALIDATORS, FormFieldValidator, FormModel, FormOutcomeEvent, FormRenderingService } from '@alfresco/adf-core'; import { AttachFileCloudWidgetComponent } from '../../../form/components/widgets/attach-file/attach-file-cloud-widget.component'; import { DropdownCloudWidgetComponent } from '../../../form/components/widgets/dropdown/dropdown-cloud.widget'; import { DateCloudWidgetComponent } from '../../../form/components/widgets/date/date-cloud.widget'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; import { FormCloudDisplayModeConfiguration } from '../../../services/form-fields.interfaces'; import { FormCloudComponent } from '../../../form/components/form-cloud.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-cloud-task-form', @@ -33,7 +44,7 @@ import { FormCloudComponent } from '../../../form/components/form-cloud.componen styleUrls: ['./task-form-cloud.component.scss'], encapsulation: ViewEncapsulation.None }) -export class TaskFormCloudComponent implements OnInit, OnChanges, OnDestroy { +export class TaskFormCloudComponent implements OnInit, OnChanges { /** App id to fetch corresponding form and values. */ @Input() appName: string = ''; @@ -138,7 +149,8 @@ export class TaskFormCloudComponent implements OnInit, OnChanges, OnDestroy { candidateGroups: string[] = []; loading: boolean = false; - onDestroy$ = new Subject(); + + private readonly destroyRef = inject(DestroyRef); constructor(private taskCloudService: TaskCloudService, private formRenderingService: FormRenderingService) { this.formRenderingService.setComponentTypeResolver('upload', () => AttachFileCloudWidgetComponent, true); @@ -176,7 +188,7 @@ export class TaskFormCloudComponent implements OnInit, OnChanges, OnDestroy { this.loading = true; this.taskCloudService .getTaskById(this.appName, this.taskId) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((details) => { this.taskDetails = details; this.loading = false; @@ -271,9 +283,4 @@ export class TaskFormCloudComponent implements OnInit, OnChanges, OnDestroy { onDisplayModeOff(displayModeConfiguration: FormCloudDisplayModeConfiguration) { this.displayModeOff.emit(displayModeConfiguration); } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 191df48ed1..fdc490083d 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 @@ -15,25 +15,36 @@ * limitations under the License. */ -import { Component, Input, EventEmitter, Output, OnDestroy, OnChanges, OnInit, ViewEncapsulation } from '@angular/core'; -import { takeUntil, concatMap, catchError, finalize } from 'rxjs/operators'; -import { Subject, of, forkJoin } from 'rxjs'; import { - CardViewDateItemModel, - CardViewItem, - CardViewTextItemModel, - CardViewBaseItemModel, - CardViewArrayItemModel, - TranslationService, + Component, + DestroyRef, + EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, + ViewEncapsulation +} from '@angular/core'; +import { catchError, concatMap, finalize } from 'rxjs/operators'; +import { forkJoin, of } from 'rxjs'; +import { AppConfigService, - UpdateNotification, - CardViewUpdateService, - CardViewDatetimeItemModel, CardViewArrayItem, - CardViewSelectItemModel + CardViewArrayItemModel, + CardViewBaseItemModel, + CardViewDateItemModel, + CardViewDatetimeItemModel, + CardViewItem, + CardViewSelectItemModel, + CardViewTextItemModel, + CardViewUpdateService, + TranslationService, + UpdateNotification } from '@alfresco/adf-core'; import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model'; import { TaskCloudService } from '../../services/task-cloud.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-cloud-task-header', @@ -41,7 +52,7 @@ import { TaskCloudService } from '../../services/task-cloud.service'; styleUrls: ['./task-header-cloud.component.scss'], encapsulation: ViewEncapsulation.None }) -export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges { +export class TaskHeaderCloudComponent implements OnInit, OnChanges { /** (Required) The name of the application. */ @Input() @@ -79,7 +90,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges { isLoading = true; processInstanceId: string; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor( private taskCloudService: TaskCloudService, @@ -93,13 +104,13 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges { ngOnInit() { this.taskCloudService.dataChangesDetected$ - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.loadTaskDetailsById(this.appName, this.taskId); }); this.cardViewUpdateService.itemUpdated$ - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(this.updateTaskDetails.bind(this) ); } @@ -346,9 +357,4 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges { private isValidSelection(filteredProperties: string[], cardItem: CardViewBaseItemModel): boolean { return filteredProperties ? filteredProperties.indexOf(cardItem.key) >= 0 : true; } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 367a585c42..fc091d4a6d 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 @@ -15,38 +15,51 @@ * limitations under the License. */ -import { OnChanges, Input, SimpleChanges, Output, EventEmitter, ContentChild, AfterContentInit, OnDestroy, OnInit, Directive } from '@angular/core'; +import { + AfterContentInit, + ContentChild, + DestroyRef, + Directive, + EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges +} from '@angular/core'; import { AppConfigService, - UserPreferencesService, - DataTableSchema, - UserPreferenceValues, - PaginatedComponent, - PaginationModel, - DataRowEvent, CustomEmptyContentTemplateDirective, DataCellEvent, - DataRowActionEvent, - DataRow, DataColumn, - ObjectDataTableAdapter + DataRow, + DataRowActionEvent, + DataRowEvent, + DataTableSchema, + ObjectDataTableAdapter, + PaginatedComponent, + PaginationModel, + UserPreferencesService, + UserPreferenceValues } from '@alfresco/adf-core'; import { taskPresetsCloudDefaultModel } from '../models/task-preset-cloud.model'; import { TaskQueryCloudRequestModel } from '../../../models/filter-cloud-model'; -import { BehaviorSubject, Observable, Subject } from 'rxjs'; +import { BehaviorSubject, Observable } from 'rxjs'; import { TaskListCloudSortingModel } from '../../../models/task-list-sorting.model'; -import { map, take, takeUntil } from 'rxjs/operators'; +import { map, take } from 'rxjs/operators'; import { TaskCloudService } from '../../services/task-cloud.service'; import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface'; import { TasksListCloudPreferences } from '../models/tasks-cloud-preferences'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Directive() // eslint-disable-next-line @angular-eslint/directive-class-suffix export abstract class BaseTaskListCloudComponent extends DataTableSchema - implements OnChanges, AfterContentInit, PaginatedComponent, OnDestroy, OnInit // eslint-disable-next-line @typescript-eslint/brace-style -{ + implements OnChanges, AfterContentInit, PaginatedComponent, OnInit { + @ContentChild(CustomEmptyContentTemplateDirective) emptyCustomContent: CustomEmptyContentTemplateDirective; @@ -138,11 +151,11 @@ export abstract class BaseTaskListCloudComponent private defaultSorting = { key: 'startDate', direction: 'desc' }; boundReplacePriorityValues: (row: DataRow, col: DataColumn) => any; - private onDestroy$ = new Subject(); - protected abstract isLoading$: Observable; protected isLoadingPreferences$ = new BehaviorSubject(true); + protected readonly destroyRef = inject(DestroyRef); + constructor( appConfigService: AppConfigService, private taskCloudService: TaskCloudService, @@ -165,7 +178,7 @@ export abstract class BaseTaskListCloudComponent ngOnInit() { this.userPreferences .select(UserPreferenceValues.PaginationSize) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((pageSize) => (this.size = pageSize)); } @@ -179,11 +192,6 @@ export abstract class BaseTaskListCloudComponent this.reload(); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - private retrieveTasksPreferences(): void { this.isLoadingPreferences$.next(true); this.cloudPreferenceService diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.ts index 685985e0c0..1317a74d3b 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.ts @@ -15,15 +15,16 @@ * limitations under the License. */ -import { Component, ViewEncapsulation, Input, Inject, OnDestroy } from '@angular/core'; +import { Component, Inject, Input, ViewEncapsulation } from '@angular/core'; import { AppConfigService, UserPreferencesService } 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 { Subject, combineLatest, BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, combineLatest } from 'rxjs'; import { PreferenceCloudServiceInterface, TASK_LIST_PREFERENCES_SERVICE_TOKEN } from '../../../services/public-api'; -import { map, takeUntil } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const PRESET_KEY = 'adf-cloud-service-task-list.presets'; @@ -33,12 +34,10 @@ const PRESET_KEY = 'adf-cloud-service-task-list.presets'; styleUrls: ['./base-task-list-cloud.component.scss'], encapsulation: ViewEncapsulation.None }) -export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent implements OnDestroy { +export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent { @Input() queryParams: { [key: string]: any } = {}; - private onDestroyServiceTaskList$ = new Subject(); - private isReloadingSubject$ = new BehaviorSubject(false); isLoading$ = combineLatest([this.isLoadingPreferences$, this.isReloadingSubject$]).pipe( map(([isLoadingPreferences, isReloading]) => isLoadingPreferences || isReloading) @@ -54,11 +53,6 @@ export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent im super(appConfigService, taskCloudService, userPreferences, PRESET_KEY, cloudPreferenceService); } - ngOnDestroy() { - this.onDestroyServiceTaskList$.next(true); - this.onDestroyServiceTaskList$.complete(); - } - reload() { this.isReloadingSubject$.next(true); @@ -66,7 +60,7 @@ export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent im if (this.requestNode.appName || this.requestNode.appName === '') { combineLatest([this.serviceTaskListCloudService.getServiceTaskByRequest(this.requestNode), this.isColumnSchemaCreated$]) - .pipe(takeUntil(this.onDestroyServiceTaskList$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe( ([tasks]) => { this.rows = tasks.list.entries; diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts index 81e7f6a6a6..363376bb8d 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts @@ -15,22 +15,27 @@ * limitations under the License. */ -import { Component, ViewEncapsulation, Input, Inject, OnDestroy, Optional } from '@angular/core'; +import { Component, Inject, Input, Optional, ViewEncapsulation } from '@angular/core'; import { AppConfigService, UserPreferencesService } 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, TASK_SEARCH_API_METHOD_TOKEN } from '../../../services/cloud-token.service'; +import { + TASK_LIST_CLOUD_TOKEN, + TASK_LIST_PREFERENCES_SERVICE_TOKEN, + TASK_SEARCH_API_METHOD_TOKEN +} from '../../../services/cloud-token.service'; import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface'; import { TaskListCloudServiceInterface } from '../../../services/task-list-cloud.service.interface'; -import { Subject, BehaviorSubject, combineLatest } from 'rxjs'; -import { filter, map, switchMap, take, takeUntil } from 'rxjs/operators'; +import { BehaviorSubject, combineLatest, Subject } from 'rxjs'; +import { filter, map, switchMap, take } from 'rxjs/operators'; import { VariableMapperService } from '../../../services/variable-mapper.sevice'; import { ProcessListDataColumnCustomData } from '../../../models/data-column-custom-data'; import { TaskCloudModel } from '../../../models/task-cloud.model'; import { PaginatedEntries } from '@alfresco/js-api'; import { TaskInstanceCloudListViewModel } from '../models/task-cloud-view.model'; import { TasksListDatatableAdapter } from '../datatable/task-list-datatable-adapter'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const PRESET_KEY = 'adf-cloud-task-list.presets'; @@ -40,7 +45,7 @@ const PRESET_KEY = 'adf-cloud-task-list.presets'; styleUrls: ['./base-task-list-cloud.component.scss'], encapsulation: ViewEncapsulation.None }) -export class TaskListCloudComponent extends BaseTaskListCloudComponent implements OnDestroy { +export class TaskListCloudComponent extends BaseTaskListCloudComponent { /** * 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 @@ -187,8 +192,6 @@ export class TaskListCloudComponent extends BaseTaskListCloudComponent(); - rows: TaskInstanceCloudListViewModel[] = []; dataAdapter: TasksListDatatableAdapter | undefined; @@ -223,7 +226,7 @@ export class TaskListCloudComponent extends BaseTaskListCloudComponent }) => { @@ -247,11 +250,6 @@ export class TaskListCloudComponent extends BaseTaskListCloudComponent(); - constructor(private appsProcessService: AppsProcessService) {} ngOnInit() { @@ -101,11 +107,6 @@ export class AppsListComponent implements OnInit, AfterContentInit, OnDestroy { this.load(); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - ngAfterContentInit() { if (this.emptyCustomContent) { this.hasEmptyCustomContentTemplate = true; diff --git a/lib/process-services/src/lib/form/form.component.ts b/lib/process-services/src/lib/form/form.component.ts index 23414b2ed0..f1e1d48cf5 100644 --- a/lib/process-services/src/lib/form/form.component.ts +++ b/lib/process-services/src/lib/form/form.component.ts @@ -16,36 +16,36 @@ */ import { + ChangeDetectorRef, Component, + DestroyRef, EventEmitter, - Input, - Output, - ViewEncapsulation, - SimpleChanges, - OnInit, - OnDestroy, - OnChanges, inject, - ChangeDetectorRef + Input, + OnChanges, + OnInit, + Output, + SimpleChanges, + ViewEncapsulation } from '@angular/core'; import { - WidgetVisibilityService, - FormService, + ContentLinkModel, + FormatSpacePipe, FormBaseComponent, - FormOutcomeModel, - FormEvent, FormErrorEvent, + FormEvent, FormFieldModel, FormModel, FormOutcomeEvent, - FormValues, - ContentLinkModel, - TaskProcessVariableModel, + FormOutcomeModel, FormRendererComponent, - FormatSpacePipe + FormService, + FormValues, + TaskProcessVariableModel, + WidgetVisibilityService } from '@alfresco/adf-core'; -import { from, Observable, of, Subject } from 'rxjs'; -import { switchMap, takeUntil } from 'rxjs/operators'; +import { from, Observable, of } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; import { EcmModelService } from './services/ecm-model.service'; import { ModelService } from './services/model.service'; import { EditorService } from './services/editor.service'; @@ -58,6 +58,7 @@ import { MatCardModule } from '@angular/material/card'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { TranslateModule } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-form', @@ -67,7 +68,7 @@ import { TranslateModule } from '@ngx-translate/core'; styleUrls: ['./form.component.scss'], encapsulation: ViewEncapsulation.None }) -export class FormComponent extends FormBaseComponent implements OnInit, OnDestroy, OnChanges { +export class FormComponent extends FormBaseComponent implements OnInit, OnChanges { protected formService = inject(FormService); protected taskFormService = inject(TaskFormService); protected taskService = inject(TaskService); @@ -132,16 +133,16 @@ export class FormComponent extends FormBaseComponent implements OnInit, OnDestro debugMode: boolean = false; - protected onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor() { super(); } ngOnInit() { - this.formService.formContentClicked.pipe(takeUntil(this.onDestroy$)).subscribe((content) => this.formContentClicked.emit(content)); + this.formService.formContentClicked.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((content) => this.formContentClicked.emit(content)); - this.formService.validateForm.pipe(takeUntil(this.onDestroy$)).subscribe((validateFormEvent) => { + this.formService.validateForm.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((validateFormEvent) => { if (validateFormEvent.errorsField.length > 0) { this.formError.next(validateFormEvent.errorsField); this.cdRef.detectChanges(); @@ -149,11 +150,6 @@ export class FormComponent extends FormBaseComponent implements OnInit, OnDestro }); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - ngOnChanges(changes: SimpleChanges) { const taskId = changes['taskId']; if (taskId?.currentValue) { diff --git a/lib/process-services/src/lib/form/start-form/start-form.component.ts b/lib/process-services/src/lib/form/start-form/start-form.component.ts index 0acb308d01..79b0ec3e58 100644 --- a/lib/process-services/src/lib/form/start-form/start-form.component.ts +++ b/lib/process-services/src/lib/form/start-form/start-form.component.ts @@ -19,15 +19,14 @@ import { Component, ElementRef, EventEmitter, + inject, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild, - ViewEncapsulation, - OnDestroy, - inject + ViewEncapsulation } from '@angular/core'; import { FormComponent } from '../form.component'; import { ContentLinkModel, FormOutcomeModel, FormRendererComponent } from '@alfresco/adf-core'; @@ -46,7 +45,7 @@ import { MatIconModule } from '@angular/material/icon'; styleUrls: ['./start-form.component.scss'], encapsulation: ViewEncapsulation.None }) -export class StartFormComponent extends FormComponent implements OnChanges, OnInit, OnDestroy { +export class StartFormComponent extends FormComponent implements OnChanges, OnInit { public processService = inject(ProcessService); /** Definition ID of the process to start, this parameter can not be use in combination with processId */ 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 ac23c61967..a475c8e69a 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,24 +15,23 @@ * limitations under the License. */ -import { Component, Inject, ViewEncapsulation, ViewChild, OnDestroy, OnInit } from '@angular/core'; +import { Component, DestroyRef, inject, Inject, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; -import { LoginDialogPanelComponent, TranslationService, AuthenticationService } from '@alfresco/adf-core'; +import { AuthenticationService, LoginDialogPanelComponent, TranslationService } from '@alfresco/adf-core'; import { AttachFileWidgetDialogComponentData } from './attach-file-widget-dialog-component.interface'; import { - DocumentListService, - SitesService, - SearchService, + AlfrescoApiService, ContentNodeSelectorPanelComponent, - AlfrescoApiService + DocumentListService, + SearchService, + SitesService } from '@alfresco/adf-content-services'; import { ExternalAlfrescoApiService } from '../../services/external-alfresco-api.service'; import { Node } from '@alfresco/js-api'; import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { TranslateModule } from '@ngx-translate/core'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-attach-file-widget-dialog', @@ -49,7 +48,7 @@ import { takeUntil } from 'rxjs/operators'; { provide: AlfrescoApiService, useClass: ExternalAlfrescoApiService } ] }) -export class AttachFileWidgetDialogComponent implements OnInit, OnDestroy { +export class AttachFileWidgetDialogComponent implements OnInit { @ViewChild('adfLoginPanel') loginPanel: LoginDialogPanelComponent; @@ -58,7 +57,7 @@ export class AttachFileWidgetDialogComponent implements OnInit, OnDestroy { buttonActionName: string; chosenNode: Node[]; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor( private translation: TranslationService, @@ -74,18 +73,12 @@ export class AttachFileWidgetDialogComponent implements OnInit, OnDestroy { } ngOnInit() { - this.authenticationService.onLogin.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.registerAndClose()); + this.authenticationService.onLogin.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.registerAndClose()); if (this.isLoggedIn()) { this.registerAndClose(); } } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - isLoggedIn(): boolean { return !!this.externalApiService.getInstance()?.isLoggedIn(); } 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 8fd2f046f3..b48bbe77a3 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 @@ -17,12 +17,24 @@ /* eslint-disable @angular-eslint/component-selector */ -import { Component, isDevMode, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; -import { AppConfigService, AppConfigValues, DownloadService, ErrorWidgetComponent, FormService, ThumbnailService } from '@alfresco/adf-core'; +import { Component, DestroyRef, inject, isDevMode, OnInit, ViewEncapsulation } from '@angular/core'; +import { + AppConfigService, + AppConfigValues, + DownloadService, + ErrorWidgetComponent, + FormService, + ThumbnailService +} from '@alfresco/adf-core'; import { AlfrescoIconComponent, ContentNodeDialogService, ContentService } from '@alfresco/adf-content-services'; -import { AlfrescoEndpointRepresentation, Node, NodeChildAssociation, RelatedContentRepresentation } from '@alfresco/js-api'; -import { from, of, Subject, zip } from 'rxjs'; -import { mergeMap, takeUntil } from 'rxjs/operators'; +import { + AlfrescoEndpointRepresentation, + Node, + NodeChildAssociation, + RelatedContentRepresentation +} from '@alfresco/js-api'; +import { from, of, zip } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; import { AttachFileWidgetDialogService } from './attach-file-widget-dialog.service'; import { UploadWidgetComponent } from '../upload/upload.widget'; import { ProcessContentService } from '../../services/process-content.service'; @@ -34,6 +46,7 @@ import { MatButtonModule } from '@angular/material/button'; import { MatMenuModule } from '@angular/material/menu'; import { MatListModule } from '@angular/material/list'; import { ActivatedRoute, Router } from '@angular/router'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'attach-widget', @@ -63,11 +76,12 @@ import { ActivatedRoute, Router } from '@angular/router'; }, encapsulation: ViewEncapsulation.None }) -export class AttachFileWidgetComponent extends UploadWidgetComponent implements OnInit, OnDestroy { +export class AttachFileWidgetComponent extends UploadWidgetComponent implements OnInit { typeId = 'AttachFileWidgetComponent'; repositoryList: AlfrescoEndpointRepresentation[] = []; private tempFilesList = []; - private onDestroy$ = new Subject(); + + private readonly destroyRef = inject(DestroyRef); constructor( public formService: FormService, @@ -96,18 +110,13 @@ export class AttachFileWidgetComponent extends UploadWidgetComponent implements this.repositoryList = repoList; }); - this.formService.taskSaved.pipe(takeUntil(this.onDestroy$)).subscribe((formSaved) => { + this.formService.taskSaved.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((formSaved) => { if (formSaved.form.id === this.field.form.id) { this.tempFilesList = []; } }); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - isFileSourceConfigured(): boolean { return !!this.field.params?.fileSource; } diff --git a/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.ts b/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.ts index 717028268f..02326416bd 100644 --- a/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.ts +++ b/lib/process-services/src/lib/form/widgets/dropdown/dropdown.widget.ts @@ -17,17 +17,24 @@ /* eslint-disable @angular-eslint/component-selector */ -import { Component, inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; -import { FormService, FormFieldOption, WidgetComponent, ErrorWidgetComponent, ErrorMessageModel, FormFieldModel } from '@alfresco/adf-core'; +import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { + ErrorMessageModel, + ErrorWidgetComponent, + FormFieldModel, + FormFieldOption, + FormService, + WidgetComponent +} from '@alfresco/adf-core'; import { ProcessDefinitionService } from '../../services/process-definition.service'; import { TaskFormService } from '../../services/task-form.service'; import { CommonModule } from '@angular/common'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; import { AbstractControl, FormControl, ReactiveFormsModule, ValidationErrors, ValidatorFn } from '@angular/forms'; -import { filter, takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; +import { filter } from 'rxjs/operators'; import { TranslateModule } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'dropdown-widget', @@ -48,14 +55,14 @@ import { TranslateModule } from '@ngx-translate/core'; }, encapsulation: ViewEncapsulation.None }) -export class DropdownWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { +export class DropdownWidgetComponent extends WidgetComponent implements OnInit { public formsService = inject(FormService); public taskFormService = inject(TaskFormService); public processDefinitionService = inject(ProcessDefinitionService); dropdownControl = new FormControl(undefined); - private readonly onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); get isReadOnlyType(): boolean { return this.field.type === 'readonly'; @@ -89,11 +96,6 @@ export class DropdownWidgetComponent extends WidgetComponent implements OnInit, this.initFormControl(); } - ngOnDestroy(): void { - this.onDestroy$.next(); - this.onDestroy$.complete(); - } - getValuesByTaskId() { this.taskFormService.getRestFieldValues(this.field.form.taskId, this.field.id).subscribe((formFieldOption) => { const options = []; @@ -134,7 +136,7 @@ export class DropdownWidgetComponent extends WidgetComponent implements OnInit, this.dropdownControl.valueChanges .pipe( filter(() => !!this.field), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((value) => { this.setOptionValue(value, this.field); 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 a158df351c..ca70ed9f29 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 @@ -15,18 +15,18 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; import { ProcessInstanceFilterRepresentation, UserProcessInstanceFilterRepresentation } from '@alfresco/js-api'; -import { Subject } from 'rxjs'; import { ProcessFilterService } from '../../services/process-filter.service'; import { AppsProcessService } from '../../../services/apps-process.service'; import { IconModel } from '../../../app-list/icon.model'; import { NavigationStart, Router } from '@angular/router'; -import { filter, takeUntil } from 'rxjs/operators'; +import { filter } from 'rxjs/operators'; import { CommonModule, Location } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { MatButtonModule } from '@angular/material/button'; import { IconComponent } from '@alfresco/adf-core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-process-instance-filters', @@ -36,7 +36,7 @@ import { IconComponent } from '@alfresco/adf-core'; styleUrls: ['./process-filters.component.scss'], encapsulation: ViewEncapsulation.None }) -export class ProcessFiltersComponent implements OnInit, OnChanges, OnDestroy { +export class ProcessFiltersComponent implements OnInit, OnChanges { /** * 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. @@ -78,10 +78,11 @@ export class ProcessFiltersComponent implements OnInit, OnChanges, OnDestroy { active = false; isProcessRoute: boolean; isProcessActive: boolean; - private onDestroy$ = new Subject(); private iconsMDL: IconModel; + private readonly destroyRef = inject(DestroyRef); + constructor( private processFilterService: ProcessFilterService, private appsProcessService: AppsProcessService, @@ -94,7 +95,7 @@ export class ProcessFiltersComponent implements OnInit, OnChanges, OnDestroy { this.router.events .pipe( filter((event) => event instanceof NavigationStart), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((navigationStart: NavigationStart) => { const activeRoute = navigationStart.url; @@ -248,9 +249,4 @@ export class ProcessFiltersComponent implements OnInit, OnChanges, OnDestroy { this.filters = []; this.currentFilter = undefined; } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 8dbdae2e99..6dcea875cb 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 @@ -16,12 +16,12 @@ */ import { CommonModule, DatePipe } from '@angular/common'; -import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild, OnDestroy } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; -import { Observable, Observer, Subject } from 'rxjs'; +import { Observable, Observer } from 'rxjs'; import { TaskDetailsEvent } from '../../../task-list'; import { ProcessService } from '../../services/process.service'; -import { share, takeUntil } from 'rxjs/operators'; +import { share } from 'rxjs/operators'; import { ProcessInstanceRepresentation, TaskRepresentation } from '@alfresco/js-api'; import { MatButtonModule } from '@angular/material/button'; import { TranslateModule } from '@ngx-translate/core'; @@ -29,6 +29,7 @@ import { MatChipsModule } from '@angular/material/chips'; import { MatListModule } from '@angular/material/list'; import { MatIconModule } from '@angular/material/icon'; import { StartFormComponent } from '../../../form'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-process-instance-tasks', @@ -37,7 +38,7 @@ import { StartFormComponent } from '../../../form'; templateUrl: './process-instance-tasks.component.html', styleUrls: ['./process-instance-tasks.component.css'] }) -export class ProcessInstanceTasksComponent implements OnInit, OnChanges, OnDestroy { +export class ProcessInstanceTasksComponent implements OnInit, OnChanges { /** The ID of the process instance to display tasks for. */ @Input() processInstanceDetails: ProcessInstanceRepresentation; @@ -72,7 +73,8 @@ export class ProcessInstanceTasksComponent implements OnInit, OnChanges, OnDestr private taskObserver: Observer; private completedTaskObserver: Observer; - private onDestroy$ = new Subject(); + + private readonly destroyRef = inject(DestroyRef); constructor(private processService: ProcessService, private dialog: MatDialog) { this.task$ = new Observable((observer) => (this.taskObserver = observer)).pipe(share()); @@ -80,14 +82,9 @@ export class ProcessInstanceTasksComponent implements OnInit, OnChanges, OnDestr } ngOnInit() { - this.task$.pipe(takeUntil(this.onDestroy$)).subscribe((task) => this.activeTasks.push(task)); + this.task$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((task) => this.activeTasks.push(task)); - this.completedTask$.pipe(takeUntil(this.onDestroy$)).subscribe((task) => this.completedTasks.push(task)); - } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); + this.completedTask$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((task) => this.completedTasks.push(task)); } ngOnChanges(changes: SimpleChanges) { 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 8d8e4bfbf2..056489efe6 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 @@ -15,22 +15,40 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild, ViewEncapsulation, OnDestroy } from '@angular/core'; -import { AppConfigService, AppConfigValues, EmptyContentComponent, FormValues, LocalizedDatePipe } from '@alfresco/adf-core'; +import { + Component, + DestroyRef, + EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { + AppConfigService, + AppConfigValues, + EmptyContentComponent, + FormValues, + LocalizedDatePipe +} from '@alfresco/adf-core'; import { AppsProcessService } from '../../../services/apps-process.service'; import { ProcessService } from '../../services/process.service'; -import { UntypedFormControl, Validators, AbstractControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { Observable, Subject, forkJoin } from 'rxjs'; -import { map, takeUntil } from 'rxjs/operators'; +import { AbstractControl, FormsModule, ReactiveFormsModule, UntypedFormControl, Validators } from '@angular/forms'; +import { forkJoin, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; import { MatAutocompleteModule, MatAutocompleteTrigger } from '@angular/material/autocomplete'; import { MatSelectChange, MatSelectModule } from '@angular/material/select'; import { StartFormComponent } from '../../../form'; import { AppDefinitionRepresentation, Node, + ProcessDefinitionRepresentation, ProcessInstanceRepresentation, RelatedContentRepresentation, - ProcessDefinitionRepresentation, RestVariable } from '@alfresco/js-api'; import { ActivitiContentService } from '../../../form/services/activiti-alfresco.service'; @@ -42,6 +60,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const MAX_LENGTH = 255; const DATE_TIME_IDENTIFIER_REG_EXP = new RegExp('%{datetime}', 'i'); @@ -69,7 +88,7 @@ const PROCESS_DEFINITION_IDENTIFIER_REG_EXP = new RegExp('%{processdefinition}', styleUrls: ['./start-process.component.scss'], encapsulation: ViewEncapsulation.None }) -export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestroy { +export class StartProcessInstanceComponent implements OnChanges, OnInit { /** * Limit the list of processes that can be started to those * contained in the specified app. @@ -152,7 +171,7 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr isAppsLoading = true; movedNodeToPS: FormValues; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor( private processService: ProcessService, @@ -174,7 +193,7 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr this.filteredProcessesDefinitions$ = this.processDefinitionInput.valueChanges.pipe( map((value) => this._filter(value)), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ); this.contentService.getAlfrescoRepositories().subscribe((repoList) => { @@ -185,11 +204,6 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr }); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - ngOnChanges(changes: SimpleChanges) { if (changes['values']?.currentValue) { this.moveNodeFromCStoPS(); diff --git a/lib/process-services/src/lib/task-list/components/no-task-details/no-task-detail-template.directive.spec.ts b/lib/process-services/src/lib/task-list/components/no-task-details/no-task-detail-template.directive.spec.ts index dc57825b54..2903324e7d 100644 --- a/lib/process-services/src/lib/task-list/components/no-task-details/no-task-detail-template.directive.spec.ts +++ b/lib/process-services/src/lib/task-list/components/no-task-details/no-task-detail-template.directive.spec.ts @@ -23,7 +23,7 @@ describe('NoTaskDetailsTemplateDirective', () => { let detailsComponent: TaskDetailsComponent; beforeEach(() => { - detailsComponent = new TaskDetailsComponent(null, null, null, null); + detailsComponent = new TaskDetailsComponent(null, null, null, null, null); component = new NoTaskDetailsTemplateDirective(detailsComponent); }); 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 0cd9c56cc1..bce075898e 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 @@ -15,14 +15,21 @@ * limitations under the License. */ -import { FormFieldModel, FormModel, DateFnsUtils, AdfDateFnsAdapter, ADF_DATE_FORMATS } from '@alfresco/adf-core'; -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, OnDestroy } from '@angular/core'; +import { ADF_DATE_FORMATS, AdfDateFnsAdapter, DateFnsUtils, FormFieldModel, FormModel } from '@alfresco/adf-core'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; -import { EMPTY, Observable, Subject } from 'rxjs'; +import { EMPTY, Observable } from 'rxjs'; import { Form } from '../../models/form.model'; import { TaskListService } from '../../services/tasklist.service'; -import { switchMap, defaultIfEmpty, takeUntil } from 'rxjs/operators'; -import { UntypedFormBuilder, AbstractControl, Validators, UntypedFormGroup, UntypedFormControl, ReactiveFormsModule } from '@angular/forms'; +import { defaultIfEmpty, switchMap } from 'rxjs/operators'; +import { + AbstractControl, + ReactiveFormsModule, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validators +} from '@angular/forms'; import { isValid } from 'date-fns'; import { TaskRepresentation } from '@alfresco/js-api'; import { CommonModule } from '@angular/common'; @@ -35,6 +42,7 @@ import { MatIconModule } from '@angular/material/icon'; import { MatSelectModule } from '@angular/material/select'; import { MatButtonModule } from '@angular/material/button'; import { PeopleWidgetComponent } from '../../../form'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const FORMAT_DATE = 'DD/MM/YYYY'; const MAX_LENGTH = 255; @@ -63,7 +71,7 @@ const MAX_LENGTH = 255; ], encapsulation: ViewEncapsulation.None }) -export class StartTaskComponent implements OnInit, OnDestroy { +export class StartTaskComponent implements OnInit { /** (required) The id of the app. */ @Input() appId: number; @@ -93,7 +101,7 @@ export class StartTaskComponent implements OnInit, OnDestroy { maxTaskNameLength: number = MAX_LENGTH; loading = false; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private taskService: TaskListService, private formBuilder: UntypedFormBuilder) {} @@ -110,11 +118,6 @@ export class StartTaskComponent implements OnInit, OnDestroy { this.buildForm(); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - buildForm(): void { this.taskForm = this.formBuilder.group({ name: new UntypedFormControl(this.taskDetailsModel.name, [ @@ -126,7 +129,7 @@ export class StartTaskComponent implements OnInit, OnDestroy { formKey: new UntypedFormControl('') }); - this.taskForm.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((taskFormValues) => this.setTaskDetails(taskFormValues)); + this.taskForm.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((taskFormValues) => this.setTaskDetails(taskFormValues)); } private whitespaceValidator(control: UntypedFormControl): any { 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 f546ec3acd..3b1fa89fc8 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 @@ -29,10 +29,10 @@ import { } from '@alfresco/adf-core'; import { Component, + DestroyRef, EventEmitter, Input, OnChanges, - OnDestroy, OnInit, Output, SimpleChanges, @@ -41,9 +41,9 @@ import { ViewEncapsulation } from '@angular/core'; import { MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; -import { Observable, Observer, of, Subject } from 'rxjs'; +import { Observable, Observer, of } from 'rxjs'; import { TaskListService } from '../../services/tasklist.service'; -import { catchError, share, takeUntil } from 'rxjs/operators'; +import { catchError, share } from 'rxjs/operators'; import { TaskFormComponent } from '../task-form/task-form.component'; import { PeopleProcessService } from '../../../services/people-process.service'; import { LightUserRepresentation, TaskQueryRepresentation, TaskRepresentation } from '@alfresco/js-api'; @@ -56,6 +56,7 @@ import { TaskCommentsComponent, TaskCommentsService } from '../../../task-commen import { ChecklistComponent } from '../checklist/checklist.component'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-task-details', @@ -86,7 +87,7 @@ import { MatCardModule } from '@angular/material/card'; styleUrls: ['./task-details.component.scss'], encapsulation: ViewEncapsulation.None }) -export class TaskDetailsComponent implements OnInit, OnChanges, OnDestroy { +export class TaskDetailsComponent implements OnInit, OnChanges { @ViewChild('errorDialog') errorDialog: TemplateRef; @@ -207,13 +208,13 @@ export class TaskDetailsComponent implements OnInit, OnChanges, OnDestroy { data: any; private peopleSearchObserver: Observer; - private onDestroy$ = new Subject(); constructor( - private taskListService: TaskListService, - private peopleProcessService: PeopleProcessService, - private cardViewUpdateService: CardViewUpdateService, - private dialog: MatDialog + private readonly taskListService: TaskListService, + private readonly peopleProcessService: PeopleProcessService, + private readonly cardViewUpdateService: CardViewUpdateService, + private readonly dialog: MatDialog, + private readonly destroyRef: DestroyRef ) {} ngOnInit() { @@ -223,14 +224,9 @@ export class TaskDetailsComponent implements OnInit, OnChanges, OnDestroy { this.loadDetails(this.taskId); } - this.cardViewUpdateService.itemUpdated$.pipe(takeUntil(this.onDestroy$)).subscribe(this.updateTaskDetails.bind(this)); + this.cardViewUpdateService.itemUpdated$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(this.updateTaskDetails.bind(this)); - this.cardViewUpdateService.itemClicked$.pipe(takeUntil(this.onDestroy$)).subscribe(this.clickTaskDetails.bind(this)); - } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); + this.cardViewUpdateService.itemClicked$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(this.clickTaskDetails.bind(this)); } ngOnChanges(changes: SimpleChanges): void { 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 4a76cb6291..e7e4c1f6cb 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 @@ -16,18 +16,18 @@ */ import { AppsProcessService } from '../../../services/apps-process.service'; -import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; -import { Subject } from 'rxjs'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; import { TaskFilterService } from '../../services/task-filter.service'; import { TaskListService } from '../../services/tasklist.service'; import { IconModel } from '../../../app-list/icon.model'; import { ActivatedRoute, NavigationStart, Router } from '@angular/router'; -import { filter, takeUntil } from 'rxjs/operators'; +import { filter } from 'rxjs/operators'; import { UserTaskFilterRepresentation } from '@alfresco/js-api'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { MatButtonModule } from '@angular/material/button'; import { IconComponent } from '@alfresco/adf-core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-task-filters', @@ -37,7 +37,7 @@ import { IconComponent } from '@alfresco/adf-core'; styleUrls: ['./task-filters.component.scss'], encapsulation: ViewEncapsulation.None }) -export class TaskFiltersComponent implements OnInit, OnChanges, OnDestroy { +export class TaskFiltersComponent implements OnInit, OnChanges { /** * Parameters to use for the task filter. If there is no match then * the default filter (the first one the list) is selected. @@ -76,12 +76,13 @@ export class TaskFiltersComponent implements OnInit, OnChanges, OnDestroy { currentFilter: UserTaskFilterRepresentation; filters: UserTaskFilterRepresentation[] = []; - private onDestroy$ = new Subject(); isTaskRoute: boolean; isTaskActive: boolean; private iconsMDL: IconModel; + private readonly destroyRef = inject(DestroyRef); + constructor( private taskFilterService: TaskFilterService, private taskListService: TaskListService, @@ -95,7 +96,7 @@ export class TaskFiltersComponent implements OnInit, OnChanges, OnDestroy { this.router.events .pipe( filter((event) => event instanceof NavigationStart), - takeUntil(this.onDestroy$) + takeUntilDestroyed(this.destroyRef) ) .subscribe((navigationStart: NavigationStart) => { const activeRoute = navigationStart.url; @@ -271,9 +272,4 @@ export class TaskFiltersComponent implements OnInit, OnChanges, OnDestroy { this.filters = []; this.currentFilter = undefined; } - - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } } 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 89395bb9f7..b021f4c1f2 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 @@ -90,10 +90,11 @@ export class TaskHeaderComponent implements OnChanges, OnInit { dateLocale: string; private currentUserId: number; - private readonly destroyRef = inject(DestroyRef); private readonly usersSubject$ = new BehaviorSubject[]>([]); users$ = this.usersSubject$.asObservable(); + private readonly destroyRef = inject(DestroyRef); + constructor( private peopleProcessService: PeopleProcessService, private translationService: TranslationService, 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 690e40b605..dc4c80c329 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,32 +16,45 @@ */ import { - DataRowEvent, - DataTableAdapter, - DataTableSchema, + AppConfigService, CustomEmptyContentTemplateDirective, CustomLoadingContentTemplateDirective, - AppConfigService, - PaginatedComponent, - UserPreferencesService, - UserPreferenceValues, - PaginationModel, DataCellEvent, + DataRowEvent, + DataTableAdapter, + DataTableComponent, + DataTableSchema, DEFAULT_PAGINATION, EmptyContentComponent, - DataTableComponent, LoadingContentTemplateDirective, - NoContentTemplateDirective + NoContentTemplateDirective, + PaginatedComponent, + PaginationModel, + UserPreferencesService, + UserPreferenceValues } from '@alfresco/adf-core'; -import { AfterContentInit, Component, ContentChild, EventEmitter, Input, OnChanges, Output, SimpleChanges, OnDestroy, OnInit } from '@angular/core'; -import { BehaviorSubject, Subject } from 'rxjs'; +import { + AfterContentInit, + Component, + ContentChild, + DestroyRef, + EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges +} from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; import { taskPresetsDefaultModel } from '../../models/task-preset.model'; import { TaskListService } from '../../services/tasklist.service'; -import { takeUntil, finalize } from 'rxjs/operators'; +import { finalize } from 'rxjs/operators'; import { TaskQueryRepresentation, TaskRepresentation } from '@alfresco/js-api'; import { CommonModule } from '@angular/common'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { TranslateModule } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export const PRESET_KEY = 'adf-task-list.presets'; @@ -60,7 +73,7 @@ export const PRESET_KEY = 'adf-task-list.presets'; templateUrl: './task-list.component.html', styleUrls: ['./task-list.component.css'] }) -export class TaskListComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent, OnDestroy, OnInit { +export class TaskListComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent, OnInit { @ContentChild(CustomEmptyContentTemplateDirective) customEmptyContent: CustomEmptyContentTemplateDirective; @@ -212,7 +225,7 @@ export class TaskListComponent extends DataTableSchema implements OnChanges, Aft */ hasCustomDataSource: boolean = false; - private onDestroy$ = new Subject(); + private readonly destroyRef = inject(DestroyRef); constructor(private taskListService: TaskListService, appConfigService: AppConfigService, private userPreferences: UserPreferencesService) { super(appConfigService, PRESET_KEY, taskPresetsDefaultModel); @@ -238,15 +251,10 @@ export class TaskListComponent extends DataTableSchema implements OnChanges, Aft ngOnInit() { this.userPreferences .select(UserPreferenceValues.PaginationSize) - .pipe(takeUntil(this.onDestroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((pageSize) => (this.size = pageSize)); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - setCustomDataSource(rows: any[]): void { if (rows) { this.rows = rows;