[ACS-8959] Introduce new takeUntilDestroyed operator where possible (#10388)

This commit is contained in:
dominikiwanekhyland
2024-11-19 11:54:00 +01:00
committed by GitHub
parent 3f6b60760f
commit 3078387325
128 changed files with 1452 additions and 1546 deletions

View File

@@ -15,10 +15,10 @@
* limitations under the License. * 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 { NodesApiService } from '../common/services/nodes-api.service';
import { Observable, Subject, zip } from 'rxjs'; import { Observable, zip } from 'rxjs';
import { concatMap, map, takeUntil, tap } from 'rxjs/operators'; import { concatMap, map, tap } from 'rxjs/operators';
import { AspectListService } from './services/aspect-list.service'; import { AspectListService } from './services/aspect-list.service';
import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox';
import { AspectEntry } from '@alfresco/js-api'; import { AspectEntry } from '@alfresco/js-api';
@@ -27,6 +27,7 @@ import { MatExpansionModule } from '@angular/material/expansion';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-aspect-list', selector: 'adf-aspect-list',
@@ -36,7 +37,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
styleUrls: ['./aspect-list.component.scss'], styleUrls: ['./aspect-list.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class AspectListComponent implements OnInit, OnDestroy { export class AspectListComponent implements OnInit {
/** Node Id of the node that we want to update */ /** Node Id of the node that we want to update */
@Input() @Input()
nodeId: string = ''; nodeId: string = '';
@@ -60,15 +61,10 @@ export class AspectListComponent implements OnInit, OnDestroy {
notDisplayedAspects: string[] = []; notDisplayedAspects: string[] = [];
hasEqualAspect: boolean = true; hasEqualAspect: boolean = true;
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor(private aspectListService: AspectListService, private nodeApiService: NodesApiService) {} constructor(private aspectListService: AspectListService, private nodeApiService: NodesApiService) {}
ngOnDestroy(): void {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
ngOnInit(): void { ngOnInit(): void {
let aspects$: Observable<AspectEntry[]>; let aspects$: Observable<AspectEntry[]>;
if (this.nodeId) { if (this.nodeId) {
@@ -89,10 +85,10 @@ export class AspectListComponent implements OnInit, OnDestroy {
this.updateCounter.emit(this.nodeAspects.length); this.updateCounter.emit(this.nodeAspects.length);
}), }),
concatMap(() => this.aspectListService.getAspects()), concatMap(() => this.aspectListService.getAspects()),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
); );
} else { } 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)))); this.aspects$ = aspects$.pipe(map((aspects) => aspects.filter((aspect) => !this.excludedAspects.includes(aspect.entry.id))));
} }

View File

@@ -15,15 +15,25 @@
* limitations under the License. * 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 { MatSelect, MatSelectModule } from '@angular/material/select';
import { Node, PathElement } from '@alfresco/js-api'; import { Node, PathElement } from '@alfresco/js-api';
import { DocumentListComponent } from '../document-list/components/document-list.component'; import { DocumentListComponent } from '../document-list/components/document-list.component';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-breadcrumb', selector: 'adf-breadcrumb',
@@ -34,7 +44,7 @@ import { TranslateModule } from '@ngx-translate/core';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-breadcrumb' } 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. */ /** Active node, builds UI based on folderNode.path.elements collection. */
@Input() @Input()
folderNode: Node = null; folderNode: Node = null;
@@ -86,7 +96,7 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
route: PathElement[] = []; route: PathElement[] = [];
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
get hasRoot(): boolean { get hasRoot(): boolean {
return !!this.root; return !!this.root;
@@ -104,7 +114,7 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
this.transform = this.transform ? this.transform : null; this.transform = this.transform ? this.transform : null;
if (this.target) { 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.folderNode = folderNode;
this.recalculateNodes(); this.recalculateNodes();
}); });
@@ -201,9 +211,4 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
} }
} }
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -16,10 +16,22 @@
*/ */
import { Category } from '@alfresco/js-api'; 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 { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { EMPTY, Observable, Subject, timer } from 'rxjs'; 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 { CategoriesManagementMode } from './categories-management-mode';
import { CategoryService } from '../services/category.service'; import { CategoryService } from '../services/category.service';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
@@ -30,6 +42,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list'; import { MatListModule } from '@angular/material/list';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
interface CategoryNameControlErrors { interface CategoryNameControlErrors {
duplicatedExistingCategory?: boolean; duplicatedExistingCategory?: boolean;
@@ -66,7 +79,6 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy {
private existingCategoryLoaded$ = new Subject<void>(); private existingCategoryLoaded$ = new Subject<void>();
private cancelExistingCategoriesLoading$ = new Subject<void>(); private cancelExistingCategoriesLoading$ = new Subject<void>();
private onDestroy$ = new Subject<void>();
private _categoryNameControl = new FormControl<string>( private _categoryNameControl = new FormControl<string>(
'', '',
[this.validateIfNotAlreadyAdded.bind(this), this.validateEmptyCategory, Validators.required], [this.validateIfNotAlreadyAdded.bind(this), this.validateEmptyCategory, Validators.required],
@@ -147,6 +159,8 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy {
@ViewChild('categoryNameInput') @ViewChild('categoryNameInput')
private categoryNameInputElement: ElementRef; private categoryNameInputElement: ElementRef;
private readonly destroyRef = inject(DestroyRef);
constructor(private categoryService: CategoryService) {} constructor(private categoryService: CategoryService) {}
ngOnInit() { ngOnInit() {
@@ -162,11 +176,11 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy {
this.cancelExistingCategoriesLoading$.next(); this.cancelExistingCategoriesLoading$.next();
}), }),
debounce((name: string) => (name ? timer(300) : EMPTY)), debounce((name: string) => (name ? timer(300) : EMPTY)),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((name: string) => this.onNameControlValueChange(name)); .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(); this.setCategoryNameControlErrorMessageKey();
@@ -178,7 +192,7 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy {
this._categoryNameControl.removeValidators(Validators.required); this._categoryNameControl.removeValidators(Validators.required);
this.categories.forEach((category) => this.initialCategories.push(category)); this.categories.forEach((category) => this.initialCategories.push(category));
if (this.classifiableChanged) { if (this.classifiableChanged) {
this.classifiableChanged.pipe(takeUntil(this.onDestroy$)).subscribe(() => { this.classifiableChanged.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
this.categories = []; this.categories = [];
this.categoryNameControlVisible = false; this.categoryNameControlVisible = false;
this.categoryNameControlVisibleChange.emit(false); this.categoryNameControlVisibleChange.emit(false);
@@ -188,8 +202,6 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy {
} }
ngOnDestroy() { ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
this.cancelExistingCategoriesLoading$.next(); this.cancelExistingCategoriesLoading$.next();
this.cancelExistingCategoriesLoading$.complete(); this.cancelExistingCategoriesLoading$.complete();
} }

View File

@@ -15,7 +15,7 @@
* limitations under the License. * 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 { Category, CategoryEntry, CategoryLinkBody, CategoryPaging, Node, TagBody, TagEntry, TagPaging } from '@alfresco/js-api';
import { forkJoin, Observable, of, Subject, zip } from 'rxjs'; import { forkJoin, Observable, of, Subject, zip } from 'rxjs';
import { import {
@@ -28,8 +28,8 @@ import {
UpdateNotification UpdateNotification
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { ContentMetadataService } from '../../services/content-metadata.service'; import { ContentMetadataService } from '../../services/content-metadata.service';
import { CardViewGroup, PresetConfig, ContentMetadataCustomPanel, ContentMetadataPanel } from '../../interfaces/content-metadata.interfaces'; import { CardViewGroup, ContentMetadataCustomPanel, ContentMetadataPanel, PresetConfig } from '../../interfaces/content-metadata.interfaces';
import { catchError, debounceTime, map, takeUntil } from 'rxjs/operators'; import { catchError, debounceTime, map } from 'rxjs/operators';
import { CardViewContentUpdateService } from '../../../common/services/card-view-content-update.service'; import { CardViewContentUpdateService } from '../../../common/services/card-view-content-update.service';
import { NodesApiService } from '../../../common/services/nodes-api.service'; import { NodesApiService } from '../../../common/services/nodes-api.service';
import { TagsCreatorMode } from '../../../tag/tags-creator/tags-creator-mode'; import { TagsCreatorMode } from '../../../tag/tags-creator/tags-creator-mode';
@@ -49,6 +49,7 @@ import { CategoriesManagementComponent } from '../../../category';
import { DynamicExtensionComponent } from '@alfresco/adf-extensions'; import { DynamicExtensionComponent } from '@alfresco/adf-extensions';
import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatProgressBarModule } from '@angular/material/progress-bar';
import { TagsCreatorComponent } from '../../../tag'; import { TagsCreatorComponent } from '../../../tag';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
const DEFAULT_SEPARATOR = ', '; const DEFAULT_SEPARATOR = ', ';
@@ -80,9 +81,7 @@ enum DefaultPanels {
host: { class: 'adf-content-metadata' }, host: { class: 'adf-content-metadata' },
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy { export class ContentMetadataComponent implements OnChanges, OnInit {
protected onDestroy$ = new Subject<boolean>();
/** (required) The node entity to fetch metadata about */ /** (required) The node entity to fetch metadata about */
@Input() @Input()
node: Node; node: Node;
@@ -167,6 +166,8 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
panelTitle: '' panelTitle: ''
}; };
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private contentMetadataService: ContentMetadataService, private contentMetadataService: ContentMetadataService,
private cardViewContentUpdateService: CardViewContentUpdateService, private cardViewContentUpdateService: CardViewContentUpdateService,
@@ -185,14 +186,14 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.cardViewContentUpdateService.itemUpdated$ this.cardViewContentUpdateService.itemUpdated$
.pipe(debounceTime(500), takeUntil(this.onDestroy$)) .pipe(debounceTime(500), takeUntilDestroyed(this.destroyRef))
.subscribe((updatedNode: UpdateNotification) => { .subscribe((updatedNode: UpdateNotification) => {
this.hasMetadataChanged = true; this.hasMetadataChanged = true;
this.targetProperty = updatedNode.target; this.targetProperty = updatedNode.target;
this.updateChanges(updatedNode.changed); 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.node.aspectNames = node?.aspectNames;
this.loadProperties(node); this.loadProperties(node);
}); });
@@ -274,11 +275,6 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
} }
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
updateChanges(updatedNodeChanges) { updateChanges(updatedNodeChanges) {
Object.keys(updatedNodeChanges).map((propertyGroup: string) => { Object.keys(updatedNodeChanges).map((propertyGroup: string) => {
if (typeof updatedNodeChanges[propertyGroup] === 'object') { if (typeof updatedNodeChanges[propertyGroup] === 'object') {

View File

@@ -15,27 +15,27 @@
* limitations under the License. * limitations under the License.
*/ */
import { inject, Injectable } from '@angular/core'; import { inject, Injectable, Injector, runInInjectionContext } from '@angular/core';
import { import {
CardViewItemProperties, AppConfigService,
CardViewItem,
CardViewTextItemModel,
CardViewBoolItemModel, CardViewBoolItemModel,
CardViewDateItemModel, CardViewDateItemModel,
CardViewSelectItemModel,
CardViewDatetimeItemModel, CardViewDatetimeItemModel,
CardViewIntItemModel,
CardViewLongItemModel,
CardViewFloatItemModel, CardViewFloatItemModel,
MultiValuePipe, CardViewIntItemModel,
AppConfigService, CardViewItem,
CardViewItemProperties,
CardViewLongItemModel,
CardViewSelectItemModel,
CardViewTextItemModel,
DecimalNumberPipe, DecimalNumberPipe,
LogService, LogService,
MultiValuePipe,
UserPreferencesService UserPreferencesService
} from '@alfresco/adf-core'; } 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 { 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_TEXT = 'd:text';
const D_MLTEXT = 'd:mltext'; const D_MLTEXT = 'd:mltext';
@@ -59,6 +59,8 @@ export class PropertyGroupTranslatorService {
valueSeparator: string; valueSeparator: string;
private readonly injector = inject(Injector);
constructor() { constructor() {
this.valueSeparator = this.appConfig.get<string>('content-metadata.multi-value-pipe-separator'); this.valueSeparator = this.appConfig.get<string>('content-metadata.multi-value-pipe-separator');
} }
@@ -156,10 +158,7 @@ export class PropertyGroupTranslatorService {
cardViewItemProperty = new CardViewFloatItemModel( cardViewItemProperty = new CardViewFloatItemModel(
Object.assign(propertyDefinition, { Object.assign(propertyDefinition, {
multivalued: isMultiValued, multivalued: isMultiValued,
pipes: [ pipes: [{ pipe: this.getDecimalNumberPipe() }, { pipe: new MultiValuePipe(), params: [this.valueSeparator] }]
{ pipe: new DecimalNumberPipe(this.userPreferenceService, this.appConfig) },
{ pipe: new MultiValuePipe(), params: [this.valueSeparator] }
]
}) })
); );
break; break;
@@ -218,4 +217,12 @@ export class PropertyGroupTranslatorService {
private isEmpty(value: any): boolean { private isEmpty(value: any): boolean {
return value === undefined || value === null || value === ''; return value === undefined || value === null || value === '';
} }
private getDecimalNumberPipe(): DecimalNumberPipe {
let decimalNumberPipe: DecimalNumberPipe;
runInInjectionContext(this.injector, () => {
decimalNumberPipe = new DecimalNumberPipe(this.userPreferenceService, this.appConfig);
});
return decimalNumberPipe;
}
} }

View File

@@ -15,32 +15,56 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation, OnDestroy } from '@angular/core';
import { import {
Component,
DestroyRef,
EventEmitter,
inject,
Input,
OnInit,
Output,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import {
CustomEmptyContentTemplateDirective,
DataColumnComponent,
DataColumnListComponent,
DataSorting,
HighlightDirective, HighlightDirective,
UserPreferencesService,
UserPreferenceValues,
InfinitePaginationComponent, InfinitePaginationComponent,
PaginatedComponent, PaginatedComponent,
DataSorting,
ShowHeaderMode, ShowHeaderMode,
ToolbarTitleComponent,
ToolbarComponent, ToolbarComponent,
DataColumnListComponent, ToolbarTitleComponent,
DataColumnComponent, UserPreferencesService,
CustomEmptyContentTemplateDirective UserPreferenceValues
} from '@alfresco/adf-core'; } 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 { 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 { DocumentListComponent } from '../../document-list/components/document-list.component';
import { RowFilter } from '../../document-list/data/row-filter.model'; import { RowFilter } from '../../document-list/data/row-filter.model';
import { ImageResolver } from '../../document-list/data/image-resolver.model'; import { ImageResolver } from '../../document-list/data/image-resolver.model';
import { CustomResourcesService } from '../../document-list/services/custom-resources.service'; import { CustomResourcesService } from '../../document-list/services/custom-resources.service';
import { ShareDataRow } from '../../document-list/data/share-data-row.model'; import { ShareDataRow } from '../../document-list/data/share-data-row.model';
import { NodeEntryEvent } from '../../document-list/components/node.event'; import { NodeEntryEvent } from '../../document-list/components/node.event';
import { debounceTime, takeUntil } from 'rxjs/operators'; import { debounceTime } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { ContentNodeSelectorPanelService } from './content-node-selector-panel.service'; import { ContentNodeSelectorPanelService } from './content-node-selector-panel.service';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatFormFieldModule } from '@angular/material/form-field'; 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 { DropdownBreadcrumbComponent } from '../../breadcrumb/dropdown-breadcrumb.component';
import { SearchQueryBuilderService } from '../../search/services/search-query-builder.service'; import { SearchQueryBuilderService } from '../../search/services/search-query-builder.service';
import { SearchPanelComponent } from '../../search/components/search-panel/search-panel.component'; import { SearchPanelComponent } from '../../search/components/search-panel/search-panel.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export type ValidationFunction = (entry: Node) => boolean; export type ValidationFunction = (entry: Node) => boolean;
@@ -92,7 +117,7 @@ export const defaultValidation = () => true;
host: { class: 'adf-content-node-selector-panel' }, host: { class: 'adf-content-node-selector-panel' },
providers: [SearchQueryBuilderService] providers: [SearchQueryBuilderService]
}) })
export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { export class ContentNodeSelectorPanelComponent implements OnInit {
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
DEFAULT_PAGINATION: Pagination = new Pagination({ DEFAULT_PAGINATION: Pagination = new Pagination({
maxItems: 25, maxItems: 25,
@@ -303,7 +328,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
searchPanelExpanded: boolean = false; searchPanelExpanded: boolean = false;
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private customResourcesService: CustomResourcesService, private customResourcesService: CustomResourcesService,
@@ -333,13 +358,13 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
} }
ngOnInit() { 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.searchTerm = searchValue;
this.queryBuilderService.userQuery = searchValue.length > 0 ? `${searchValue}*` : searchValue; this.queryBuilderService.userQuery = searchValue.length > 0 ? `${searchValue}*` : searchValue;
this.queryBuilderService.update(); this.queryBuilderService.update();
}); });
this.queryBuilderService.updated.pipe(takeUntil(this.onDestroy$)).subscribe((searchRequest) => { this.queryBuilderService.updated.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((searchRequest) => {
if (searchRequest) { if (searchRequest) {
this.hasValidQuery = true; this.hasValidQuery = true;
this.prepareDialogForNewSearch(searchRequest); 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) { if (this.hasValidQuery) {
this.showSearchResults(results); this.showSearchResults(results);
} }
@@ -359,7 +384,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
this.userPreferencesService this.userPreferencesService
.select(UserPreferenceValues.PaginationSize) .select(UserPreferenceValues.PaginationSize)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((pagSize) => (this.pageSize = pagSize)); .subscribe((pagSize) => (this.pageSize = pagSize));
this.target = this.documentList; this.target = this.documentList;
@@ -380,16 +405,11 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
this.resetPagination(); this.resetPagination();
this.setSearchScopeToNodes(); 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); this.currentFolder.emit(currentNode);
}); });
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
toggleSearchPanel() { toggleSearchPanel() {
this.searchPanelExpanded = !this.searchPanelExpanded; this.searchPanelExpanded = !this.searchPanelExpanded;
} }
@@ -400,7 +420,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
private onFileUploadEvent() { private onFileUploadEvent() {
this.uploadService.fileUploadComplete this.uploadService.fileUploadComplete
.pipe(debounceTime(500), takeUntil(this.onDestroy$)) .pipe(debounceTime(500), takeUntilDestroyed(this.destroyRef))
.subscribe((fileUploadEvent: FileUploadCompleteEvent) => { .subscribe((fileUploadEvent: FileUploadCompleteEvent) => {
this.currentUploadBatch.push(fileUploadEvent.data); this.currentUploadBatch.push(fileUploadEvent.data);
if (!this.uploadService.isUploading()) { if (!this.uploadService.isUploading()) {
@@ -412,7 +432,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
} }
private onFileUploadDeletedEvent() { 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.unselectRowFromNodeId(deletedFileEvent.file.data.entry.id);
this.documentList.reloadWithoutResettingSelection(); this.documentList.reloadWithoutResettingSelection();
}); });

View File

@@ -15,9 +15,15 @@
* limitations under the License. * 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 { 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 { Node } from '@alfresco/js-api';
import { AllowableOperationsEnum } from '../common/models/allowable-operations.enum'; import { AllowableOperationsEnum } from '../common/models/allowable-operations.enum';
import { ContentService } from '../common/services/content.service'; 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 { NodeEntryEvent } from '../document-list/components/node.event';
import { NodeAction } from '../document-list/models/node-action.enum'; import { NodeAction } from '../document-list/models/node-action.enum';
import { OverlayContainer } from '@angular/cdk/overlay'; import { OverlayContainer } from '@angular/cdk/overlay';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { TranslateModule } from '@ngx-translate/core'; 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 { ContentNodeSelectorPanelComponent } from './content-node-selector-panel/content-node-selector-panel.component';
import { UploadButtonComponent } from '../upload/components/upload-button.component'; import { UploadButtonComponent } from '../upload/components/upload-button.component';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-content-node-selector', selector: 'adf-content-node-selector',
@@ -64,8 +69,7 @@ import { MatButtonModule } from '@angular/material/button';
styleUrls: ['./content-node-selector.component.scss'], styleUrls: ['./content-node-selector.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class ContentNodeSelectorComponent implements OnInit, OnDestroy { export class ContentNodeSelectorComponent implements OnInit {
private onDestroy$ = new Subject<void>();
title: string; title: string;
action: NodeAction; action: NodeAction;
@@ -81,6 +85,8 @@ export class ContentNodeSelectorComponent implements OnInit, OnDestroy {
emptyFolderImageUrl: string = './assets/images/empty_doc_lib.svg'; emptyFolderImageUrl: string = './assets/images/empty_doc_lib.svg';
breadcrumbFolderNode: Node; breadcrumbFolderNode: Node;
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private translation: TranslationService, private translation: TranslationService,
private contentService: ContentService, private contentService: ContentService,
@@ -99,7 +105,7 @@ export class ContentNodeSelectorComponent implements OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.dialog this.dialog
.keydownEvents() .keydownEvents()
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((event) => { .subscribe((event) => {
if (event?.key === 'Escape') { if (event?.key === 'Escape') {
event.preventDefault(); event.preventDefault();
@@ -110,28 +116,22 @@ export class ContentNodeSelectorComponent implements OnInit, OnDestroy {
this.dialog this.dialog
.backdropClick() .backdropClick()
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => { .subscribe(() => {
this.close(); this.close();
}); });
this.dialog this.dialog
.afterOpened() .afterOpened()
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => { .subscribe(() => {
this.overlayContainer.getContainerElement().setAttribute('role', 'main'); 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; this.uploadStarted = true;
}); });
} }
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
}
close() { close() {
this.dialog.close(); this.dialog.close();
this.overlayContainer.getContainerElement().setAttribute('role', 'region'); this.overlayContainer.getContainerElement().setAttribute('role', 'region');

View File

@@ -15,18 +15,17 @@
* limitations under the License. * 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 { MAT_DIALOG_DATA, MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatSlideToggleChange, MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatSlideToggleChange, MatSlideToggleModule } from '@angular/material/slide-toggle';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Subject } from 'rxjs';
import { ContentService } from '../common/services/content.service'; import { ContentService } from '../common/services/content.service';
import { SharedLinksApiService } from './services/shared-links-api.service'; import { SharedLinksApiService } from './services/shared-links-api.service';
import { SharedLinkBodyCreate } from '@alfresco/js-api'; import { SharedLinkBodyCreate } from '@alfresco/js-api';
import { ClipboardDirective, ConfirmDialogComponent } from '@alfresco/adf-core'; import { ClipboardDirective, ConfirmDialogComponent } from '@alfresco/adf-core';
import { ContentNodeShareSettings } from './content-node-share.settings'; import { ContentNodeShareSettings } from './content-node-share.settings';
import { RenditionService } from '../common/services/rendition.service'; 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 { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
@@ -61,7 +60,7 @@ interface SharedDialogFormProps {
host: { class: 'adf-share-dialog' }, host: { class: 'adf-share-dialog' },
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class ShareDialogComponent implements OnInit, OnDestroy { export class ShareDialogComponent implements OnInit {
private minDateValidator = (control: FormControl<Date>): any => private minDateValidator = (control: FormControl<Date>): any =>
isBefore(endOfDay(new Date(control.value)), this.minDate) ? { invalidDate: true } : null; 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 }) @ViewChild('slideToggleExpirationDate', { static: true })
slideToggleExpirationDate; slideToggleExpirationDate;
private onDestroy$ = new Subject<boolean>();
constructor( constructor(
private sharedLinksApiService: SharedLinksApiService, private sharedLinksApiService: SharedLinksApiService,
private dialogRef: MatDialogRef<ShareDialogComponent>, private dialogRef: MatDialogRef<ShareDialogComponent>,
@@ -121,12 +117,6 @@ export class ShareDialogComponent implements OnInit, OnDestroy {
get time(): FormControl<Date> { get time(): FormControl<Date> {
return this.form.controls['time']; return this.form.controls['time'];
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
onSlideShareChange(event: MatSlideToggleChange) { onSlideShareChange(event: MatSlideToggleChange) {
if (event.checked) { if (event.checked) {
this.createSharedLinks(this.data.node.entry.id); this.createSharedLinks(this.data.node.entry.id);

View File

@@ -15,21 +15,21 @@
* limitations under the License. * 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 { MatDialog } from '@angular/material/dialog';
import { NodeEntry, NodesApi } from '@alfresco/js-api'; import { NodeEntry, NodesApi } from '@alfresco/js-api';
import { ShareDialogComponent } from './content-node-share.dialog'; 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 { AlfrescoApiService } from '../services/alfresco-api.service';
import { takeUntil } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Directive({ @Directive({
selector: '[adf-share]', selector: '[adf-share]',
standalone: true, standalone: true,
exportAs: 'adfShare' exportAs: 'adfShare'
}) })
export class NodeSharedDirective implements OnChanges, OnDestroy { export class NodeSharedDirective implements OnChanges {
isFile: boolean = false; isFile: boolean = false;
isShared: boolean = false; isShared: boolean = false;
@@ -42,7 +42,6 @@ export class NodeSharedDirective implements OnChanges, OnDestroy {
@Input() @Input()
baseShareUrl: string; baseShareUrl: string;
private onDestroy$ = new Subject<boolean>();
_nodesApi: NodesApi; _nodesApi: NodesApi;
get nodesApi(): NodesApi { get nodesApi(): NodesApi {
@@ -50,13 +49,9 @@ export class NodeSharedDirective implements OnChanges, OnDestroy {
return this._nodesApi; return this._nodesApi;
} }
private readonly destroyRef = inject(DestroyRef);
constructor(private dialog: MatDialog, private zone: NgZone, private alfrescoApiService: AlfrescoApiService) {} constructor(private dialog: MatDialog, private zone: NgZone, private alfrescoApiService: AlfrescoApiService) {}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
shareNode(nodeEntry: NodeEntry) { shareNode(nodeEntry: NodeEntry) {
if (nodeEntry?.entry?.isFile) { if (nodeEntry?.entry?.isFile) {
// shared and favorite // shared and favorite
@@ -92,7 +87,7 @@ export class NodeSharedDirective implements OnChanges, OnDestroy {
} }
ngOnChanges() { ngOnChanges() {
this.zone.onStable.pipe(takeUntil(this.onDestroy$)).subscribe(() => { this.zone.onStable.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
if (this.node?.entry) { if (this.node?.entry) {
this.isFile = this.node.entry.isFile; this.isFile = this.node.entry.isFile;
this.isShared = this.node.entry.properties ? this.node.entry.properties['qshare:sharedId'] : false; this.isShared = this.node.entry.properties ? this.node.entry.properties['qshare:sharedId'] : false;

View File

@@ -15,21 +15,21 @@
* limitations under the License. * limitations under the License.
*/ */
import { Observable, Subject } from 'rxjs'; import { Observable } from 'rxjs';
import { Component, OnInit, Output, EventEmitter, OnDestroy, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, EventEmitter, inject, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { import {
UntypedFormBuilder,
UntypedFormGroup,
Validators,
UntypedFormControl,
AbstractControl, AbstractControl,
FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
FormsModule UntypedFormBuilder,
UntypedFormControl,
UntypedFormGroup,
Validators
} from '@angular/forms'; } from '@angular/forms';
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { QueriesApi, SiteBodyCreate, SiteEntry, SitePaging } from '@alfresco/js-api'; import { QueriesApi, SiteBodyCreate, SiteEntry, SitePaging } from '@alfresco/js-api';
import { NotificationService } from '@alfresco/adf-core'; 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 { SitesService } from '../../common/services/sites.service';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
@@ -39,6 +39,7 @@ import { AutoFocusDirective } from '../../directives';
import { MatRadioModule } from '@angular/material/radio'; import { MatRadioModule } from '@angular/material/radio';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { AlfrescoApiService } from '../../services'; import { AlfrescoApiService } from '../../services';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-library-dialog', selector: 'adf-library-dialog',
@@ -60,7 +61,7 @@ import { AlfrescoApiService } from '../../services';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-library-dialog' } host: { class: 'adf-library-dialog' }
}) })
export class LibraryDialogComponent implements OnInit, OnDestroy { export class LibraryDialogComponent implements OnInit {
/** Emitted when an error occurs. */ /** Emitted when an error occurs. */
@Output() @Output()
error: EventEmitter<any> = new EventEmitter<any>(); error: EventEmitter<any> = new EventEmitter<any>();
@@ -73,8 +74,6 @@ export class LibraryDialogComponent implements OnInit, OnDestroy {
@Output() @Output()
success: EventEmitter<any> = new EventEmitter<any>(); success: EventEmitter<any> = new EventEmitter<any>();
onDestroy$: Subject<boolean> = new Subject<boolean>();
createTitle = 'LIBRARY.DIALOG.CREATE_TITLE'; createTitle = 'LIBRARY.DIALOG.CREATE_TITLE';
libraryTitleExists = false; libraryTitleExists = false;
form: UntypedFormGroup; form: UntypedFormGroup;
@@ -96,6 +95,8 @@ export class LibraryDialogComponent implements OnInit, OnDestroy {
return this._queriesApi; return this._queriesApi;
} }
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private alfrescoApiService: AlfrescoApiService, private alfrescoApiService: AlfrescoApiService,
private sitesService: SitesService, private sitesService: SitesService,
@@ -126,7 +127,7 @@ export class LibraryDialogComponent implements OnInit, OnDestroy {
(title) => this.checkLibraryNameExists(title), (title) => this.checkLibraryNameExists(title),
(title) => title (title) => title
), ),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((title: string) => { .subscribe((title: string) => {
if (!this.form.controls['id'].dirty && this.canGenerateId(title)) { 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 { get title(): string {
const { title } = this.form.value; const { title } = this.form.value;

View File

@@ -30,7 +30,16 @@ import {
import { FavoritePaging, FavoritePagingList, Node, NodeEntry, NodePaging } from '@alfresco/js-api'; import { FavoritePaging, FavoritePagingList, Node, NodeEntry, NodePaging } from '@alfresco/js-api';
import { HarnessLoader } from '@angular/cdk/testing'; import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; 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 { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
@@ -84,6 +93,7 @@ describe('DocumentList', () => {
let spyFolder: any; let spyFolder: any;
let spyFolderNode: any; let spyFolderNode: any;
let authenticationService: AuthenticationService; let authenticationService: AuthenticationService;
let injector: Injector;
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
@@ -106,6 +116,7 @@ describe('DocumentList', () => {
contentService = TestBed.inject(ContentService); contentService = TestBed.inject(ContentService);
appConfigService = TestBed.inject(AppConfigService); appConfigService = TestBed.inject(AppConfigService);
authenticationService = TestBed.inject(AuthenticationService); authenticationService = TestBed.inject(AuthenticationService);
injector = TestBed.inject(Injector);
spyFolder = spyOn(documentListService, 'getFolder').and.returnValue(of({ list: {} })); spyFolder = spyOn(documentListService, 'getFolder').and.returnValue(of({ list: {} }));
spyFolderNode = spyOn(documentListService, 'getFolderNode').and.returnValue(of(new NodeEntry({ entry: new Node() }))); 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, 'resetSelection').and.callThrough();
spyOn(documentList, 'reload').and.callThrough(); spyOn(documentList, 'reload').and.callThrough();
documentList.ngOnDestroy(); fixture.destroy();
documentListService.reload(); documentListService.reload();
expect(documentList.resetSelection).not.toHaveBeenCalled(); expect(documentList.resetSelection).not.toHaveBeenCalled();
@@ -158,7 +169,7 @@ describe('DocumentList', () => {
it('should not reset selection after component is destroyed', () => { it('should not reset selection after component is destroyed', () => {
spyOn(documentList, 'resetSelection').and.callThrough(); spyOn(documentList, 'resetSelection').and.callThrough();
documentList.ngOnDestroy(); fixture.destroy();
documentListService.resetSelection(); documentListService.resetSelection();
expect(documentList.resetSelection).not.toHaveBeenCalled(); expect(documentList.resetSelection).not.toHaveBeenCalled();
@@ -1126,7 +1137,9 @@ describe('DocumentList', () => {
it('should display [empty folder] template ', () => { it('should display [empty folder] template ', () => {
fixture.detectChanges(); 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(documentList.dataTable).toBeDefined();
expect(fixture.debugElement.query(By.css('adf-empty-list'))).not.toBeNull(); 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', () => { 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(documentList.isEmpty()).toBeTruthy();
expect(element.querySelector('alfresco-pagination')).toBe(null); expect(element.querySelector('alfresco-pagination')).toBe(null);

View File

@@ -51,12 +51,13 @@ import {
AfterContentInit, AfterContentInit,
Component, Component,
ContentChild, ContentChild,
DestroyRef,
ElementRef, ElementRef,
EventEmitter, EventEmitter,
HostListener, HostListener,
inject,
Input, Input,
OnChanges, OnChanges,
OnDestroy,
OnInit, OnInit,
Output, Output,
SimpleChanges, SimpleChanges,
@@ -65,7 +66,6 @@ import {
} from '@angular/core'; } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { BehaviorSubject, of, Subject } from 'rxjs'; import { BehaviorSubject, of, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { ContentService, NodesApiService } from '../../common'; import { ContentService, NodesApiService } from '../../common';
import { FilterSearch } from '../../search'; import { FilterSearch } from '../../search';
import { RowFilter } from '../data/row-filter.model'; import { RowFilter } from '../data/row-filter.model';
@@ -86,6 +86,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { AlfrescoApiService } from '../../services/alfresco-api.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
const BYTES_TO_MB_CONVERSION_VALUE = 1048576; const BYTES_TO_MB_CONVERSION_VALUE = 1048576;
@@ -118,7 +119,7 @@ const BYTES_TO_MB_CONVERSION_VALUE = 1048576;
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-document-list' } 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 SINGLE_CLICK_NAVIGATION: string = 'click';
static DOUBLE_CLICK_NAVIGATION: string = 'dblclick'; static DOUBLE_CLICK_NAVIGATION: string = 'dblclick';
@@ -444,7 +445,8 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On
private rowMenuCache: { [key: string]: ContentActionModel[] } = {}; private rowMenuCache: { [key: string]: ContentActionModel[] } = {};
private loadingTimeout: any; private loadingTimeout: any;
private onDestroy$ = new Subject<boolean>();
private readonly destroyRef = inject(DestroyRef);
private _nodesApi: NodesApi; private _nodesApi: NodesApi;
get nodesApi(): NodesApi { get nodesApi(): NodesApi {
@@ -466,13 +468,13 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On
private dialog: MatDialog private dialog: MatDialog
) { ) {
super(appConfig, 'default', presetsDefaultModel); 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.dataTableService.rowUpdate.next({ id: node.id, obj: { entry: node } });
}); });
this.userPreferencesService this.userPreferencesService
.select(UserPreferenceValues.PaginationSize) .select(UserPreferenceValues.PaginationSize)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed())
.subscribe((pagSize) => { .subscribe((pagSize) => {
this.maxItems = this._pagination.maxItems = 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.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(); this.enforceSingleClickNavigationForMobile();
if (this.filterValue && Object.keys(this.filterValue).length > 0) { 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.setPresetKey(this.columnsPresetKey);
} }
this.documentListService.reload$.pipe(takeUntil(this.onDestroy$)).subscribe(() => { this.documentListService.reload$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
this.resetSelection(); this.resetSelection();
this.reload(); this.reload();
}); });
this.documentListService.resetSelection$.pipe(takeUntil(this.onDestroy$)).subscribe(() => { this.documentListService.resetSelection$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
this.resetSelection(); this.resetSelection();
}); });
} }
ngAfterContentInit() { ngAfterContentInit() {
if (this.columnList) { if (this.columnList) {
this.columnList.columns.changes.pipe(takeUntil(this.onDestroy$)).subscribe(() => { this.columnList.columns.changes.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
this.createColumns(); this.createColumns();
this.data.setColumns(this.columns); 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); const handlerSub = typeof action.handler === 'function' ? action.handler(node, this, action.permission) : of(true);
if (typeof action.execute === 'function' && handlerSub) { 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; this._pagination.maxItems = this.maxItems;
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
private handleError(err: any) { private handleError(err: any) {
if (err.message) { if (err.message) {
try { try {

View File

@@ -15,15 +15,27 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, Inject, OnInit, OnChanges, SimpleChanges, Input, Output, EventEmitter, OnDestroy } from '@angular/core'; import {
import { PaginationModel, DataSorting, HeaderFilterTemplateDirective } from '@alfresco/adf-core'; 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 { SearchHeaderQueryBuilderService } from '../../../search/services/search-header-query-builder.service';
import { FilterSearch } from './../../../search/models/filter-search.interface'; 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 { ADF_DOCUMENT_PARENT_COMPONENT } from '../document-list.token';
import { CommonModule } from '@angular/common'; 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({ @Component({
selector: 'adf-filter-header', selector: 'adf-filter-header',
@@ -31,7 +43,7 @@ import { SearchFilterContainerComponent } from '../../../search/components/searc
imports: [CommonModule, HeaderFilterTemplateDirective, SearchFilterContainerComponent], imports: [CommonModule, HeaderFilterTemplateDirective, SearchFilterContainerComponent],
templateUrl: './filter-header.component.html' templateUrl: './filter-header.component.html'
}) })
export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy { export class FilterHeaderComponent implements OnInit, OnChanges {
/** (optional) Initial filter value to sort . */ /** (optional) Initial filter value to sort . */
@Input() @Input()
value: any = {}; value: any = {};
@@ -45,14 +57,15 @@ export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy {
filterSelection: EventEmitter<FilterSearch[]> = new EventEmitter(); filterSelection: EventEmitter<FilterSearch[]> = new EventEmitter();
isFilterServiceActive: boolean; isFilterServiceActive: boolean;
private onDestroy$ = new Subject<boolean>();
private readonly destroyRef = inject(DestroyRef);
constructor(@Inject(ADF_DOCUMENT_PARENT_COMPONENT) private documentList: any, private searchFilterQueryBuilder: SearchHeaderQueryBuilderService) { constructor(@Inject(ADF_DOCUMENT_PARENT_COMPONENT) private documentList: any, private searchFilterQueryBuilder: SearchHeaderQueryBuilderService) {
this.isFilterServiceActive = this.searchFilterQueryBuilder.isFilterServiceActive(); this.isFilterServiceActive = this.searchFilterQueryBuilder.isFilterServiceActive();
} }
ngOnInit() { 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.node = newNodePaging;
this.documentList.reload(); this.documentList.reload();
}); });
@@ -81,13 +94,13 @@ export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy {
} }
initDataPagination() { 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); this.searchFilterQueryBuilder.setupCurrentPagination(newPagination.maxItems, newPagination.skipCount);
}); });
} }
initDataSorting() { 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); this.searchFilterQueryBuilder.setSorting(sorting);
}); });
} }
@@ -110,9 +123,4 @@ export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy {
}); });
} }
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -15,15 +15,24 @@
* limitations under the License. * 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 { NodeEntry, Site } from '@alfresco/js-api';
import { ShareDataRow } from '../../data/share-data-row.model'; import { ShareDataRow } from '../../data/share-data-row.model';
import { NodesApiService } from '../../../common/services/nodes-api.service'; import { NodesApiService } from '../../../common/services/nodes-api.service';
import { BehaviorSubject, Subject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-library-name-column', 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' class: 'adf-datatable-content-cell adf-datatable-link adf-library-name-column'
} }
}) })
export class LibraryNameColumnComponent implements OnInit, OnDestroy { export class LibraryNameColumnComponent implements OnInit {
@Input() @Input()
context: any; context: any;
@@ -62,14 +71,14 @@ export class LibraryNameColumnComponent implements OnInit, OnDestroy {
displayText$ = new BehaviorSubject<string>(''); displayText$ = new BehaviorSubject<string>('');
node: NodeEntry; node: NodeEntry;
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor(private element: ElementRef, private nodesApiService: NodesApiService) {} constructor(private element: ElementRef, private nodesApiService: NodesApiService) {}
ngOnInit() { ngOnInit() {
this.updateValue(); 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; const row: ShareDataRow = this.context.row;
if (row) { if (row) {
const { entry } = row.node; const { entry } = row.node;
@@ -121,8 +130,4 @@ export class LibraryNameColumnComponent implements OnInit, OnDestroy {
return isDuplicate ? `${title} (${id})` : `${title}`; return isDuplicate ? `${title} (${id})` : `${title}`;
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -15,14 +15,22 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, OnInit, Input, ChangeDetectionStrategy, ViewEncapsulation, OnDestroy } from '@angular/core'; import {
import { BehaviorSubject, Subject } from 'rxjs'; ChangeDetectionStrategy,
import { SiteEntry, Site } from '@alfresco/js-api'; 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 { ShareDataRow } from '../../data/share-data-row.model';
import { takeUntil } from 'rxjs/operators';
import { NodesApiService } from '../../../common/services/nodes-api.service'; import { NodesApiService } from '../../../common/services/nodes-api.service';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-library-role-column', selector: 'adf-library-role-column',
@@ -37,20 +45,20 @@ import { TranslateModule } from '@ngx-translate/core';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-library-role-column adf-datatable-content-cell' } host: { class: 'adf-library-role-column adf-datatable-content-cell' }
}) })
export class LibraryRoleColumnComponent implements OnInit, OnDestroy { export class LibraryRoleColumnComponent implements OnInit {
@Input() @Input()
context: any; context: any;
displayText$ = new BehaviorSubject<string>(''); displayText$ = new BehaviorSubject<string>('');
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor(private nodesApiService: NodesApiService) {} constructor(private nodesApiService: NodesApiService) {}
ngOnInit() { ngOnInit() {
this.updateValue(); 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; const row: ShareDataRow = this.context.row;
if (row) { if (row) {
const { entry } = row.node; const { entry } = row.node;
@@ -86,9 +94,4 @@ export class LibraryRoleColumnComponent implements OnInit, OnDestroy {
} }
} }
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -15,14 +15,14 @@
* limitations under the License. * 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 { NodesApiService } from '../../../common/services/nodes-api.service';
import { BehaviorSubject, Subject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { Site, SiteEntry } from '@alfresco/js-api'; import { Site, SiteEntry } from '@alfresco/js-api';
import { ShareDataRow } from '../../data/share-data-row.model'; import { ShareDataRow } from '../../data/share-data-row.model';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-library-status-column', 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' } host: { class: 'adf-library-status-column adf-datatable-content-cell' }
}) })
export class LibraryStatusColumnComponent implements OnInit, OnDestroy { export class LibraryStatusColumnComponent implements OnInit {
@Input() @Input()
context: any; context: any;
displayText$ = new BehaviorSubject<string>(''); displayText$ = new BehaviorSubject<string>('');
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor(private nodesApiService: NodesApiService) {} constructor(private nodesApiService: NodesApiService) {}
ngOnInit() { ngOnInit() {
this.updateValue(); 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; const row: ShareDataRow = this.context.row;
if (row) { if (row) {
const { entry } = row.node; const { entry } = row.node;
@@ -83,8 +83,4 @@ export class LibraryStatusColumnComponent implements OnInit, OnDestroy {
} }
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -15,15 +15,24 @@
* limitations under the License. * 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 { NodeEntry } from '@alfresco/js-api';
import { BehaviorSubject, Subject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { NodesApiService } from '../../../common/services/nodes-api.service'; import { NodesApiService } from '../../../common/services/nodes-api.service';
import { ShareDataRow } from '../../data/share-data-row.model'; import { ShareDataRow } from '../../data/share-data-row.model';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { NodeNameTooltipPipe } from '../../../pipes/node-name-tooltip.pipe'; import { NodeNameTooltipPipe } from '../../../pipes/node-name-tooltip.pipe';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-name-column', selector: 'adf-name-column',
@@ -52,7 +61,7 @@ import { NodeNameTooltipPipe } from '../../../pipes/node-name-tooltip.pipe';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-datatable-content-cell adf-datatable-link adf-name-column' } host: { class: 'adf-datatable-content-cell adf-datatable-link adf-name-column' }
}) })
export class NameColumnComponent implements OnInit, OnDestroy { export class NameColumnComponent implements OnInit {
@Input() @Input()
context: any; context: any;
@@ -62,14 +71,14 @@ export class NameColumnComponent implements OnInit, OnDestroy {
displayText$ = new BehaviorSubject<string>(''); displayText$ = new BehaviorSubject<string>('');
node: NodeEntry; node: NodeEntry;
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor(private element: ElementRef, private nodesApiService: NodesApiService) {} constructor(private element: ElementRef, private nodesApiService: NodesApiService) {}
ngOnInit() { ngOnInit() {
this.updateValue(); 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; const row: ShareDataRow = this.context.row;
if (row) { if (row) {
const { entry } = row.node; const { entry } = row.node;
@@ -102,8 +111,4 @@ export class NameColumnComponent implements OnInit, OnDestroy {
); );
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -15,12 +15,23 @@
* limitations under the License. * 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 { ConnectionPositionPair, Overlay, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal'; import { TemplatePortal } from '@angular/cdk/portal';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y'; import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Directive({ @Directive({
selector: '[adf-pop-over]', selector: '[adf-pop-over]',
@@ -38,11 +49,11 @@ export class PopOverDirective implements OnInit, OnDestroy, AfterViewInit {
@Input() autofocusedElementSelector: string; @Input() autofocusedElementSelector: string;
private _open = false; private _open = false;
private destroy$ = new Subject();
private overlayRef!: OverlayRef; private overlayRef!: OverlayRef;
private focusTrap: ConfigurableFocusTrap; private focusTrap: ConfigurableFocusTrap;
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private element: ElementRef, private element: ElementRef,
private overlay: Overlay, private overlay: Overlay,
@@ -62,8 +73,6 @@ export class PopOverDirective implements OnInit, OnDestroy, AfterViewInit {
ngOnDestroy(): void { ngOnDestroy(): void {
this.element.nativeElement.removeEventListener('keydown', this.preventDefaultForEnter); this.element.nativeElement.removeEventListener('keydown', this.preventDefaultForEnter);
this.detachOverlay(); this.detachOverlay();
this.destroy$.next(undefined);
this.destroy$.complete();
} }
private createOverlay(): void { private createOverlay(): void {
@@ -87,7 +96,7 @@ export class PopOverDirective implements OnInit, OnDestroy, AfterViewInit {
this.overlayRef this.overlayRef
.backdropClick() .backdropClick()
.pipe(takeUntil(this.destroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => { .subscribe(() => {
this.detachOverlay(); this.detachOverlay();
}); });

View File

@@ -15,19 +15,20 @@
* limitations under the License. * 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 { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox';
import { SearchWidget } from '../../models/search-widget.interface'; import { SearchWidget } from '../../models/search-widget.interface';
import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface';
import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service';
import { SearchFilterList } from '../../models/search-filter-list.model'; import { SearchFilterList } from '../../models/search-filter-list.model';
import { TranslationService } from '@alfresco/adf-core'; import { TranslationService } from '@alfresco/adf-core';
import { ReplaySubject, Subject } from 'rxjs'; import { ReplaySubject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface SearchListOption { export interface SearchListOption {
name: string; name: string;
@@ -44,7 +45,7 @@ export interface SearchListOption {
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-search-check-list' } host: { class: 'adf-search-check-list' }
}) })
export class SearchCheckListComponent implements SearchWidget, OnInit, OnDestroy { export class SearchCheckListComponent implements SearchWidget, OnInit {
id: string; id: string;
settings?: SearchWidgetSettings; settings?: SearchWidgetSettings;
context?: SearchQueryBuilderService; context?: SearchQueryBuilderService;
@@ -56,7 +57,7 @@ export class SearchCheckListComponent implements SearchWidget, OnInit, OnDestroy
enableChangeUpdate = true; enableChangeUpdate = true;
displayValue$ = new ReplaySubject<string>(1); displayValue$ = new ReplaySubject<string>(1);
private readonly destroy$ = new Subject<void>(); private readonly destroyRef = inject(DestroyRef);
constructor(private translationService: TranslationService) { constructor(private translationService: TranslationService) {
this.options = new SearchFilterList<SearchListOption>(); this.options = new SearchFilterList<SearchListOption>();
@@ -84,7 +85,7 @@ export class SearchCheckListComponent implements SearchWidget, OnInit, OnDestroy
.asObservable() .asObservable()
.pipe( .pipe(
map((filtersQueries) => filtersQueries[this.id]), map((filtersQueries) => filtersQueries[this.id]),
takeUntil(this.destroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((filterQuery) => { .subscribe((filterQuery) => {
if (filterQuery) { if (filterQuery) {
@@ -102,11 +103,6 @@ export class SearchCheckListComponent implements SearchWidget, OnInit, OnDestroy
}); });
} }
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
clear() { clear() {
this.isActive = false; this.isActive = false;
this.clearOptions(); this.clearOptions();

View File

@@ -17,28 +17,30 @@
import { import {
Component, Component,
ViewEncapsulation, DestroyRef,
ElementRef, ElementRef,
ViewChild,
OnInit,
OnDestroy,
Input,
Output,
EventEmitter, EventEmitter,
inject,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges, SimpleChanges,
OnChanges ViewChild,
ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { ENTER } from '@angular/cdk/keycodes'; import { ENTER } from '@angular/cdk/keycodes';
import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips'; import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips';
import { EMPTY, Observable, Subject, timer } from 'rxjs'; import { EMPTY, Observable, timer } from 'rxjs';
import { debounce, startWith, takeUntil, tap } from 'rxjs/operators'; import { debounce, startWith, tap } from 'rxjs/operators';
import { AutocompleteOption } from '../../models/autocomplete-option.interface'; import { AutocompleteOption } from '../../models/autocomplete-option.interface';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-search-chip-autocomplete-input', selector: 'adf-search-chip-autocomplete-input',
@@ -48,7 +50,7 @@ import { MatIconModule } from '@angular/material/icon';
styleUrls: ['./search-chip-autocomplete-input.component.scss'], styleUrls: ['./search-chip-autocomplete-input.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class SearchChipAutocompleteInputComponent implements OnInit, OnDestroy, OnChanges { export class SearchChipAutocompleteInputComponent implements OnInit, OnChanges {
@ViewChild('optionInput') @ViewChild('optionInput')
optionInput: ElementRef<HTMLInputElement>; optionInput: ElementRef<HTMLInputElement>;
@@ -89,9 +91,11 @@ export class SearchChipAutocompleteInputComponent implements OnInit, OnDestroy,
formCtrl = new FormControl(''); formCtrl = new FormControl('');
filteredOptions: AutocompleteOption[] = []; filteredOptions: AutocompleteOption[] = [];
selectedOptions: AutocompleteOption[] = []; selectedOptions: AutocompleteOption[] = [];
private onDestroy$ = new Subject<void>();
private _activeAnyOption = false; private _activeAnyOption = false;
private readonly destroyRef = inject(DestroyRef);
set activeAnyOption(active: boolean) { set activeAnyOption(active: boolean) {
this._activeAnyOption = active; this._activeAnyOption = active;
} }
@@ -102,13 +106,13 @@ export class SearchChipAutocompleteInputComponent implements OnInit, OnDestroy,
startWith(''), startWith(''),
tap(() => (this.activeAnyOption = false)), tap(() => (this.activeAnyOption = false)),
debounce((value: string) => (value ? timer(300) : EMPTY)), debounce((value: string) => (value ? timer(300) : EMPTY)),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((value: string) => { .subscribe((value: string) => {
this.filteredOptions = value ? this.filter(this.autocompleteOptions, value) : []; this.filteredOptions = value ? this.filter(this.autocompleteOptions, value) : [];
this.inputChanged.emit(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 ?? []; this.selectedOptions = this.preselectedOptions ?? [];
} }
@@ -121,11 +125,6 @@ export class SearchChipAutocompleteInputComponent implements OnInit, OnDestroy,
} }
} }
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
}
add(event: MatChipInputEvent) { add(event: MatChipInputEvent) {
if (!this._activeAnyOption) { if (!this._activeAnyOption) {
let value = (event.value || '').trim(); let value = (event.value || '').trim();

View File

@@ -15,19 +15,18 @@
* limitations under the License. * limitations under the License.
*/ */
import { AuthenticationService, ThumbnailService, SearchTextInputComponent, HighlightPipe } from '@alfresco/adf-core'; import { AuthenticationService, HighlightPipe, SearchTextInputComponent, ThumbnailService } from '@alfresco/adf-core';
import { import {
Component, Component,
ContentChild,
EventEmitter, EventEmitter,
Input, Input,
OnDestroy,
Output, Output,
QueryList, QueryList,
ViewEncapsulation, TemplateRef,
ViewChild, ViewChild,
ViewChildren, ViewChildren,
TemplateRef, ViewEncapsulation
ContentChild
} from '@angular/core'; } from '@angular/core';
import { NodeEntry } from '@alfresco/js-api'; import { NodeEntry } from '@alfresco/js-api';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@@ -47,7 +46,7 @@ import { TranslateModule } from '@ngx-translate/core';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-search-control' } host: { class: 'adf-search-control' }
}) })
export class SearchControlComponent implements OnDestroy { export class SearchControlComponent {
/** Toggles highlighting of the search term in the results. */ /** Toggles highlighting of the search term in the results. */
@Input() @Input()
highlight: boolean = false; highlight: boolean = false;
@@ -111,19 +110,11 @@ export class SearchControlComponent implements OnDestroy {
noSearchResultTemplate: TemplateRef<any> = null; noSearchResultTemplate: TemplateRef<any> = null;
searchTerm: string = ''; searchTerm: string = '';
private onDestroy$ = new Subject<boolean>();
constructor(public authService: AuthenticationService, private thumbnailService: ThumbnailService) {} constructor(public authService: AuthenticationService, private thumbnailService: ThumbnailService) {}
isNoSearchTemplatePresent(): boolean { isNoSearchTemplatePresent(): boolean {
return !!this.emptySearchTemplate; return !!this.emptySearchTemplate;
} }
ngOnDestroy(): void {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
isLoggedIn(): boolean { isLoggedIn(): boolean {
return this.authService.isEcmLoggedIn(); return this.authService.isEcmLoggedIn();
} }

View File

@@ -15,9 +15,9 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core';
import { ReplaySubject, Subject } from 'rxjs'; import { ReplaySubject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { DateRangeType } from './search-date-range/date-range-type'; import { DateRangeType } from './search-date-range/date-range-type';
import { SearchDateRange } from './search-date-range/search-date-range'; import { SearchDateRange } from './search-date-range/search-date-range';
import { SearchWidget } from '../../models/search-widget.interface'; 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 { SearchFilterTabbedComponent } from '../search-filter-tabbed/search-filter-tabbed.component';
import { SearchDateRangeComponent } from './search-date-range/search-date-range.component'; import { SearchDateRangeComponent } from './search-date-range/search-date-range.component';
import { SearchFilterTabDirective } from '../search-filter-tabbed/search-filter-tab.directive'; 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'; 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'], styleUrls: ['./search-date-range-tabbed.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit, OnDestroy { export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit {
displayValue$ = new ReplaySubject<string>(1); displayValue$ = new ReplaySubject<string>(1);
id: string; id: string;
startValue: SearchDateRange = { startValue: SearchDateRange = {
@@ -62,7 +63,8 @@ export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit, OnD
private value: { [key: string]: Partial<SearchDateRange> } = {}; private value: { [key: string]: Partial<SearchDateRange> } = {};
private queryMapByField: Map<string, string> = new Map<string, string>(); private queryMapByField: Map<string, string> = new Map<string, string>();
private displayValueMapByField: Map<string, string> = new Map<string, string>(); private displayValueMapByField: Map<string, string> = new Map<string, string>();
private readonly destroy$ = new Subject<void>();
private readonly destroyRef = inject(DestroyRef);
constructor(private translateService: TranslationService) {} constructor(private translateService: TranslationService) {}
@@ -73,7 +75,7 @@ export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit, OnD
.asObservable() .asObservable()
.pipe( .pipe(
map((filtersQueries) => filtersQueries[this.id]), map((filtersQueries) => filtersQueries[this.id]),
takeUntil(this.destroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((filterQuery) => { .subscribe((filterQuery) => {
if (filterQuery) { if (filterQuery) {
@@ -94,12 +96,6 @@ export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit, OnD
this.context.filterLoaded.next(); this.context.filterLoaded.next();
}); });
} }
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
private setDefaultDateFormatSettings() { private setDefaultDateFormatSettings() {
if (this.settings && !this.settings.dateFormat) { if (this.settings && !this.settings.dateFormat) {
this.settings.dateFormat = DEFAULT_DATE_DISPLAY_FORMAT; this.settings.dateFormat = DEFAULT_DATE_DISPLAY_FORMAT;

View File

@@ -15,17 +15,15 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, EventEmitter, inject, Inject, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { Subject } from 'rxjs'; import { endOfDay, isAfter, isBefore, isValid, parse } from 'date-fns';
import { endOfDay, parse, isValid, isBefore, isAfter } from 'date-fns';
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE, MatDateFormats } from '@angular/material/core'; 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 { DateFnsAdapter, MAT_DATE_FNS_FORMATS } from '@angular/material-date-fns-adapter';
import { InLastDateType } from './in-last-date-type'; import { InLastDateType } from './in-last-date-type';
import { DateRangeType } from './date-range-type'; import { DateRangeType } from './date-range-type';
import { SearchDateRange } from './search-date-range'; import { SearchDateRange } from './search-date-range';
import { FormBuilder, ReactiveFormsModule, UntypedFormControl, Validators } from '@angular/forms'; import { FormBuilder, ReactiveFormsModule, UntypedFormControl, Validators } from '@angular/forms';
import { takeUntil } from 'rxjs/operators'; import { DateFnsUtils, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core';
import { UserPreferencesService, UserPreferenceValues, DateFnsUtils } from '@alfresco/adf-core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatRadioModule } from '@angular/material/radio'; import { MatRadioModule } from '@angular/material/radio';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
@@ -33,6 +31,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
const DEFAULT_DATE_DISPLAY_FORMAT = 'dd-MMM-yy'; const DEFAULT_DATE_DISPLAY_FORMAT = 'dd-MMM-yy';
@@ -58,7 +57,7 @@ const DEFAULT_DATE_DISPLAY_FORMAT = 'dd-MMM-yy';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-search-date-range' } host: { class: 'adf-search-date-range' }
}) })
export class SearchDateRangeComponent implements OnInit, OnDestroy { export class SearchDateRangeComponent implements OnInit {
@Input() @Input()
dateFormat = DEFAULT_DATE_DISPLAY_FORMAT; dateFormat = DEFAULT_DATE_DISPLAY_FORMAT;
@Input() @Input()
@@ -87,11 +86,12 @@ export class SearchDateRangeComponent implements OnInit, OnDestroy {
betweenStartDateFormControl = this.form.controls.betweenStartDate; betweenStartDateFormControl = this.form.controls.betweenStartDate;
betweenEndDateFormControl = this.form.controls.betweenEndDate; betweenEndDateFormControl = this.form.controls.betweenEndDate;
convertedMaxDate: Date; convertedMaxDate: Date;
private readonly destroy$ = new Subject<void>();
readonly DateRangeType = DateRangeType; readonly DateRangeType = DateRangeType;
readonly InLastDateType = InLastDateType; readonly InLastDateType = InLastDateType;
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private userPreferencesService: UserPreferencesService, 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.convertedMaxDate = endOfDay(this.maxDate && this.maxDate !== 'today' ? parse(this.maxDate, this.dateFormat, new Date()) : new Date());
this.userPreferencesService this.userPreferencesService
.select(UserPreferenceValues.Locale) .select(UserPreferenceValues.Locale)
.pipe(takeUntil(this.destroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((locale) => this.dateAdapter.setLocale(DateFnsUtils.getLocaleFromString(locale))); .subscribe((locale) => this.dateAdapter.setLocale(DateFnsUtils.getLocaleFromString(locale)));
this.form.controls.dateRangeType.valueChanges this.form.controls.dateRangeType.valueChanges
.pipe(takeUntil(this.destroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((dateRangeType) => this.updateValidators(dateRangeType)); .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) { private updateValidators(dateRangeType: DateRangeType) {
switch (dateRangeType) { switch (dateRangeType) {
case DateRangeType.BETWEEN: case DateRangeType.BETWEEN:

View File

@@ -15,22 +15,34 @@
* limitations under the License. * 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 { 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 { SearchWidget } from '../../models/search-widget.interface';
import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface';
import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service';
import { LiveErrorStateMatcher } from '../../forms/live-error-state-matcher'; import { LiveErrorStateMatcher } from '../../forms/live-error-state-matcher';
import { ReplaySubject, Subject } from 'rxjs'; import { ReplaySubject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { DatetimeAdapter, MAT_DATETIME_FORMATS, MatDatetimepickerInputEvent, MatDatetimepickerModule } from '@mat-datetimepicker/core'; import {
DatetimeAdapter,
MAT_DATETIME_FORMATS,
MatDatetimepickerInputEvent,
MatDatetimepickerModule
} from '@mat-datetimepicker/core';
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/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 { CommonModule } from '@angular/common';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface DatetimeRangeValue { export interface DatetimeRangeValue {
from: string; from: string;
@@ -59,7 +71,7 @@ export const DEFAULT_DATETIME_FORMAT: string = 'dd/MM/yyyy HH:mm';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-search-date-range' } host: { class: 'adf-search-date-range' }
}) })
export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDestroy { export class SearchDatetimeRangeComponent implements SearchWidget, OnInit {
from: FormControl<Date>; from: FormControl<Date>;
to: FormControl<Date>; to: FormControl<Date>;
@@ -77,7 +89,7 @@ export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDes
enableChangeUpdate: boolean; enableChangeUpdate: boolean;
displayValue$ = new ReplaySubject<string>(1); displayValue$ = new ReplaySubject<string>(1);
private readonly destroy$ = new Subject<void>(); private readonly destroyRef = inject(DestroyRef);
constructor(private dateAdapter: DateAdapter<Date>, private dateTimeAdapter: DatetimeAdapter<Date>) {} constructor(private dateAdapter: DateAdapter<Date>, private dateTimeAdapter: DatetimeAdapter<Date>) {}
@@ -140,7 +152,7 @@ export class SearchDatetimeRangeComponent implements SearchWidget, OnInit, OnDes
.asObservable() .asObservable()
.pipe( .pipe(
map((filtersQueries) => filtersQueries[this.id]), map((filtersQueries) => filtersQueries[this.id]),
takeUntil(this.destroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((filterQuery) => { .subscribe((filterQuery) => {
if (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) { apply(model: Partial<{ from: Date; to: Date }>, isValidValue: boolean, updateContext = true) {
if (isValidValue && this.id && this.context && this.settings && this.settings.field) { if (isValidValue && this.id && this.context && this.settings && this.settings.field) {
this.isActive = true; this.isActive = true;

View File

@@ -15,9 +15,9 @@
* limitations under the License. * 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 { 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 { SearchWidget } from '../../models/search-widget.interface';
import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface';
import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; 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 { SearchChipAutocompleteInputComponent } from '../search-chip-autocomplete-input';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-search-filter-autocomplete-chips', selector: 'adf-search-filter-autocomplete-chips',
@@ -37,7 +38,7 @@ import { MatButtonModule } from '@angular/material/button';
templateUrl: './search-filter-autocomplete-chips.component.html', templateUrl: './search-filter-autocomplete-chips.component.html',
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnInit, OnDestroy { export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnInit {
id: string; id: string;
settings?: SearchWidgetSettings; settings?: SearchWidgetSettings;
context?: SearchQueryBuilderService; context?: SearchQueryBuilderService;
@@ -51,7 +52,8 @@ export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnI
reset$: Observable<void> = this.resetSubject$.asObservable(); reset$: Observable<void> = this.resetSubject$.asObservable();
private autocompleteOptionsSubject$ = new BehaviorSubject<AutocompleteOption[]>([]); private autocompleteOptionsSubject$ = new BehaviorSubject<AutocompleteOption[]>([]);
autocompleteOptions$: Observable<AutocompleteOption[]> = this.autocompleteOptionsSubject$.asObservable(); autocompleteOptions$: Observable<AutocompleteOption[]> = this.autocompleteOptionsSubject$.asObservable();
private readonly destroy$ = new Subject<void>();
private readonly destroyRef = inject(DestroyRef);
constructor(private tagService: TagService, private categoryService: CategoryService) { constructor(private tagService: TagService, private categoryService: CategoryService) {
this.options = new SearchFilterList<AutocompleteOption[]>(); this.options = new SearchFilterList<AutocompleteOption[]>();
@@ -69,7 +71,7 @@ export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnI
.asObservable() .asObservable()
.pipe( .pipe(
map((filterQueries) => filterQueries[this.id]), map((filterQueries) => filterQueries[this.id]),
takeUntil(this.destroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((filterQuery) => { .subscribe((filterQuery) => {
if (filterQuery) { if (filterQuery) {
@@ -82,11 +84,6 @@ export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnI
}); });
} }
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
reset(updateContext = true) { reset(updateContext = true) {
this.selectedOptions = []; this.selectedOptions = [];
this.context.filterRawParams[this.id] = undefined; this.context.filterRawParams[this.id] = undefined;

View File

@@ -15,19 +15,19 @@
* limitations under the License. * 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 { Observable, Subject } from 'rxjs';
import { SearchQueryBuilderService } from '../../../services/search-query-builder.service'; import { SearchQueryBuilderService } from '../../../services/search-query-builder.service';
import { FacetWidget } from '../../../models/facet-widget.interface'; import { FacetWidget } from '../../../models/facet-widget.interface';
import { TranslationService } from '@alfresco/adf-core'; import { TranslationService } from '@alfresco/adf-core';
import { AutocompleteOption } from '../../../models/autocomplete-option.interface'; import { AutocompleteOption } from '../../../models/autocomplete-option.interface';
import { takeUntil } from 'rxjs/operators';
import { TabbedFacetField } from '../../../models/tabbed-facet-field.interface'; import { TabbedFacetField } from '../../../models/tabbed-facet-field.interface';
import { SearchFacetFiltersService } from '../../../services/search-facet-filters.service'; import { SearchFacetFiltersService } from '../../../services/search-facet-filters.service';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { SearchChipAutocompleteInputComponent } from '../../search-chip-autocomplete-input'; import { SearchChipAutocompleteInputComponent } from '../../search-chip-autocomplete-input';
import { SearchFilterTabbedComponent } from '../../search-filter-tabbed/search-filter-tabbed.component'; import { SearchFilterTabbedComponent } from '../../search-filter-tabbed/search-filter-tabbed.component';
import { SearchFilterTabDirective } from '../../search-filter-tabbed'; import { SearchFilterTabDirective } from '../../search-filter-tabbed';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-search-facet-tabbed-content', selector: 'adf-search-facet-tabbed-content',
@@ -36,7 +36,7 @@ import { SearchFilterTabDirective } from '../../search-filter-tabbed';
templateUrl: './search-facet-tabbed-content.component.html', templateUrl: './search-facet-tabbed-content.component.html',
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class SearchFacetTabbedContentComponent implements OnInit, OnDestroy, OnChanges, FacetWidget { export class SearchFacetTabbedContentComponent implements OnInit, OnChanges, FacetWidget {
private queryBuilder = inject(SearchQueryBuilderService); private queryBuilder = inject(SearchQueryBuilderService);
private translationService = inject(TranslationService); private translationService = inject(TranslationService);
private searchFacetFiltersService = inject(SearchFacetFiltersService); private searchFacetFiltersService = inject(SearchFacetFiltersService);
@@ -57,13 +57,14 @@ export class SearchFacetTabbedContentComponent implements OnInit, OnDestroy, OnC
displayValue$ = new EventEmitter<string>(); displayValue$ = new EventEmitter<string>();
private resetSubject$ = new Subject<void>(); private resetSubject$ = new Subject<void>();
private onDestroy$ = new Subject<void>();
reset$ = this.resetSubject$.asObservable(); reset$ = this.resetSubject$.asObservable();
chipIcon = 'keyboard_arrow_down'; chipIcon = 'keyboard_arrow_down';
autocompleteOptions = {}; autocompleteOptions = {};
selectedOptions = {}; selectedOptions = {};
private readonly destroyRef = inject(DestroyRef);
ngOnInit() { ngOnInit() {
this.tabbedFacet.fields.forEach((field) => { this.tabbedFacet.fields.forEach((field) => {
Object.defineProperty(this.selectedOptions, 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.onReset$?.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.reset());
this.onApply$?.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.submitValues()); this.onApply$?.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.submitValues());
}
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {

View File

@@ -15,11 +15,9 @@
* limitations under the License. * 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 { SearchFacetFiltersService } from '../../services/search-facet-filters.service';
import { SearchQueryBuilderService } from '../../services/search-query-builder.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 { FacetField, SearchCategory, TabbedFacetField } from '../../models';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatChipsModule } from '@angular/material/chips'; 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 { SearchFacetChipTabbedComponent } from './search-facet-chip-tabbed/search-facet-chip-tabbed.component';
import { SearchFacetChipComponent } from './search-facet-chip/search-facet-chip.component'; import { SearchFacetChipComponent } from './search-facet-chip/search-facet-chip.component';
import { SearchWidgetChipComponent } from './search-widget-chip/search-widget-chip.component'; import { SearchWidgetChipComponent } from './search-widget-chip/search-widget-chip.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-search-filter-chips', selector: 'adf-search-filter-chips',
@@ -36,11 +35,11 @@ import { SearchWidgetChipComponent } from './search-widget-chip/search-widget-ch
styleUrls: ['./search-filter-chips.component.scss'], styleUrls: ['./search-filter-chips.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class SearchFilterChipsComponent implements OnInit, OnDestroy { export class SearchFilterChipsComponent implements OnInit {
private queryBuilder = inject(SearchQueryBuilderService); private queryBuilder = inject(SearchQueryBuilderService);
private facetFiltersService = inject(SearchFacetFiltersService); private facetFiltersService = inject(SearchFacetFiltersService);
private onDestroy$ = new Subject<void>(); private readonly destroyRef = inject(DestroyRef);
/** Toggles whether to show or not the context facet filters. */ /** Toggles whether to show or not the context facet filters. */
@Input() @Input()
@@ -63,12 +62,7 @@ export class SearchFilterChipsComponent implements OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.queryBuilder.executed this.queryBuilder.executed
.asObservable() .asObservable()
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => (this.facetChipTabbedId = 'search-fact-chip-tabbed-' + this.facetFiltersService.tabbedFacet?.fields.join('-'))); .subscribe(() => (this.facetChipTabbedId = 'search-fact-chip-tabbed-' + this.facetFiltersService.tabbedFacet?.fields.join('-')));
} }
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
}
} }

View File

@@ -15,13 +15,21 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, Input, Output, OnInit, EventEmitter, ViewEncapsulation, ViewChild, OnDestroy, ElementRef } from '@angular/core'; import {
import { ConfigurableFocusTrapFactory, ConfigurableFocusTrap } from '@angular/cdk/a11y'; 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 { DataColumn, IconComponent, TranslationService } from '@alfresco/adf-core';
import { SearchWidgetContainerComponent } from '../search-widget-container/search-widget-container.component'; import { SearchWidgetContainerComponent } from '../search-widget-container/search-widget-container.component';
import { SearchHeaderQueryBuilderService } from '../../services/search-header-query-builder.service'; import { SearchHeaderQueryBuilderService } from '../../services/search-header-query-builder.service';
import { SearchCategory } from '../../models/search-category.interface'; import { SearchCategory } from '../../models/search-category.interface';
import { Subject } from 'rxjs';
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu'; import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
import { FilterSearch } from '../../models/filter-search.interface'; import { FilterSearch } from '../../models/filter-search.interface';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
@@ -47,7 +55,7 @@ import { MatDialogModule } from '@angular/material/dialog';
styleUrls: ['./search-filter-container.component.scss'], styleUrls: ['./search-filter-container.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class SearchFilterContainerComponent implements OnInit, OnDestroy { export class SearchFilterContainerComponent implements OnInit {
/** The column the filter will be applied on. */ /** The column the filter will be applied on. */
@Input() @Input()
col: DataColumn; col: DataColumn;
@@ -70,8 +78,6 @@ export class SearchFilterContainerComponent implements OnInit, OnDestroy {
focusTrap: ConfigurableFocusTrap; focusTrap: ConfigurableFocusTrap;
initialValue: any; initialValue: any;
private onDestroy$ = new Subject<boolean>();
constructor( constructor(
private searchFilterQueryBuilder: SearchHeaderQueryBuilderService, private searchFilterQueryBuilder: SearchHeaderQueryBuilderService,
private translationService: TranslationService, 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; 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) { onKeyPressed(event: KeyboardEvent, menuTrigger: MatMenuTrigger) {
if (event.key === 'Enter' && this.widgetContainer.selector !== 'check-list') { if (event.key === 'Enter' && this.widgetContainer.selector !== 'check-list') {
this.onApply(); this.onApply();

View File

@@ -15,17 +15,18 @@
* limitations under the License. * 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 { SearchWidget } from '../../models/search-widget.interface';
import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface';
import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service';
import { ReplaySubject, Subject } from 'rxjs'; import { ReplaySubject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { TranslationService } from '@alfresco/adf-core'; import { TranslationService } from '@alfresco/adf-core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export enum LogicalSearchFields { export enum LogicalSearchFields {
MATCH_ALL = 'matchAll', MATCH_ALL = 'matchAll',
@@ -46,7 +47,7 @@ export interface LogicalSearchCondition extends LogicalSearchConditionEnumValued
styleUrls: ['./search-logical-filter.component.scss'], styleUrls: ['./search-logical-filter.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class SearchLogicalFilterComponent implements SearchWidget, OnInit, OnDestroy { export class SearchLogicalFilterComponent implements SearchWidget, OnInit {
id: string; id: string;
settings?: SearchWidgetSettings; settings?: SearchWidgetSettings;
context?: SearchQueryBuilderService; context?: SearchQueryBuilderService;
@@ -56,7 +57,7 @@ export class SearchLogicalFilterComponent implements SearchWidget, OnInit, OnDes
LogicalSearchFields = LogicalSearchFields; LogicalSearchFields = LogicalSearchFields;
displayValue$ = new ReplaySubject<string>(1); displayValue$ = new ReplaySubject<string>(1);
private readonly destroy$ = new Subject<void>(); private readonly destroyRef = inject(DestroyRef);
constructor(private translationService: TranslationService) {} constructor(private translationService: TranslationService) {}
@@ -66,7 +67,7 @@ export class SearchLogicalFilterComponent implements SearchWidget, OnInit, OnDes
.asObservable() .asObservable()
.pipe( .pipe(
map((filtersQueries) => filtersQueries[this.id]), map((filtersQueries) => filtersQueries[this.id]),
takeUntil(this.destroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((filterQuery) => { .subscribe((filterQuery) => {
if (filterQuery) { if (filterQuery) {
@@ -79,11 +80,6 @@ export class SearchLogicalFilterComponent implements SearchWidget, OnInit, OnDes
}); });
} }
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
submitValues(updateContext = true) { submitValues(updateContext = true) {
if (this.hasValidValue() && this.id && this.context && this.settings && this.settings.field) { if (this.hasValidValue() && this.id && this.context && this.settings && this.settings.field) {
this.updateDisplayValue(); this.updateDisplayValue();

View File

@@ -15,7 +15,7 @@
* limitations under the License. * 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 { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { FileSizeCondition } from './file-size-condition'; import { FileSizeCondition } from './file-size-condition';
import { FileSizeOperator } from './file-size-operator.enum'; import { FileSizeOperator } from './file-size-operator.enum';
@@ -31,7 +31,8 @@ import { CommonModule } from '@angular/common';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { SearchChipAutocompleteInputComponent } from '../search-chip-autocomplete-input'; 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({ @Component({
selector: 'adf-search-properties', selector: 'adf-search-properties',
@@ -41,7 +42,7 @@ import { map, takeUntil } from 'rxjs/operators';
styleUrls: ['./search-properties.component.scss'], styleUrls: ['./search-properties.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class SearchPropertiesComponent implements OnInit, AfterViewChecked, OnDestroy, SearchWidget { export class SearchPropertiesComponent implements OnInit, AfterViewChecked, SearchWidget {
id: string; id: string;
settings?: SearchWidgetSettings; settings?: SearchWidgetSettings;
context?: SearchQueryBuilderService; context?: SearchQueryBuilderService;
@@ -95,7 +96,7 @@ export class SearchPropertiesComponent implements OnInit, AfterViewChecked, OnDe
this._selectedExtensions = this.parseFromAutocompleteOptions(extensions); this._selectedExtensions = this.parseFromAutocompleteOptions(extensions);
} }
private readonly destroy$ = new Subject<void>(); private readonly destroyRef = inject(DestroyRef);
constructor(private formBuilder: FormBuilder, private translateService: TranslateService) {} constructor(private formBuilder: FormBuilder, private translateService: TranslateService) {}
@@ -114,7 +115,7 @@ export class SearchPropertiesComponent implements OnInit, AfterViewChecked, OnDe
.asObservable() .asObservable()
.pipe( .pipe(
map((filtersQueries) => filtersQueries[this.id]), map((filtersQueries) => filtersQueries[this.id]),
takeUntil(this.destroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((filterQuery) => { .subscribe((filterQuery) => {
if (filterQuery) { if (filterQuery) {
@@ -149,11 +150,6 @@ export class SearchPropertiesComponent implements OnInit, AfterViewChecked, OnDe
} }
} }
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
narrowDownAllowedCharacters(event: Event) { narrowDownAllowedCharacters(event: Event) {
const value = (event.target as HTMLInputElement).value; const value = (event.target as HTMLInputElement).value;
if (!(event.target as HTMLInputElement).value) { if (!(event.target as HTMLInputElement).value) {

View File

@@ -15,17 +15,17 @@
* limitations under the License. * 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 { SearchWidget } from '../../models/search-widget.interface';
import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface';
import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service';
import { ReplaySubject, Subject } from 'rxjs'; import { ReplaySubject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatSliderModule } from '@angular/material/slider'; import { MatSliderModule } from '@angular/material/slider';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-search-slider', selector: 'adf-search-slider',
@@ -36,7 +36,7 @@ import { TranslateModule } from '@ngx-translate/core';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-search-slider' } 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. */ /** The numeric value represented by the slider. */
@Input() @Input()
value: number | null; value: number | null;
@@ -54,7 +54,7 @@ export class SearchSliderComponent implements SearchWidget, OnInit, OnDestroy {
enableChangeUpdate: boolean; enableChangeUpdate: boolean;
displayValue$ = new ReplaySubject<string>(1); displayValue$ = new ReplaySubject<string>(1);
private readonly destroy$ = new Subject<void>(); private readonly destroyRef = inject(DestroyRef);
ngOnInit() { ngOnInit() {
if (this.settings) { if (this.settings) {
@@ -79,7 +79,7 @@ export class SearchSliderComponent implements SearchWidget, OnInit, OnDestroy {
} }
this.context.populateFilters this.context.populateFilters
.asObservable() .asObservable()
.pipe(takeUntil(this.destroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((filtersQueries) => { .subscribe((filtersQueries) => {
if (filtersQueries[this.id]) { if (filtersQueries[this.id]) {
this.value = 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() { clear() {
this.value = this.min || 0; this.value = this.min || 0;
if (this.enableChangeUpdate) { if (this.enableChangeUpdate) {

View File

@@ -15,12 +15,12 @@
* limitations under the License. * 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 { SearchWidget } from '../../models/search-widget.interface';
import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface';
import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service';
import { ReplaySubject, Subject } from 'rxjs'; import { ReplaySubject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
@@ -28,6 +28,7 @@ import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-search-text', selector: 'adf-search-text',
@@ -38,7 +39,7 @@ import { MatIconModule } from '@angular/material/icon';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-search-text' } host: { class: 'adf-search-text' }
}) })
export class SearchTextComponent implements SearchWidget, OnInit, OnDestroy { export class SearchTextComponent implements SearchWidget, OnInit {
/** The content of the text box. */ /** The content of the text box. */
@Input() @Input()
value = ''; value = '';
@@ -51,7 +52,7 @@ export class SearchTextComponent implements SearchWidget, OnInit, OnDestroy {
enableChangeUpdate = true; enableChangeUpdate = true;
displayValue$ = new ReplaySubject<string>(1); displayValue$ = new ReplaySubject<string>(1);
private readonly destroy$ = new Subject<void>(); private readonly destroyRef = inject(DestroyRef);
ngOnInit() { ngOnInit() {
if (this.context && this.settings && this.settings.pattern) { if (this.context && this.settings && this.settings.pattern) {
@@ -77,7 +78,7 @@ export class SearchTextComponent implements SearchWidget, OnInit, OnDestroy {
.asObservable() .asObservable()
.pipe( .pipe(
map((filtersQueries) => filtersQueries[this.id]), map((filtersQueries) => filtersQueries[this.id]),
takeUntil(this.destroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((filterQuery) => { .subscribe((filterQuery) => {
if (filterQuery) { if (filterQuery) {
@@ -89,12 +90,6 @@ export class SearchTextComponent implements SearchWidget, OnInit, OnDestroy {
this.context.filterLoaded.next(); this.context.filterLoaded.next();
}); });
} }
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
clear() { clear() {
this.isActive = false; this.isActive = false;
this.value = ''; this.value = '';

View File

@@ -25,18 +25,18 @@ import {
Input, Input,
OnChanges, OnChanges,
Output, Output,
SimpleChanges,
TemplateRef, TemplateRef,
ViewChild, ViewChild,
ViewEncapsulation, ViewEncapsulation
OnDestroy,
SimpleChanges
} from '@angular/core'; } from '@angular/core';
import { NodePaging, ResultSetPaging } from '@alfresco/js-api'; import { NodePaging, ResultSetPaging } from '@alfresco/js-api';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { debounceTime, takeUntil } from 'rxjs/operators'; import { debounceTime } from 'rxjs/operators';
import { SearchComponentInterface } from '@alfresco/adf-core'; import { SearchComponentInterface } from '@alfresco/adf-core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-search', selector: 'adf-search',
@@ -49,7 +49,7 @@ import { TranslateModule } from '@ngx-translate/core';
exportAs: 'searchAutocomplete', exportAs: 'searchAutocomplete',
host: { class: 'adf-search' } host: { class: 'adf-search' }
}) })
export class SearchComponent implements SearchComponentInterface, AfterContentInit, OnChanges, OnDestroy { export class SearchComponent implements SearchComponentInterface, AfterContentInit, OnChanges {
@ViewChild('panel', { static: true }) @ViewChild('panel', { static: true })
panel: ElementRef; panel: ElementRef;
@@ -107,14 +107,12 @@ export class SearchComponent implements SearchComponentInterface, AfterContentIn
_isOpen: boolean = false; _isOpen: boolean = false;
keyPressedStream = new Subject<string>(); keyPressedStream = new Subject<string>();
_classList: { [key: string]: boolean } = {}; _classList: { [key: string]: boolean } = {};
private onDestroy$ = new Subject<boolean>();
constructor(private searchService: SearchService, private _elementRef: ElementRef) { 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); this.loadSearchResults(searchedWord);
}); });
searchService.dataLoaded.pipe(takeUntil(this.onDestroy$)).subscribe( searchService.dataLoaded.pipe(takeUntilDestroyed()).subscribe(
(nodePaging) => this.onSearchDataLoaded(nodePaging), (nodePaging) => this.onSearchDataLoaded(nodePaging),
(error) => this.onSearchDataError(error) (error) => this.onSearchDataError(error)
); );
@@ -129,12 +127,6 @@ export class SearchComponent implements SearchComponentInterface, AfterContentIn
this.loadSearchResults(changes.searchTerm.currentValue); this.loadSearchResults(changes.searchTerm.currentValue);
} }
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
resetResults() { resetResults() {
this.cleanResults(); this.cleanResults();
this.setVisibility(); this.setVisibility();

View File

@@ -15,18 +15,19 @@
* limitations under the License. * 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 { 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 { SearchQueryBuilderService } from './search-query-builder.service';
import { TranslationService } from '@alfresco/adf-core'; import { TranslationService } from '@alfresco/adf-core';
import { SearchService } from './search.service'; 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 { GenericBucket, GenericFacetResponse, ResultSetContext, ResultSetPaging } from '@alfresco/js-api';
import { SearchFilterList } from '../models/search-filter-list.model'; import { SearchFilterList } from '../models/search-filter-list.model';
import { FacetFieldBucket } from '../models/facet-field-bucket.interface'; import { FacetFieldBucket } from '../models/facet-field-bucket.interface';
import { CategoryService } from '../../category/services/category.service'; import { CategoryService } from '../../category/services/category.service';
import { TabbedFacetField } from '../models/tabbed-facet-field.interface'; import { TabbedFacetField } from '../models/tabbed-facet-field.interface';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface SelectedBucket { export interface SelectedBucket {
field: FacetField; field: FacetField;
@@ -38,7 +39,7 @@ const DEFAULT_PAGE_SIZE: number = 5;
@Injectable({ @Injectable({
providedIn: 'root' 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. * 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 * 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[] = []; selectedBuckets: SelectedBucket[] = [];
private readonly facetQueriesPageSize = DEFAULT_PAGE_SIZE; private readonly facetQueriesPageSize = DEFAULT_PAGE_SIZE;
private readonly onDestroy$ = new Subject<boolean>();
constructor( constructor(
private queryBuilder: SearchQueryBuilderService, private queryBuilder: SearchQueryBuilderService,
private searchService: SearchService, private searchService: SearchService,
@@ -64,14 +63,14 @@ export class SearchFacetFiltersService implements OnDestroy {
this.facetQueriesPageSize = queryBuilder.config.facetQueries.pageSize || DEFAULT_PAGE_SIZE; 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.selectedBuckets = [];
this.responseFacets = null; 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.onDataLoaded(resultSetPaging);
this.searchService.dataLoaded.next(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() { resetAllSelectedBuckets() {
this.responseFacets.forEach((facetField) => { this.responseFacets.forEach((facetField) => {
if (facetField?.buckets) { if (facetField?.buckets) {

View File

@@ -16,11 +16,19 @@
*/ */
import { TranslationService } from '@alfresco/adf-core'; 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 { TagService } from '../services/tag.service';
import { Subject } from 'rxjs';
import { TagPaging } from '@alfresco/js-api'; import { TagPaging } from '@alfresco/js-api';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatListModule } from '@angular/material/list'; import { MatListModule } from '@angular/material/list';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
@@ -29,6 +37,7 @@ import { MatInputModule } from '@angular/material/input';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; 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, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-tag-node-actions-list' } 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. */ /** The identifier of a node. */
@Input() @Input()
nodeId: string; nodeId: string;
@@ -66,23 +75,18 @@ export class TagActionsComponent implements OnChanges, OnInit, OnDestroy {
errorMsg: string; errorMsg: string;
disableAddTag: boolean = true; disableAddTag: boolean = true;
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor(private tagService: TagService, private translateService: TranslationService) {} constructor(private tagService: TagService, private translateService: TranslationService) {}
ngOnInit() { ngOnInit() {
this.tagService.refresh.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.refreshTag()); this.tagService.refresh.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.refreshTag());
} }
ngOnChanges() { ngOnChanges() {
return this.refreshTag(); return this.refreshTag();
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
refreshTag() { refreshTag() {
if (this.nodeId) { if (this.nodeId) {
this.tagService.getTagsByNodeId(this.nodeId).subscribe( this.tagService.getTagsByNodeId(this.nodeId).subscribe(

View File

@@ -15,16 +15,15 @@
* limitations under the License. * 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 { TagService } from '../services/tag.service';
import { PaginationModel } from '@alfresco/adf-core'; import { PaginationModel } from '@alfresco/adf-core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { TagEntry } from '@alfresco/js-api'; import { TagEntry } from '@alfresco/js-api';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatChipsModule } from '@angular/material/chips'; import { MatChipsModule } from '@angular/material/chips';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; 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 * 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, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-tag-list' } host: { class: 'adf-tag-list' }
}) })
export class TagListComponent implements OnInit, OnDestroy { export class TagListComponent implements OnInit {
/** Emitted when a tag is selected. */ /** Emitted when a tag is selected. */
@Output() @Output()
result = new EventEmitter(); result = new EventEmitter();
@@ -59,8 +58,6 @@ export class TagListComponent implements OnInit, OnDestroy {
isLoading = false; isLoading = false;
isSizeMinimum = true; isSizeMinimum = true;
private onDestroy$ = new Subject<boolean>();
constructor(private tagService: TagService) { constructor(private tagService: TagService) {
this.defaultPagination = { this.defaultPagination = {
skipCount: 0, skipCount: 0,
@@ -70,7 +67,7 @@ export class TagListComponent implements OnInit, OnDestroy {
this.pagination = this.defaultPagination; this.pagination = this.defaultPagination;
this.tagService.refresh.pipe(takeUntil(this.onDestroy$)).subscribe(() => { this.tagService.refresh.pipe(takeUntilDestroyed()).subscribe(() => {
this.tagsEntries = []; this.tagsEntries = [];
this.refreshTag(this.defaultPagination); this.refreshTag(this.defaultPagination);
}); });
@@ -79,12 +76,6 @@ export class TagListComponent implements OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.refreshTag(this.defaultPagination); this.refreshTag(this.defaultPagination);
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
refreshTag(opts?: any) { refreshTag(opts?: any) {
this.tagService.getAllTheTags(opts).subscribe((tags) => { this.tagService.getAllTheTags(opts).subscribe((tags) => {
this.tagsEntries = this.tagsEntries.concat(tags.list.entries); this.tagsEntries = this.tagsEntries.concat(tags.list.entries);

View File

@@ -15,12 +15,11 @@
* limitations under the License. * 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 { TagService } from '../services/tag.service';
import { TagEntry } from '@alfresco/js-api'; import { TagEntry } from '@alfresco/js-api';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Chip, DynamicChipListComponent } from '@alfresco/adf-core'; 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', templateUrl: './tag-node-list.component.html',
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit { export class TagNodeListComponent implements OnChanges, OnInit {
/** The identifier of a node. */ /** The identifier of a node. */
@Input() @Input()
nodeId: string; nodeId: string;
@@ -51,13 +50,14 @@ export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit {
@Output() @Output()
results = new EventEmitter<TagEntry[]>(); results = new EventEmitter<TagEntry[]>();
private onDestroy$ = new Subject<boolean>();
private _tagChips: Chip[] = []; private _tagChips: Chip[] = [];
get tagChips(): Chip[] { get tagChips(): Chip[] {
return this._tagChips; return this._tagChips;
} }
private readonly destroyRef = inject(DestroyRef);
constructor(private tagService: TagService) {} constructor(private tagService: TagService) {}
ngOnChanges(): void { ngOnChanges(): void {
@@ -65,12 +65,7 @@ export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.tagService.refresh.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.refreshTag()); this.tagService.refresh.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.refreshTag());
}
ngOnDestroy(): void {
this.onDestroy$.next(true);
this.onDestroy$.complete();
} }
refreshTag(): void { refreshTag(): void {

View File

@@ -16,7 +16,20 @@
*/ */
import { TagEntry, TagPaging } from '@alfresco/js-api'; 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 { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { debounce, distinctUntilChanged, finalize, first, map, takeUntil, tap } from 'rxjs/operators'; import { debounce, distinctUntilChanged, finalize, first, map, takeUntil, tap } from 'rxjs/operators';
import { EMPTY, forkJoin, Observable, Subject, timer } from 'rxjs'; 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 { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list'; import { MatListModule } from '@angular/material/list';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
interface TagNameControlErrors { interface TagNameControlErrors {
duplicatedExistingTag?: boolean; duplicatedExistingTag?: boolean;
@@ -162,7 +176,6 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
private _tagNameControlVisible = false; private _tagNameControlVisible = false;
private _existingTags: TagEntry[]; private _existingTags: TagEntry[];
private _initialExistingTags: TagEntry[]; private _initialExistingTags: TagEntry[];
private onDestroy$ = new Subject<void>();
private _tagNameErrorMessageKey = ''; private _tagNameErrorMessageKey = '';
private _spinnerVisible = false; private _spinnerVisible = false;
private _typing = false; private _typing = false;
@@ -176,6 +189,8 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
@ViewChild('tagNameInput') @ViewChild('tagNameInput')
private tagNameInputElement: ElementRef; private tagNameInputElement: ElementRef;
private readonly destroyRef = inject(DestroyRef);
constructor(private tagService: TagService, private notificationService: NotificationService) {} constructor(private tagService: TagService, private notificationService: NotificationService) {}
ngOnInit(): void { ngOnInit(): void {
@@ -201,18 +216,16 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
this._existingTags = null; this._existingTags = null;
}), }),
debounce((name: string) => (name ? timer(300) : EMPTY)), debounce((name: string) => (name ? timer(300) : EMPTY)),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((name: string) => this.onTagNameControlValueChange(name)); .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(); this.setTagNameControlErrorMessageKey();
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.onDestroy$.next();
this.onDestroy$.complete();
this.cancelExistingTagsLoading$.next(); this.cancelExistingTagsLoading$.next();
this.cancelExistingTagsLoading$.complete(); this.cancelExistingTagsLoading$.complete();
} }

View File

@@ -19,14 +19,13 @@ import { FileInfo, TranslationService } from '@alfresco/adf-core';
import { FileUploadErrorEvent } from '../../../common/events/file.event'; import { FileUploadErrorEvent } from '../../../common/events/file.event';
import { FileModel } from '../../../common/models/file.model'; import { FileModel } from '../../../common/models/file.model';
import { UploadService } from '../../../common/services/upload.service'; import { UploadService } from '../../../common/services/upload.service';
import { EventEmitter, Input, Output, OnInit, OnDestroy, NgZone, Directive, inject } from '@angular/core'; import { DestroyRef, Directive, EventEmitter, inject, Input, NgZone, OnInit, Output } from '@angular/core';
import { Subject } from 'rxjs';
import { UploadFilesEvent } from '../upload-files.event'; import { UploadFilesEvent } from '../upload-files.event';
import { takeUntil } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Directive() @Directive()
// eslint-disable-next-line @angular-eslint/directive-class-suffix // 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 uploadService = inject(UploadService);
protected translationService = inject(TranslationService); protected translationService = inject(TranslationService);
protected ngZone = inject(NgZone); protected ngZone = inject(NgZone);
@@ -85,19 +84,14 @@ export abstract class UploadBase implements OnInit, OnDestroy {
@Output() @Output()
updateFileVersion = new EventEmitter<CustomEvent>(); updateFileVersion = new EventEmitter<CustomEvent>();
protected onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
ngOnInit() { ngOnInit() {
this.uploadService.fileUploadError this.uploadService.fileUploadError
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(error => this.error.emit(error)); .subscribe(error => this.error.emit(error));
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
/** /**
* Upload a list of file in the specified path * Upload a list of file in the specified path
* *

View File

@@ -19,29 +19,32 @@ import { UserPreferencesService } from '@alfresco/adf-core';
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
Input, DestroyRef,
Output, ElementRef,
EventEmitter, EventEmitter,
HostBinding,
inject,
Input,
OnDestroy, OnDestroy,
OnInit, OnInit,
Output,
ViewChild, ViewChild,
HostBinding,
ElementRef,
ViewEncapsulation ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { Subscription, merge, Subject } from 'rxjs'; import { merge, Subject } from 'rxjs';
import { FileUploadingListComponent } from './file-uploading-list.component'; import { FileUploadingListComponent } from './file-uploading-list.component';
import { Direction } from '@angular/cdk/bidi'; 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 { UploadService } from '../../common/services/upload.service';
import { FileModel, FileUploadStatus } from '../../common/models/file.model'; 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 { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { FileUploadingListRowComponent } from './file-uploading-list-row.component'; import { FileUploadingListRowComponent } from './file-uploading-list-row.component';
import { A11yModule } from '@angular/cdk/a11y'; import { A11yModule } from '@angular/cdk/a11y';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-file-uploading-dialog', selector: 'adf-file-uploading-dialog',
@@ -54,7 +57,6 @@ import { A11yModule } from '@angular/cdk/a11y';
export class FileUploadingDialogComponent implements OnInit, OnDestroy { export class FileUploadingDialogComponent implements OnInit, OnDestroy {
/** Dialog direction. Can be 'ltr' or 'rtl. */ /** Dialog direction. Can be 'ltr' or 'rtl. */
private direction: Direction = 'ltr'; private direction: Direction = 'ltr';
private onDestroy$ = new Subject<boolean>();
@ViewChild('uploadList') @ViewChild('uploadList')
uploadList: FileUploadingListComponent; uploadList: FileUploadingListComponent;
@@ -87,12 +89,10 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
isDialogMinimized: boolean = false; isDialogMinimized: boolean = false;
isConfirmation: boolean = false; isConfirmation: boolean = false;
private listSubscription: Subscription;
private counterSubscription: Subscription;
private fileUploadSubscription: Subscription;
private errorSubscription: Subscription;
private dialogActive = new Subject<boolean>(); private dialogActive = new Subject<boolean>();
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private uploadService: UploadService, private uploadService: UploadService,
private changeDetector: ChangeDetectorRef, private changeDetector: ChangeDetectorRef,
@@ -101,14 +101,14 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
) {} ) {}
ngOnInit() { 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'); const element: any = this.elementRef.nativeElement.querySelector('#upload-dialog');
if (element) { if (element) {
element.focus(); 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; this.filesUploadingList = fileList;
if (this.filesUploadingList.length && !this.isDialogActive) { 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) merge(this.uploadService.fileUploadComplete, this.uploadService.fileUploadDeleted)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((event: FileUploadCompleteEvent | FileUploadDeleteEvent) => { .subscribe((event: FileUploadCompleteEvent | FileUploadDeleteEvent) => {
this.totalCompleted = event.totalComplete; this.totalCompleted = event.totalComplete;
this.changeDetector.detectChanges(); 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.totalErrors = event.totalError;
this.changeDetector.detectChanges(); 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.changeDetector.detectChanges();
}); });
this.uploadService.fileDeleted.pipe(takeUntil(this.onDestroy$)).subscribe((objId) => { this.uploadService.fileDeleted.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((objId) => {
if (this.filesUploadingList) { if (this.filesUploadingList) {
const uploadedFile = this.filesUploadingList.find((file) => (file.data ? file.data.entry.id === objId : false)); const uploadedFile = this.filesUploadingList.find((file) => (file.data ? file.data.entry.id === objId : false));
if (uploadedFile) { if (uploadedFile) {
@@ -147,7 +147,7 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
this.userPreferencesService this.userPreferencesService
.select('textOrientation') .select('textOrientation')
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((textOrientation: Direction) => { .subscribe((textOrientation: Direction) => {
this.direction = textOrientation; this.direction = textOrientation;
}); });
@@ -201,12 +201,6 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
ngOnDestroy() { ngOnDestroy() {
this.uploadService.clearQueue(); this.uploadService.clearQueue();
this.listSubscription.unsubscribe();
this.counterSubscription.unsubscribe();
this.fileUploadSubscription.unsubscribe();
this.errorSubscription.unsubscribe();
this.onDestroy$.next(true);
this.onDestroy$.complete();
} }
canShowDialog(): boolean { canShowDialog(): boolean {

View File

@@ -17,14 +17,25 @@
import { ConfirmDialogComponent } from '@alfresco/adf-core'; import { ConfirmDialogComponent } from '@alfresco/adf-core';
import { AlfrescoApiService } from '../services/alfresco-api.service'; import { AlfrescoApiService } from '../services/alfresco-api.service';
import { Component, Input, OnChanges, ViewEncapsulation, EventEmitter, Output, OnInit, OnDestroy, ViewChild } from '@angular/core'; import {
import { VersionsApi, Node, VersionEntry, NodesApi, NodeEntry, ContentApi, ContentPagingQuery } from '@alfresco/js-api'; 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 { MatDialog } from '@angular/material/dialog';
import { ContentVersionService } from './content-version.service'; import { ContentVersionService } from './content-version.service';
import { ContentService } from '../common'; import { ContentService } from '../common';
import { InfiniteScrollDatasource } from '../infinite-scroll-datasource'; import { InfiniteScrollDatasource } from '../infinite-scroll-datasource';
import { from, Observable, Subject } from 'rxjs'; import { from, Observable } from 'rxjs';
import { map, take, takeUntil } from 'rxjs/operators'; import { map, take } from 'rxjs/operators';
import { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatProgressBarModule } from '@angular/material/progress-bar';
@@ -34,6 +45,7 @@ import { MatMenuModule } from '@angular/material/menu';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { VersionCompatibilityDirective } from '../version-compatibility'; import { VersionCompatibilityDirective } from '../version-compatibility';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export class VersionListDataSource extends InfiniteScrollDatasource<VersionEntry> { export class VersionListDataSource extends InfiniteScrollDatasource<VersionEntry> {
constructor(private versionsApi: VersionsApi, private node: Node) { constructor(private versionsApi: VersionsApi, private node: Node) {
@@ -69,8 +81,7 @@ export class VersionListDataSource extends InfiniteScrollDatasource<VersionEntry
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-version-list' } host: { class: 'adf-version-list' }
}) })
export class VersionListComponent implements OnChanges, OnInit, OnDestroy { export class VersionListComponent implements OnChanges, OnInit {
private onDestroy$ = new Subject<void>();
private _contentApi: ContentApi; private _contentApi: ContentApi;
get contentApi(): ContentApi { get contentApi(): ContentApi {
this._contentApi = this._contentApi ?? new ContentApi(this.alfrescoApi.getInstance()); this._contentApi = this._contentApi ?? new ContentApi(this.alfrescoApi.getInstance());
@@ -132,6 +143,8 @@ export class VersionListComponent implements OnChanges, OnInit, OnDestroy {
@ViewChild('viewport') @ViewChild('viewport')
viewport: CdkVirtualScrollViewport; viewport: CdkVirtualScrollViewport;
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private alfrescoApi: AlfrescoApiService, private alfrescoApi: AlfrescoApiService,
private contentService: ContentService, private contentService: ContentService,
@@ -141,7 +154,7 @@ export class VersionListComponent implements OnChanges, OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.versionsDataSource = new VersionListDataSource(this.versionsApi, this.node); 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.isLoading = isLoading;
this.latestVersion = this.versionsDataSource.firstItem; 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 { canUpdate(): boolean {
return this.contentService.hasAllowableOperations(this.node, 'update') && this.versionsDataSource.itemsCount > 1; 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) { if (this.allowDownload) {
this.contentVersionService this.contentVersionService
.getVersionContentUrl(this.node.id, versionId, true) .getVersionContentUrl(this.node.id, versionId, true)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((versionDownloadUrl) => this.downloadContent(versionDownloadUrl)); .subscribe((versionDownloadUrl) => this.downloadContent(versionDownloadUrl));
} }
} }
@@ -207,7 +216,7 @@ export class VersionListComponent implements OnChanges, OnInit, OnDestroy {
dialogRef dialogRef
.afterClosed() .afterClosed()
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((result) => { .subscribe((result) => {
if (result) { if (result) {
this.versionsApi.deleteVersion(this.node.id, versionId).then(() => this.onVersionDeleted(this.node)); this.versionsApi.deleteVersion(this.node.id, versionId).then(() => this.onVersionDeleted(this.node));

View File

@@ -15,10 +15,8 @@
* limitations under the License. * 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 { Node, Version } from '@alfresco/js-api';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { ContentService } from '../common/services/content.service'; import { ContentService } from '../common/services/content.service';
import { UploadService } from '../common/services/upload.service'; import { UploadService } from '../common/services/upload.service';
import { FileUploadErrorEvent, FileUploadEvent } from '../common/events/file.event'; 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 { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { UploadVersionButtonComponent } from '../upload'; import { UploadVersionButtonComponent } from '../upload';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-version-upload', selector: 'adf-version-upload',
@@ -49,12 +48,11 @@ import { UploadVersionButtonComponent } from '../upload';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-version-upload' } host: { class: 'adf-version-upload' }
}) })
export class VersionUploadComponent implements OnInit, OnDestroy { export class VersionUploadComponent implements OnInit {
semanticVersion: string = 'minor'; semanticVersion: string = 'minor';
comment: string; comment: string;
uploadVersion: boolean = false; uploadVersion: boolean = false;
disabled: boolean = false; disabled: boolean = false;
onDestroy$ = new Subject<void>();
majorVersion = '2.0'; majorVersion = '2.0';
minorVersion = '1.1'; minorVersion = '1.1';
@@ -107,10 +105,12 @@ export class VersionUploadComponent implements OnInit, OnDestroy {
@Output() @Output()
uploadStarted = new EventEmitter<FileUploadEvent>(); uploadStarted = new EventEmitter<FileUploadEvent>();
private readonly destroyRef = inject(DestroyRef);
constructor(private contentService: ContentService, private uploadService: UploadService) {} constructor(private contentService: ContentService, private uploadService: UploadService) {}
ngOnInit() { 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.disabled = true;
this.uploadStarted.emit(event); this.uploadStarted.emit(event);
}); });
@@ -146,11 +146,6 @@ export class VersionUploadComponent implements OnInit, OnDestroy {
this.error.emit(event); this.error.emit(event);
} }
ngOnDestroy() {
this.onDestroy$.next(undefined);
this.onDestroy$.complete();
}
getNextMinorVersion(version: string): string { getNextMinorVersion(version: string): string {
const { major, minor } = this.getParsedVersion(version); const { major, minor } = this.getParsedVersion(version);
return `${major}.${minor + 1}`; return `${major}.${minor + 1}`;

View File

@@ -19,10 +19,11 @@ import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ContentChild, ContentChild,
DestroyRef,
EventEmitter, EventEmitter,
inject,
Input, Input,
OnChanges, OnChanges,
OnDestroy,
OnInit, OnInit,
Output, Output,
SimpleChanges, SimpleChanges,
@@ -33,8 +34,8 @@ import {
import { import {
CloseButtonPosition, CloseButtonPosition,
Track, Track,
ViewerComponent,
VIEWER_DIRECTIVES, VIEWER_DIRECTIVES,
ViewerComponent,
ViewerMoreActionsComponent, ViewerMoreActionsComponent,
ViewerOpenWithComponent, ViewerOpenWithComponent,
ViewerSidebarComponent, ViewerSidebarComponent,
@@ -43,11 +44,10 @@ import {
ViewUtilService ViewUtilService
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { AlfrescoApiService } from '../../services/alfresco-api.service'; 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 { ContentApi, Node, NodeEntry, NodesApi, RenditionEntry, SharedlinksApi, Version, VersionEntry, VersionsApi } from '@alfresco/js-api';
import { RenditionService } from '../../common/services/rendition.service'; import { RenditionService } from '../../common/services/rendition.service';
import { MatDialog } from '@angular/material/dialog'; 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 { ContentService } from '../../common/services/content.service';
import { NodesApiService } from '../../common/services/nodes-api.service'; import { NodesApiService } from '../../common/services/nodes-api.service';
import { UploadService } from '../../common/services/upload.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 { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { NodeDownloadDirective } from '../../directives'; import { NodeDownloadDirective } from '../../directives';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-alfresco-viewer', selector: 'adf-alfresco-viewer',
@@ -69,7 +70,7 @@ import { NodeDownloadDirective } from '../../directives';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
providers: [ViewUtilService] providers: [ViewUtilService]
}) })
export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy { export class AlfrescoViewerComponent implements OnChanges, OnInit {
@ViewChild('adfViewer') @ViewChild('adfViewer')
adfViewer: ViewerComponent<{ node: Node }>; adfViewer: ViewerComponent<{ node: Node }>;
@@ -204,8 +205,6 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
@Output() @Output()
showViewerChange = new EventEmitter<boolean>(); showViewerChange = new EventEmitter<boolean>();
private onDestroy$ = new Subject<boolean>();
private cacheBusterNumber: number; private cacheBusterNumber: number;
versionEntry: VersionEntry; versionEntry: VersionEntry;
@@ -247,6 +246,8 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
return this._contentApi; return this._contentApi;
} }
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private apiService: AlfrescoApiService, private apiService: AlfrescoApiService,
private nodesApiService: NodesApiService, private nodesApiService: NodesApiService,
@@ -268,7 +269,7 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
(node) => (node) =>
node && node.id === this.nodeId && this.getNodeVersionProperty(this.nodeEntry.entry) !== this.getNodeVersionProperty(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)); .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() { onDownloadFile() {
this.nodeActionsService.downloadNode(this.nodeEntry); this.nodeActionsService.downloadNode(this.nodeEntry);
} }

View File

@@ -15,11 +15,10 @@
* limitations under the License. * 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 { CommonModule } from '@angular/common';
import { FeaturesServiceToken, IDebugFeaturesService } from '../interfaces/features.interface'; import { FeaturesServiceToken, IDebugFeaturesService } from '../interfaces/features.interface';
import { takeUntil } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Subject } from 'rxjs';
@Component({ @Component({
selector: 'adf-feature-flags-override-indicator', selector: 'adf-feature-flags-override-indicator',
@@ -49,9 +48,8 @@ import { Subject } from 'rxjs';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class FlagsOverrideComponent implements OnDestroy { export class FlagsOverrideComponent {
isEnabled = false; isEnabled = false;
destroy$ = new Subject<void>();
@Input() @Input()
size: 'small' | 'medium' | 'large' = 'medium'; size: 'small' | 'medium' | 'large' = 'medium';
@@ -64,16 +62,11 @@ export class FlagsOverrideComponent implements OnDestroy {
if (this.featuresService.isEnabled$) { if (this.featuresService.isEnabled$) {
this.featuresService this.featuresService
.isEnabled$() .isEnabled$()
.pipe(takeUntil(this.destroy$)) .pipe(takeUntilDestroyed())
.subscribe((isEnabled) => { .subscribe((isEnabled) => {
this.isEnabled = isEnabled; this.isEnabled = isEnabled;
changeDetectorRef.markForCheck(); changeDetectorRef.markForCheck();
}); });
} }
} }
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
} }

View File

@@ -15,18 +15,18 @@
* limitations under the License. * 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 { CommonModule } from '@angular/common';
import { import {
IWritableFeaturesService,
FeaturesServiceToken, FeaturesServiceToken,
WritableFeaturesServiceToken,
IDebugFeaturesService, IDebugFeaturesService,
WritableFlagChangeset, IFeaturesService,
IFeaturesService IWritableFeaturesService,
WritableFeaturesServiceToken,
WritableFlagChangeset
} from '../../interfaces/features.interface'; } from '../../interfaces/features.interface';
import { BehaviorSubject, Observable, Subject, combineLatest } from 'rxjs'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { debounceTime, map, take, takeUntil, tap } from 'rxjs/operators'; import { debounceTime, map, take, tap } from 'rxjs/operators';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar';
@@ -37,6 +37,7 @@ import { FormsModule } from '@angular/forms';
import { FlagsOverrideComponent } from '../feature-override-indicator.component'; import { FlagsOverrideComponent } from '../feature-override-indicator.component';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-feature-flags-overrides', selector: 'adf-feature-flags-overrides',
@@ -59,17 +60,15 @@ import { TranslateModule } from '@ngx-translate/core';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class FlagsComponent implements OnDestroy { export class FlagsComponent {
displayedColumns: string[] = ['icon', 'flag', 'value']; displayedColumns: string[] = ['icon', 'flag', 'value'];
flags$: Observable<{ fictive: boolean; flag: string; value: any }[]>; flags$: Observable<{ fictive: boolean; flag: string; value: any }[]>;
isEnabled = false; isEnabled = false;
destroy$ = new Subject<void>();
inputValue = ''; inputValue = '';
inputValue$ = new BehaviorSubject<string>(''); inputValue$ = new BehaviorSubject<string>('');
showPlusButton$!: Observable<boolean>; showPlusButton$!: Observable<boolean>;
writableFlagChangeset: WritableFlagChangeset = {}; writableFlagChangeset: WritableFlagChangeset = {};
constructor( constructor(
@Inject(FeaturesServiceToken) @Inject(FeaturesServiceToken)
private featuresService: IDebugFeaturesService & IFeaturesService<WritableFlagChangeset>, private featuresService: IDebugFeaturesService & IFeaturesService<WritableFlagChangeset>,
@@ -79,7 +78,7 @@ export class FlagsComponent implements OnDestroy {
if (this.featuresService.isEnabled$) { if (this.featuresService.isEnabled$) {
this.featuresService this.featuresService
.isEnabled$() .isEnabled$()
.pipe(takeUntil(this.destroy$)) .pipe(takeUntilDestroyed())
.subscribe((isEnabled) => { .subscribe((isEnabled) => {
this.isEnabled = isEnabled; this.isEnabled = isEnabled;
}); });
@@ -149,9 +148,4 @@ export class FlagsComponent implements OnDestroy {
protected onDelete(flag: string) { protected onDelete(flag: string) {
this.writableFeaturesService.removeFlag(flag); this.writableFeaturesService.removeFlag(flag);
} }
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
} }

View File

@@ -15,21 +15,20 @@
* limitations under the License. * limitations under the License.
*/ */
import { Directive, Inject, Input, OnDestroy, TemplateRef, ViewContainerRef } from '@angular/core'; import { Directive, Inject, Input, TemplateRef, ViewContainerRef } from '@angular/core';
import { BehaviorSubject, Subject, combineLatest } from 'rxjs'; import { BehaviorSubject, combineLatest } from 'rxjs';
import { IFeaturesService, FeaturesServiceToken, FlagChangeset } from '../interfaces/features.interface'; import { FeaturesServiceToken, FlagChangeset, IFeaturesService } from '../interfaces/features.interface';
import { takeUntil } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Directive({ @Directive({
/* eslint-disable-next-line @angular-eslint/directive-selector */ /* eslint-disable-next-line @angular-eslint/directive-selector */
selector: '[adfForFeatures]', selector: '[adfForFeatures]',
standalone: true standalone: true
}) })
export class FeaturesDirective implements OnDestroy { export class FeaturesDirective {
private hasView = false; private hasView = false;
private inputUpdate$ = new BehaviorSubject([] as string[]); private inputUpdate$ = new BehaviorSubject([] as string[]);
private destroy$ = new Subject();
@Input() @Input()
set adfForFeatures(feature: string[] | string) { set adfForFeatures(feature: string[] | string) {
this.inputUpdate$.next(Array.isArray(feature) ? feature : [feature]); this.inputUpdate$.next(Array.isArray(feature) ? feature : [feature]);
@@ -41,7 +40,7 @@ export class FeaturesDirective implements OnDestroy {
private viewContainer: ViewContainerRef private viewContainer: ViewContainerRef
) { ) {
combineLatest([this.featuresService.getFlags$(), this.inputUpdate$]) combineLatest([this.featuresService.getFlags$(), this.inputUpdate$])
.pipe(takeUntil(this.destroy$)) .pipe(takeUntilDestroyed())
.subscribe(([flags, features]: any) => this.updateView(flags, features)); .subscribe(([flags, features]: any) => this.updateView(flags, features));
} }
@@ -56,9 +55,4 @@ export class FeaturesDirective implements OnDestroy {
this.hasView = false; this.hasView = false;
} }
} }
ngOnDestroy() {
this.destroy$.next({});
this.destroy$.complete();
}
} }

View File

@@ -15,20 +15,19 @@
* limitations under the License. * limitations under the License.
*/ */
import { Directive, Inject, Input, OnDestroy, TemplateRef, ViewContainerRef } from '@angular/core'; import { Directive, Inject, Input, TemplateRef, ViewContainerRef } from '@angular/core';
import { BehaviorSubject, Subject, combineLatest } from 'rxjs'; import { BehaviorSubject, combineLatest } from 'rxjs';
import { IFeaturesService, FeaturesServiceToken, FlagChangeset } from '../interfaces/features.interface'; import { FeaturesServiceToken, FlagChangeset, IFeaturesService } from '../interfaces/features.interface';
import { takeUntil } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Directive({ @Directive({
/* eslint-disable-next-line @angular-eslint/directive-selector */ /* eslint-disable-next-line @angular-eslint/directive-selector */
selector: '[adfNotForFeatures]', selector: '[adfNotForFeatures]',
standalone: true standalone: true
}) })
export class NotFeaturesDirective implements OnDestroy { export class NotFeaturesDirective {
private hasView = false; private hasView = false;
private inputUpdate$ = new BehaviorSubject([] as string[]); private inputUpdate$ = new BehaviorSubject([] as string[]);
private destroy$ = new Subject();
@Input() @Input()
set adfNotForFeatures(feature: string[] | string) { set adfNotForFeatures(feature: string[] | string) {
@@ -41,7 +40,7 @@ export class NotFeaturesDirective implements OnDestroy {
private viewContainer: ViewContainerRef private viewContainer: ViewContainerRef
) { ) {
combineLatest([this.featuresService.getFlags$(), this.inputUpdate$]) combineLatest([this.featuresService.getFlags$(), this.inputUpdate$])
.pipe(takeUntil(this.destroy$)) .pipe(takeUntilDestroyed())
.subscribe(([flags, features]: any) => this.updateView(flags, features)); .subscribe(([flags, features]: any) => this.updateView(flags, features));
} }
@@ -56,9 +55,4 @@ export class NotFeaturesDirective implements OnDestroy {
this.hasView = false; this.hasView = false;
} }
} }
ngOnDestroy() {
this.destroy$.next({});
this.destroy$.complete();
}
} }

View File

@@ -17,14 +17,20 @@
import { AppConfigService, SidenavLayoutComponent, SidenavLayoutModule } from '@alfresco/adf-core'; import { AppConfigService, SidenavLayoutComponent, SidenavLayoutModule } from '@alfresco/adf-core';
import { DynamicExtensionComponent } from '@alfresco/adf-extensions'; 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 { NavigationEnd, Router, RouterModule } from '@angular/router';
import { Subject, Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { filter, takeUntil, map, withLatestFrom } from 'rxjs/operators'; import { filter, map, withLatestFrom } from 'rxjs/operators';
import { BreakpointObserver } from '@angular/cdk/layout'; import { BreakpointObserver } from '@angular/cdk/layout';
import { Directionality } from '@angular/cdk/bidi'; 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 { CommonModule } from '@angular/common';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'app-shell', selector: 'app-shell',
@@ -35,11 +41,10 @@ import { CommonModule } from '@angular/common';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'app-shell' } host: { class: 'app-shell' }
}) })
export class ShellLayoutComponent implements OnInit, OnDestroy { export class ShellLayoutComponent implements OnInit {
@ViewChild('layout', { static: true }) @ViewChild('layout', { static: true })
layout: SidenavLayoutComponent; layout: SidenavLayoutComponent;
onDestroy$: Subject<boolean> = new Subject<boolean>();
isSmallScreen$: Observable<boolean>; isSmallScreen$: Observable<boolean>;
expandedSidenav: boolean; expandedSidenav: boolean;
@@ -49,6 +54,8 @@ export class ShellLayoutComponent implements OnInit, OnDestroy {
sidenavMax: number; sidenavMax: number;
direction: Directionality; direction: Directionality;
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private router: Router, private router: Router,
private appConfigService: AppConfigService, private appConfigService: AppConfigService,
@@ -77,7 +84,7 @@ export class ShellLayoutComponent implements OnInit, OnDestroy {
.pipe( .pipe(
withLatestFrom(this.isSmallScreen$), withLatestFrom(this.isSmallScreen$),
filter(([event, isSmallScreen]) => isSmallScreen && event instanceof NavigationEnd), filter(([event, isSmallScreen]) => isSmallScreen && event instanceof NavigationEnd),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe(() => { .subscribe(() => {
this.layout.container.sidenav.close(); this.layout.container.sidenav.close();
@@ -86,7 +93,7 @@ export class ShellLayoutComponent implements OnInit, OnDestroy {
this.router.events this.router.events
.pipe( .pipe(
filter((event) => event instanceof NavigationEnd), filter((event) => event instanceof NavigationEnd),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((event: NavigationEnd) => { .subscribe((event: NavigationEnd) => {
this.minimizeSidenav = this.shellService.minimizeSidenavConditions.some((el) => event.urlAfterRedirects.includes(el)); this.minimizeSidenav = this.shellService.minimizeSidenavConditions.some((el) => event.urlAfterRedirects.includes(el));
@@ -95,12 +102,6 @@ export class ShellLayoutComponent implements OnInit, OnDestroy {
this.updateState(); this.updateState();
}); });
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
hideMenu(event: Event) { hideMenu(event: Event) {
if (this.layout.container.isMobileScreenSize) { if (this.layout.container.isMobileScreenSize) {
event.preventDefault(); event.preventDefault();

View File

@@ -15,15 +15,14 @@
* limitations under the License. * 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 { CardViewUpdateService } from '../services/card-view-update.service';
import { CardViewItem } from '../interfaces/card-view.interfaces'; import { CardViewItem } from '../interfaces/card-view.interfaces';
import { Subject } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { takeUntil } from 'rxjs/operators';
@Directive() @Directive()
// eslint-disable-next-line @angular-eslint/directive-class-suffix // eslint-disable-next-line @angular-eslint/directive-class-suffix
export abstract class BaseCardView<T extends CardViewItem> implements OnDestroy { export abstract class BaseCardView<T extends CardViewItem> {
protected cardViewUpdateService = inject(CardViewUpdateService); protected cardViewUpdateService = inject(CardViewUpdateService);
@Input() @Input()
@@ -32,10 +31,8 @@ export abstract class BaseCardView<T extends CardViewItem> implements OnDestroy
@Input() @Input()
property: T; property: T;
protected destroy$ = new Subject<boolean>();
constructor() { constructor() {
this.cardViewUpdateService.updateItem$.pipe(takeUntil(this.destroy$)).subscribe((itemModel) => { this.cardViewUpdateService.updateItem$.pipe(takeUntilDestroyed()).subscribe((itemModel) => {
if (this.property.key === itemModel.key) { if (this.property.key === itemModel.key) {
this.property.value = itemModel.value; this.property.value = itemModel.value;
} }
@@ -54,8 +51,4 @@ export abstract class BaseCardView<T extends CardViewItem> implements OnDestroy
return !!this.property.icon; return !!this.property.icon;
} }
ngOnDestroy(): void {
this.destroy$.next(true);
this.destroy$.complete();
}
} }

View File

@@ -15,7 +15,7 @@
* limitations under the License. * 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 { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
import { import {
DatetimeAdapter, DatetimeAdapter,
@@ -26,7 +26,6 @@ import {
} from '@mat-datetimepicker/core'; } from '@mat-datetimepicker/core';
import { CardViewDateItemModel } from '../../models/card-view-dateitem.model'; import { CardViewDateItemModel } from '../../models/card-view-dateitem.model';
import { UserPreferencesService, UserPreferenceValues } from '../../../common/services/user-preferences.service'; import { UserPreferencesService, UserPreferenceValues } from '../../../common/services/user-preferences.service';
import { takeUntil } from 'rxjs/operators';
import { BaseCardView } from '../base-card-view'; import { BaseCardView } from '../base-card-view';
import { ClipboardService } from '../../../clipboard/clipboard.service'; import { ClipboardService } from '../../../clipboard/clipboard.service';
import { TranslationService } from '../../../translation/translation.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 { MatFormFieldModule } from '@angular/material/form-field';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
providers: [ providers: [
@@ -66,7 +66,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-card-view-dateitem' } host: { class: 'adf-card-view-dateitem' }
}) })
export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemModel> implements OnInit, OnDestroy { export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemModel> implements OnInit {
@Input() @Input()
displayEmpty = true; displayEmpty = true;
@@ -78,6 +78,8 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
valueDate: Date; valueDate: Date;
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private dateAdapter: DateAdapter<Date>, private dateAdapter: DateAdapter<Date>,
private userPreferencesService: UserPreferencesService, private userPreferencesService: UserPreferencesService,
@@ -90,7 +92,7 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
ngOnInit() { ngOnInit() {
this.userPreferencesService this.userPreferencesService
.select(UserPreferenceValues.Locale) .select(UserPreferenceValues.Locale)
.pipe(takeUntil(this.destroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((locale) => { .subscribe((locale) => {
this.property.locale = locale; this.property.locale = locale;
}); });
@@ -104,10 +106,6 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
} }
} }
ngOnDestroy() {
super.ngOnDestroy();
}
get showProperty(): boolean { get showProperty(): boolean {
return this.displayEmpty || !this.property.isEmpty(); return this.displayEmpty || !this.property.isEmpty();
} }

View File

@@ -52,7 +52,6 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
}) })
export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItemModel<string | number>> implements OnInit, OnChanges { export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItemModel<string | number>> implements OnInit, OnChanges {
private appConfig = inject(AppConfigService); private appConfig = inject(AppConfigService);
private readonly destroyRef = inject(DestroyRef);
static HIDE_FILTER_LIMIT = 5; static HIDE_FILTER_LIMIT = 5;
@Input() options$: Observable<CardViewSelectItemOption<string | number>[]>; @Input() options$: Observable<CardViewSelectItemOption<string | number>[]>;
@@ -71,6 +70,8 @@ export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItem
autocompleteControl = new UntypedFormControl(); autocompleteControl = new UntypedFormControl();
editedValue: string | number; editedValue: string | number;
private readonly destroyRef = inject(DestroyRef);
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
this.value = this.property.value; this.value = this.property.value;
if (changes.property?.firstChange) { if (changes.property?.firstChange) {

View File

@@ -15,10 +15,19 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, ViewEncapsulation, ViewChild, ElementRef, OnDestroy, Inject, Output, EventEmitter, OnInit } from '@angular/core'; import {
Component,
DestroyRef,
ElementRef,
EventEmitter,
Inject,
inject,
OnInit,
Output,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import { MatSelect } from '@angular/material/select'; import { MatSelect } from '@angular/material/select';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
@@ -26,6 +35,7 @@ import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-select-filter-input', selector: 'adf-select-filter-input',
@@ -36,13 +46,14 @@ import { TranslateModule } from '@ngx-translate/core';
host: { class: 'adf-select-filter-input' }, host: { class: 'adf-select-filter-input' },
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class SelectFilterInputComponent implements OnInit, OnDestroy { export class SelectFilterInputComponent implements OnInit {
@ViewChild('selectFilterInput', { read: ElementRef, static: false }) selectFilterInput: ElementRef; @ViewChild('selectFilterInput', { read: ElementRef, static: false }) selectFilterInput: ElementRef;
@Output() change = new EventEmitter<string>(); @Output() change = new EventEmitter<string>();
term = ''; term = '';
previousSelected: any[]; previousSelected: any[];
private onDestroy$ = new Subject<void>();
private readonly destroyRef = inject(DestroyRef);
constructor(@Inject(MatSelect) private matSelect: MatSelect) {} constructor(@Inject(MatSelect) private matSelect: MatSelect) {}
@@ -51,9 +62,9 @@ export class SelectFilterInputComponent implements OnInit, OnDestroy {
} }
ngOnInit() { 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) { if (isOpened) {
this.selectFilterInput.nativeElement.focus(); this.selectFilterInput.nativeElement.focus();
} else { } else {
@@ -63,7 +74,7 @@ export class SelectFilterInputComponent implements OnInit, OnDestroy {
if (this.matSelect.ngControl) { if (this.matSelect.ngControl) {
this.previousSelected = this.matSelect.ngControl.value; 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; let restoreSelection = false;
if (this.matSelect.multiple && Array.isArray(this.previousSelected)) { if (this.matSelect.multiple && Array.isArray(this.previousSelected)) {
if (!Array.isArray(values)) { if (!Array.isArray(values)) {
@@ -109,9 +120,4 @@ export class SelectFilterInputComponent implements OnInit, OnDestroy {
} }
} }
} }
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
}
} }

View File

@@ -15,7 +15,16 @@
* limitations under the License. * 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 { CardViewTextItemModel } from '../../models/card-view-textitem.model';
import { BaseCardView } from '../base-card-view'; import { BaseCardView } from '../base-card-view';
import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips'; import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips';
@@ -23,7 +32,7 @@ import { ClipboardService } from '../../../clipboard/clipboard.service';
import { TranslationService } from '../../../translation/translation.service'; import { TranslationService } from '../../../translation/translation.service';
import { CardViewItemValidator } from '../../interfaces/card-view-item-validator.interface'; import { CardViewItemValidator } from '../../interfaces/card-view-item-validator.interface';
import { FormsModule, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; 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 { CommonModule } from '@angular/common';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
@@ -31,6 +40,7 @@ import { MatInputModule } from '@angular/material/input';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export const DEFAULT_SEPARATOR = ', '; export const DEFAULT_SEPARATOR = ', ';
const templateTypes = { const templateTypes = {
@@ -61,7 +71,7 @@ const templateTypes = {
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-card-view-textitem' } host: { class: 'adf-card-view-textitem' }
}) })
export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemModel> implements OnChanges, OnDestroy { export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemModel> implements OnChanges {
@Input() @Input()
displayEmpty = true; displayEmpty = true;
@@ -81,6 +91,8 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
errors: CardViewItemValidator[]; errors: CardViewItemValidator[];
templateType: string; templateType: string;
textInput = new UntypedFormControl(); textInput = new UntypedFormControl();
private readonly destroyRef = inject(DestroyRef);
constructor(private clipboardService: ClipboardService, private translateService: TranslationService, private cd: ChangeDetectorRef) { constructor(private clipboardService: ClipboardService, private translateService: TranslationService, private cd: ChangeDetectorRef) {
super(); super();
@@ -92,7 +104,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
.pipe( .pipe(
filter((textInputValue) => textInputValue !== this.editedValue && textInputValue !== null), filter((textInputValue) => textInputValue !== this.editedValue && textInputValue !== null),
debounceTime(50), debounceTime(50),
takeUntil(this.destroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((textInputValue) => { .subscribe((textInputValue) => {
this.editedValue = textInputValue; this.editedValue = textInputValue;
@@ -222,10 +234,6 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
} }
} }
ngOnDestroy() {
super.ngOnDestroy();
}
get showProperty(): boolean { get showProperty(): boolean {
return this.displayEmpty || !this.property.isEmpty(); return this.displayEmpty || !this.property.isEmpty();
} }

View File

@@ -18,7 +18,7 @@
import { ChangeDetectionStrategy, Component, OnInit, ViewEncapsulation } from '@angular/core'; import { ChangeDetectionStrategy, Component, OnInit, ViewEncapsulation } from '@angular/core';
import { DataTableCellComponent } from '../datatable-cell/datatable-cell.component'; import { DataTableCellComponent } from '../datatable-cell/datatable-cell.component';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { takeUntil } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
standalone: true, standalone: true,
@@ -39,7 +39,7 @@ export class BooleanCellComponent extends DataTableCellComponent implements OnIn
ngOnInit() { ngOnInit() {
super.ngOnInit(); super.ngOnInit();
this.value$.pipe(takeUntil(this.onDestroy$)).subscribe((value) => { this.value$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((value) => {
this.boolValue = this.transformBoolean(value); this.boolValue = this.transformBoolean(value);
}); });
} }

View File

@@ -15,11 +15,10 @@
* limitations under the License. * 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 { ReactiveFormsModule, UntypedFormControl } from '@angular/forms';
import { MatMenuTrigger } from '@angular/material/menu'; import { MatMenuTrigger } from '@angular/material/menu';
import { Subject } from 'rxjs'; import { debounceTime } from 'rxjs/operators';
import { debounceTime, takeUntil } from 'rxjs/operators';
import { DataColumn } from '../../data/data-column.model'; import { DataColumn } from '../../data/data-column.model';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
@@ -28,6 +27,7 @@ import { MatIconModule } from '@angular/material/icon';
import { MatDividerModule } from '@angular/material/divider'; import { MatDividerModule } from '@angular/material/divider';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { TranslationService } from '../../../translation'; import { TranslationService } from '../../../translation';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-datatable-column-selector', selector: 'adf-datatable-column-selector',
@@ -37,7 +37,7 @@ import { TranslationService } from '../../../translation';
styleUrls: ['./columns-selector.component.scss'], styleUrls: ['./columns-selector.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class ColumnsSelectorComponent implements OnInit, OnDestroy { export class ColumnsSelectorComponent implements OnInit {
private translationService = inject(TranslationService); private translationService = inject(TranslationService);
@Input() @Input()
@@ -55,21 +55,22 @@ export class ColumnsSelectorComponent implements OnInit, OnDestroy {
@Output() @Output()
submitColumnsVisibility = new EventEmitter<DataColumn[]>(); submitColumnsVisibility = new EventEmitter<DataColumn[]>();
onDestroy$ = new Subject();
columnItems: DataColumn[] = []; columnItems: DataColumn[] = [];
searchInputControl = new UntypedFormControl(''); searchInputControl = new UntypedFormControl('');
searchQuery = ''; searchQuery = '';
private readonly destroyRef = inject(DestroyRef);
ngOnInit(): void { ngOnInit(): void {
this.mainMenuTrigger.menuOpened.pipe(takeUntil(this.onDestroy$)).subscribe(() => { this.mainMenuTrigger.menuOpened.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
this.updateColumnItems(); this.updateColumnItems();
}); });
this.mainMenuTrigger.menuClosed.pipe(takeUntil(this.onDestroy$)).subscribe(() => { this.mainMenuTrigger.menuClosed.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
this.searchInputControl.setValue(''); 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.searchQuery = searchQuery;
this.updateColumnItems(); this.updateColumnItems();
}); });
@@ -81,12 +82,6 @@ export class ColumnsSelectorComponent implements OnInit, OnDestroy {
columns = this.sortColumns(columns); columns = this.sortColumns(columns);
this.columnItems = columns; this.columnItems = columns;
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
closeMenu(): void { closeMenu(): void {
this.mainMenuTrigger.closeMenu(); this.mainMenuTrigger.closeMenu();
} }

View File

@@ -15,15 +15,23 @@
* limitations under the License. * 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 { DataColumn } from '../../data/data-column.model';
import { DataRow } from '../../data/data-row.model'; import { DataRow } from '../../data/data-row.model';
import { DataTableAdapter } from '../../data/datatable-adapter'; import { DataTableAdapter } from '../../data/datatable-adapter';
import { BehaviorSubject, Subject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { DataTableService } from '../../services/datatable.service'; import { DataTableService } from '../../services/datatable.service';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ClipboardDirective } from '../../../clipboard/clipboard.directive'; import { ClipboardDirective } from '../../../clipboard/clipboard.directive';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-datatable-cell', selector: 'adf-datatable-cell',
@@ -49,7 +57,7 @@ import { ClipboardDirective } from '../../../clipboard/clipboard.directive';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: { class: 'adf-datatable-content-cell' } host: { class: 'adf-datatable-content-cell' }
}) })
export class DataTableCellComponent implements OnInit, OnDestroy { export class DataTableCellComponent implements OnInit {
/** Data table adapter instance. */ /** Data table adapter instance. */
@Input() @Input()
data: DataTableAdapter; data: DataTableAdapter;
@@ -74,7 +82,7 @@ export class DataTableCellComponent implements OnInit, OnDestroy {
@Input() @Input()
resolverFn: (row: DataRow, col: DataColumn) => any = null; resolverFn: (row: DataRow, col: DataColumn) => any = null;
protected onDestroy$ = new Subject<boolean>(); protected destroyRef = inject(DestroyRef);
protected dataTableService = inject(DataTableService, { optional: true }); protected dataTableService = inject(DataTableService, { optional: true });
value$ = new BehaviorSubject<any>(''); value$ = new BehaviorSubject<any>('');
@@ -100,7 +108,7 @@ export class DataTableCellComponent implements OnInit, OnDestroy {
return; 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) { if (data?.id === this.row?.id && data.obj) {
this.row.obj = data.obj; this.row.obj = data.obj;
this.row['cache'][this.column.key] = this.getNestedPropertyValue(data.obj, this.column.key); 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) { private getNestedPropertyValue(obj: any, path: string) {
return path.split('.').reduce((source, key) => (source ? source[key] : ''), obj); return path.split('.').reduce((source, key) => (source ? source[key] : ''), obj);
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -22,10 +22,12 @@ import {
AfterViewInit, AfterViewInit,
Component, Component,
ContentChild, ContentChild,
DestroyRef,
DoCheck, DoCheck,
ElementRef, ElementRef,
EventEmitter, EventEmitter,
HostListener, HostListener,
inject,
Input, Input,
IterableDiffers, IterableDiffers,
OnChanges, OnChanges,
@@ -79,6 +81,7 @@ import { BooleanCellComponent } from '../boolean-cell/boolean-cell.component';
import { JsonCellComponent } from '../json-cell/json-cell.component'; import { JsonCellComponent } from '../json-cell/json-cell.component';
import { AmountCellComponent } from '../amount-cell/amount-cell.component'; import { AmountCellComponent } from '../amount-cell/amount-cell.component';
import { NumberCellComponent } from '../number-cell/number-cell.component'; import { NumberCellComponent } from '../number-cell/number-cell.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
// eslint-disable-next-line no-shadow // eslint-disable-next-line no-shadow
export enum ShowHeaderMode { export enum ShowHeaderMode {
@@ -325,10 +328,10 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
private differ: any; private differ: any;
private rowMenuCache: any = {}; private rowMenuCache: any = {};
private subscriptions: Subscription[] = [];
private singleClickStreamSub: Subscription; private singleClickStreamSub: Subscription;
private multiClickStreamSub: Subscription; private multiClickStreamSub: Subscription;
private dataRowsChanged: Subscription;
private readonly destroyRef = inject(DestroyRef);
@HostListener('keyup', ['$event']) @HostListener('keyup', ['$event'])
onKeydown(event: KeyboardEvent): void { onKeydown(event: KeyboardEvent): void {
@@ -349,11 +352,9 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
ngAfterContentInit() { ngAfterContentInit() {
if (this.columnList) { if (this.columnList) {
this.subscriptions.push( this.columnList.columns.changes.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
this.columnList.columns.changes.subscribe(() => { this.setTableSchema();
this.setTableSchema(); });
})
);
} }
this.setTableSchema(); this.setTableSchema();
} }
@@ -713,7 +714,7 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
this.isSelectAllIndeterminate = false; this.isSelectAllIndeterminate = false;
if (this.multiselect) { 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) { if (selectableRows && selectableRows.length > 0) {
for (let i = 0; i < selectableRows.length; i++) { for (let i = 0; i < selectableRows.length; i++) {
this.selectRow(selectableRows[i], matCheckboxChange.checked); this.selectRow(selectableRows[i], matCheckboxChange.checked);
@@ -954,14 +955,6 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
ngOnDestroy() { ngOnDestroy() {
this.unsubscribeClickStream(); this.unsubscribeClickStream();
this.subscriptions.forEach((s) => s.unsubscribe());
this.subscriptions = [];
if (this.dataRowsChanged) {
this.dataRowsChanged.unsubscribe();
this.dataRowsChanged = null;
}
} }
getNameColumnValue() { getNameColumnValue() {

View File

@@ -19,7 +19,7 @@ import { CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewEncapsulation } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewEncapsulation } from '@angular/core';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { DataTableCellComponent } from '../datatable-cell/datatable-cell.component'; import { DataTableCellComponent } from '../datatable-cell/datatable-cell.component';
import { takeUntil } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
standalone: true, standalone: true,
@@ -43,7 +43,7 @@ export class IconCellComponent extends DataTableCellComponent implements OnInit
ngOnInit(): void { ngOnInit(): void {
super.ngOnInit(); super.ngOnInit();
this.value$.pipe(takeUntil(this.onDestroy$)).subscribe((value) => { this.value$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((value) => {
const newIcon = this.validateIconValue(value) ? value : ''; const newIcon = this.validateIconValue(value) ? value : '';
if (this.icon !== newIcon) { if (this.icon !== newIcon) {
this.icon = newIcon; this.icon = newIcon;

View File

@@ -16,7 +16,7 @@
*/ */
import { TestBed } from '@angular/core/testing'; 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'; import { ResizableDirective } from './resizable.directive';
describe('ResizableDirective', () => { describe('ResizableDirective', () => {
@@ -64,9 +64,12 @@ describe('ResizableDirective', () => {
element = TestBed.inject(ElementRef); element = TestBed.inject(ElementRef);
renderer = TestBed.inject(Renderer2); renderer = TestBed.inject(Renderer2);
ngZone = TestBed.inject(NgZone); ngZone = TestBed.inject(NgZone);
const injector = TestBed.inject(Injector);
spyOn(ngZone, 'runOutsideAngular').and.callFake((fn) => fn()); spyOn(ngZone, 'runOutsideAngular').and.callFake((fn) => fn());
spyOn(ngZone, 'run').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(); directive.ngOnInit();
}); });

View File

@@ -15,10 +15,11 @@
* limitations under the License. * limitations under the License.
*/ */
import { Subject, Observable, Observer, merge } from 'rxjs'; import { merge, Observable, Observer, Subject } from 'rxjs';
import { BoundingRectangle, ResizeEvent, IResizeMouseEvent, ICoordinateX } from './types'; import { BoundingRectangle, ICoordinateX, IResizeMouseEvent, ResizeEvent } from './types';
import { map, take, share, filter, pairwise, mergeMap, takeUntil } from 'rxjs/operators'; import { filter, map, mergeMap, pairwise, share, take, takeUntil } from 'rxjs/operators';
import { OnInit, Output, NgZone, OnDestroy, Directive, Renderer2, ElementRef, EventEmitter, Input } from '@angular/core'; import { DestroyRef, Directive, ElementRef, EventEmitter, inject, Input, NgZone, OnDestroy, OnInit, Output, Renderer2 } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Directive({ @Directive({
selector: '[adf-resizable]', selector: '[adf-resizable]',
@@ -64,7 +65,7 @@ export class ResizableDirective implements OnInit, OnDestroy {
private unsubscribeMouseMove?: () => void; private unsubscribeMouseMove?: () => void;
private unsubscribeMouseUp?: () => void; private unsubscribeMouseUp?: () => void;
private destroy$ = new Subject<void>(); private readonly destroyRef = inject(DestroyRef);
constructor(private readonly renderer: Renderer2, private readonly element: ElementRef<HTMLElement>, private readonly zone: NgZone) { constructor(private readonly renderer: Renderer2, private readonly element: ElementRef<HTMLElement>, private readonly zone: NgZone) {
this.pointerDown = new Observable((observer: Observer<IResizeMouseEvent>) => { this.pointerDown = new Observable((observer: Observer<IResizeMouseEvent>) => {
@@ -133,7 +134,7 @@ export class ResizableDirective implements OnInit, OnDestroy {
.pipe( .pipe(
map(({ resize = false }) => resize), map(({ resize = false }) => resize),
filter((resize) => resize), filter((resize) => resize),
takeUntil(this.destroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe(() => { .subscribe(() => {
const startingRect: BoundingRectangle = this.getElementRect(this.element); 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) { if (this.currentRect) {
this.renderer.setStyle(document.body, 'cursor', ''); this.renderer.setStyle(document.body, 'cursor', '');
if (this.resizeEnd.observers.length > 0) { if (this.resizeEnd.observers.length > 0) {
@@ -172,7 +173,6 @@ export class ResizableDirective implements OnInit, OnDestroy {
this.unsubscribeMouseDown?.(); this.unsubscribeMouseDown?.();
this.unsubscribeMouseMove?.(); this.unsubscribeMouseMove?.();
this.unsubscribeMouseUp?.(); this.unsubscribeMouseUp?.();
this.destroy$.next();
} }
private getNewBoundingRectangle({ top, bottom, left, right }: BoundingRectangle, clientX: number): BoundingRectangle { private getNewBoundingRectangle({ top, bottom, left, right }: BoundingRectangle, clientX: number): BoundingRectangle {

View File

@@ -15,9 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
import { Subject } from 'rxjs';
import { ResizableDirective } from './resizable.directive'; 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({ @Directive({
selector: '[adf-resize-handle]', selector: '[adf-resize-handle]',
@@ -32,9 +31,6 @@ export class ResizeHandleDirective implements OnInit, OnDestroy {
private unlistenMouseDown?: () => void; private unlistenMouseDown?: () => void;
private unlistenMouseMove?: () => void; private unlistenMouseMove?: () => void;
private unlistenMouseUp?: () => void; private unlistenMouseUp?: () => void;
private destroy$ = new Subject<void>();
constructor(private readonly renderer: Renderer2, private readonly element: ElementRef, private readonly zone: NgZone) {} constructor(private readonly renderer: Renderer2, private readonly element: ElementRef, private readonly zone: NgZone) {}
ngOnInit(): void { ngOnInit(): void {
@@ -49,7 +45,6 @@ export class ResizeHandleDirective implements OnInit, OnDestroy {
this.unlistenMouseDown?.(); this.unlistenMouseDown?.();
this.unlistenMouseMove?.(); this.unlistenMouseMove?.();
this.unlistenMouseUp?.(); this.unlistenMouseUp?.();
this.destroy$.next();
} }
private onMousedown(event: MouseEvent): void { private onMousedown(event: MouseEvent): void {

View File

@@ -15,16 +15,16 @@
* limitations under the License. * 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 { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { AdditionalDialogActionButton, DialogData } from './dialog-data.interface'; import { AdditionalDialogActionButton, DialogData } from './dialog-data.interface';
import { BehaviorSubject, Subject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { DialogSize, DialogSizes } from './dialog.model'; import { DialogSize, DialogSizes } from './dialog.model';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { takeUntil } from 'rxjs/operators';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export const DIALOG_COMPONENT_DATA = new InjectionToken<any>('dialog component data'); export const DIALOG_COMPONENT_DATA = new InjectionToken<any>('dialog component data');
@@ -36,7 +36,7 @@ export const DIALOG_COMPONENT_DATA = new InjectionToken<any>('dialog component d
imports: [CommonModule, TranslateModule, MatIconModule, MatDialogModule, MatButtonModule], imports: [CommonModule, TranslateModule, MatIconModule, MatDialogModule, MatButtonModule],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class DialogComponent implements OnDestroy { export class DialogComponent {
isConfirmButtonDisabled$ = new BehaviorSubject<boolean>(false); isConfirmButtonDisabled$ = new BehaviorSubject<boolean>(false);
isCloseButtonHidden: boolean; isCloseButtonHidden: boolean;
isCancelButtonHidden: boolean; isCancelButtonHidden: boolean;
@@ -48,8 +48,6 @@ export class DialogComponent implements OnDestroy {
dataInjector: Injector; dataInjector: Injector;
private onDestroy$ = new Subject<void>();
constructor( constructor(
@Inject(MAT_DIALOG_DATA) @Inject(MAT_DIALOG_DATA)
public data: DialogData, public data: DialogData,
@@ -68,11 +66,11 @@ export class DialogComponent implements OnDestroy {
}); });
if (data.isConfirmButtonDisabled$) { 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$) { 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.isConfirmButtonDisabled$.next(true);
this.dialogRef.close(this.dataOnConfirm || true); this.dialogRef.close(this.dataOnConfirm || true);
} }
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
}
} }

View File

@@ -15,10 +15,9 @@
* limitations under the License. * 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 { MatSelect } from '@angular/material/select';
import { Subject } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { takeUntil } from 'rxjs/operators';
const SELECT_ITEM_HEIGHT_EM = 3; const SELECT_ITEM_HEIGHT_EM = 3;
@@ -26,19 +25,19 @@ const SELECT_ITEM_HEIGHT_EM = 3;
selector: '[adf-infinite-select-scroll]', selector: '[adf-infinite-select-scroll]',
standalone: true standalone: true
}) })
export class InfiniteSelectScrollDirective implements AfterViewInit, OnDestroy { export class InfiniteSelectScrollDirective implements AfterViewInit {
static readonly MAX_ITEMS = 50; static readonly MAX_ITEMS = 50;
/** Emitted when scroll reaches the last item. */ /** Emitted when scroll reaches the last item. */
@Output() scrollEnd = new EventEmitter<Event>(); @Output() scrollEnd = new EventEmitter<Event>();
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
private itemHeightToWaitBeforeLoadNext = 0; private itemHeightToWaitBeforeLoadNext = 0;
constructor(@Inject(MatSelect) private matSelect: MatSelect) {} constructor(@Inject(MatSelect) private matSelect: MatSelect) {}
ngAfterViewInit() { ngAfterViewInit() {
this.matSelect.openedChange.pipe(takeUntil(this.onDestroy$)).subscribe((opened: boolean) => { this.matSelect.openedChange.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((opened: boolean) => {
if (opened) { if (opened) {
this.itemHeightToWaitBeforeLoadNext = this.getItemHeight() * (InfiniteSelectScrollDirective.MAX_ITEMS / 2); this.itemHeightToWaitBeforeLoadNext = this.getItemHeight() * (InfiniteSelectScrollDirective.MAX_ITEMS / 2);
this.matSelect.panel.nativeElement.addEventListener('scroll', (event: Event) => this.handleScrollEvent(event)); 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) { private handleScrollEvent(event: Event) {
if (this.isScrollInNextFetchArea(event)) { if (this.isScrollInNextFetchArea(event)) {
this.scrollEnd.emit(event); this.scrollEnd.emit(event);

View File

@@ -38,7 +38,6 @@ import { MatButtonModule } from '@angular/material/button';
import { MatChip, MatChipsModule } from '@angular/material/chips'; import { MatChip, MatChipsModule } from '@angular/material/chips';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { Subject } from 'rxjs';
import { Chip } from './chip'; import { Chip } from './chip';
/** /**
@@ -98,7 +97,6 @@ export class DynamicChipListComponent implements OnChanges, OnInit, AfterViewIni
paginationData: Pagination; paginationData: Pagination;
private initialChips: Chip[] = []; private initialChips: Chip[] = [];
private onDestroy$ = new Subject<boolean>();
private initialLimitChipsDisplayed: boolean; private initialLimitChipsDisplayed: boolean;
private viewMoreButtonLeftOffsetBeforeFlexDirection: number; private viewMoreButtonLeftOffsetBeforeFlexDirection: number;
private requestedDisplayingAllChips = false; private requestedDisplayingAllChips = false;
@@ -139,8 +137,6 @@ export class DynamicChipListComponent implements OnChanges, OnInit, AfterViewIni
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.onDestroy$.next(true);
this.onDestroy$.complete();
this.resizeObserver.unobserve(this.containerView.nativeElement); this.resizeObserver.unobserve(this.containerView.nativeElement);
} }

View File

@@ -18,20 +18,25 @@
/* eslint-disable @angular-eslint/component-selector */ /* eslint-disable @angular-eslint/component-selector */
import { NgIf } from '@angular/common'; 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 { FormControl, ReactiveFormsModule, ValidationErrors, Validators } from '@angular/forms';
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { DatetimeAdapter, MAT_DATETIME_FORMATS, MatDatetimepickerModule } from '@mat-datetimepicker/core'; import { DatetimeAdapter, MAT_DATETIME_FORMATS, MatDatetimepickerModule } from '@mat-datetimepicker/core';
import { TranslateModule } from '@ngx-translate/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 { FormService } from '../../../services/form.service';
import { ErrorWidgetComponent } from '../error/error.component'; import { ErrorWidgetComponent } from '../error/error.component';
import { WidgetComponent } from '../widget.component'; import { WidgetComponent } from '../widget.component';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { ErrorMessageModel } from '../core/error-message.model'; import { ErrorMessageModel } from '../core/error-message.model';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'date-time-widget', selector: 'date-time-widget',
@@ -47,14 +52,15 @@ import { ErrorMessageModel } from '../core/error-message.model';
imports: [NgIf, TranslateModule, MatFormFieldModule, MatInputModule, MatDatetimepickerModule, ReactiveFormsModule, ErrorWidgetComponent], imports: [NgIf, TranslateModule, MatFormFieldModule, MatInputModule, MatDatetimepickerModule, ReactiveFormsModule, ErrorWidgetComponent],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class DateTimeWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { export class DateTimeWidgetComponent extends WidgetComponent implements OnInit {
minDate: Date; minDate: Date;
maxDate: Date; maxDate: Date;
datetimeInputControl: FormControl<Date> = new FormControl<Date>(null); datetimeInputControl: FormControl<Date> = new FormControl<Date>(null);
private onDestroy$ = new Subject<void>();
public readonly formService = inject(FormService); public readonly formService = inject(FormService);
private readonly destroyRef = inject(DestroyRef);
private readonly dateAdapter = inject(DateAdapter); private readonly dateAdapter = inject(DateAdapter);
private readonly dateTimeAdapter = inject(DatetimeAdapter); private readonly dateTimeAdapter = inject(DatetimeAdapter);
@@ -82,7 +88,7 @@ export class DateTimeWidgetComponent extends WidgetComponent implements OnInit,
} }
private subscribeToDateChanges(): void { 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.field.value = newDate;
this.updateField(); this.updateField();
}); });
@@ -151,9 +157,4 @@ export class DateTimeWidgetComponent extends WidgetComponent implements OnInit,
this.maxDate = DateFnsUtils.getDate(this.field.maxValue); this.maxDate = DateFnsUtils.getDate(this.field.maxValue);
} }
} }
ngOnDestroy(): void {
this.onDestroy$.next();
this.onDestroy$.complete();
}
} }

View File

@@ -18,21 +18,20 @@
/* eslint-disable @angular-eslint/component-selector */ /* eslint-disable @angular-eslint/component-selector */
import { NgIf } from '@angular/common'; 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 { FormControl, ReactiveFormsModule, ValidationErrors, Validators } from '@angular/forms';
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { TranslateModule } from '@ngx-translate/core'; 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 { ADF_DATE_FORMATS, AdfDateFnsAdapter, DateFnsUtils, DEFAULT_DATE_FORMAT } from '../../../../common';
import { FormService } from '../../../services/form.service'; import { FormService } from '../../../services/form.service';
import { ErrorWidgetComponent } from '../error/error.component'; import { ErrorWidgetComponent } from '../error/error.component';
import { WidgetComponent } from '../widget.component'; import { WidgetComponent } from '../widget.component';
import { ErrorMessageModel } from '../core/error-message.model'; import { ErrorMessageModel } from '../core/error-message.model';
import { parseISO } from 'date-fns'; import { parseISO } from 'date-fns';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'date-widget', selector: 'date-widget',
@@ -56,17 +55,17 @@ import { parseISO } from 'date-fns';
imports: [MatFormFieldModule, TranslateModule, MatInputModule, MatDatepickerModule, ReactiveFormsModule, ErrorWidgetComponent, NgIf], imports: [MatFormFieldModule, TranslateModule, MatInputModule, MatDatepickerModule, ReactiveFormsModule, ErrorWidgetComponent, NgIf],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class DateWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { export class DateWidgetComponent extends WidgetComponent implements OnInit {
minDate: Date; minDate: Date;
maxDate: Date; maxDate: Date;
startAt: Date; startAt: Date;
dateInputControl: FormControl<Date> = new FormControl<Date>(null); dateInputControl: FormControl<Date> = new FormControl<Date>(null);
private onDestroy$ = new Subject<void>();
public readonly formService = inject(FormService); public readonly formService = inject(FormService);
private readonly dateAdapter = inject(DateAdapter); private readonly dateAdapter = inject(DateAdapter);
private readonly destroyRef = inject(DestroyRef);
ngOnInit(): void { ngOnInit(): void {
this.patchFormControl(); this.patchFormControl();
@@ -92,7 +91,7 @@ export class DateWidgetComponent extends WidgetComponent implements OnInit, OnDe
} }
private subscribeToDateChanges(): void { 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.field.value = newDate;
this.updateField(); 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); this.startAt = this.dateAdapter.parse(this.field.value, DEFAULT_DATE_FORMAT);
} }
} }
ngOnDestroy(): void {
this.onDestroy$.next();
this.onDestroy$.complete();
}
} }

View File

@@ -15,10 +15,9 @@
* limitations under the License. * 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 { MatMenuModule, MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu';
import { IdentityUserModel } from '../auth/models/identity-user.model'; import { IdentityUserModel } from '../auth/models/identity-user.model';
import { Subject } from 'rxjs';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { FullNamePipe, InitialUsernamePipe } from '../pipes'; import { FullNamePipe, InitialUsernamePipe } from '../pipes';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@@ -33,7 +32,7 @@ import { TranslateModule } from '@ngx-translate/core';
styleUrls: ['./identity-user-info.component.scss'], styleUrls: ['./identity-user-info.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class IdentityUserInfoComponent implements OnDestroy { export class IdentityUserInfoComponent {
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger; @ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
/** Is the user logged in */ /** Is the user logged in */
@@ -67,13 +66,6 @@ export class IdentityUserInfoComponent implements OnDestroy {
@Input() @Input()
namePosition: 'right' | 'left' = 'right'; namePosition: 'right' | 'left' = 'right';
private destroy$ = new Subject();
ngOnDestroy(): void {
this.destroy$.next(true);
this.destroy$.complete();
}
onKeyPress(event: KeyboardEvent) { onKeyPress(event: KeyboardEvent) {
this.closeUserModal(event); this.closeUserModal(event);
} }

View File

@@ -16,29 +16,31 @@
*/ */
import { import {
AfterViewInit,
ChangeDetectorRef,
Component, Component,
ContentChild, ContentChild,
Input, DestroyRef,
Output,
OnInit,
AfterViewInit,
ViewChild,
OnDestroy,
TemplateRef,
EventEmitter, EventEmitter,
ViewEncapsulation, inject,
ChangeDetectorRef Input,
OnDestroy,
OnInit,
Output,
TemplateRef,
ViewChild,
ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { MediaMatcher } from '@angular/cdk/layout'; import { MediaMatcher } from '@angular/cdk/layout';
import { UserPreferencesService } from '../../../common/services/user-preferences.service'; import { UserPreferencesService } from '../../../common/services/user-preferences.service';
import { SidenavLayoutContentDirective } from '../../directives/sidenav-layout-content.directive'; import { SidenavLayoutContentDirective } from '../../directives/sidenav-layout-content.directive';
import { SidenavLayoutHeaderDirective } from '../../directives/sidenav-layout-header.directive'; import { SidenavLayoutHeaderDirective } from '../../directives/sidenav-layout-header.directive';
import { SidenavLayoutNavigationDirective } from '../../directives/sidenav-layout-navigation.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 { Direction } from '@angular/cdk/bidi';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { LayoutContainerComponent } from '../layout-container/layout-container.component'; import { LayoutContainerComponent } from '../layout-container/layout-container.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-sidenav-layout', selector: 'adf-sidenav-layout',
@@ -99,7 +101,7 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy
isMenuMinimized: () => this.isMenuMinimized isMenuMinimized: () => this.isMenuMinimized
}; };
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private readonly mediaMatcher: MediaMatcher, private readonly mediaMatcher: MediaMatcher,
@@ -120,7 +122,7 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy
this.userPreferencesService this.userPreferencesService
.select('textOrientation') .select('textOrientation')
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((direction: Direction) => { .subscribe((direction: Direction) => {
this.dir = direction; this.dir = direction;
}); });
@@ -132,8 +134,6 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy
ngOnDestroy(): void { ngOnDestroy(): void {
this.mediaQueryList.removeListener(this.onMediaQueryChange); this.mediaQueryList.removeListener(this.onMediaQueryChange);
this.onDestroy$.next(true);
this.onDestroy$.complete();
} }
toggleMenu() { toggleMenu() {

View File

@@ -16,7 +16,7 @@
*/ */
import { CommonModule } from '@angular/common'; 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 { AbstractControl, ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
@@ -27,8 +27,6 @@ import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { ActivatedRoute, Params, Router } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { AppConfigService, AppConfigValues } from '../../../app-config'; import { AppConfigService, AppConfigValues } from '../../../app-config';
import { AuthenticationService, BasicAlfrescoAuthService } from '../../../auth'; import { AuthenticationService, BasicAlfrescoAuthService } from '../../../auth';
import { OidcAuthenticationService } from '../../../auth/oidc/oidc-authentication.service'; import { OidcAuthenticationService } from '../../../auth/oidc/oidc-authentication.service';
@@ -38,6 +36,7 @@ import { TranslationService } from '../../../translation';
import { LoginErrorEvent } from '../../models/login-error.event'; import { LoginErrorEvent } from '../../models/login-error.event';
import { LoginSubmitEvent } from '../../models/login-submit.event'; import { LoginSubmitEvent } from '../../models/login-submit.event';
import { LoginSuccessEvent } from '../../models/login-success.event'; import { LoginSuccessEvent } from '../../models/login-success.event';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
// eslint-disable-next-line no-shadow // eslint-disable-next-line no-shadow
enum LoginSteps { enum LoginSteps {
@@ -76,7 +75,7 @@ interface LoginFormValues {
], ],
host: { class: 'adf-login' } host: { class: 'adf-login' }
}) })
export class LoginComponent implements OnInit, OnDestroy { export class LoginComponent implements OnInit {
isPasswordShow: boolean = false; isPasswordShow: boolean = false;
/** /**
@@ -146,7 +145,8 @@ export class LoginComponent implements OnInit, OnDestroy {
data: any; data: any;
private _message: { [id: string]: { [id: string]: ValidationMessage } }; private _message: { [id: string]: { [id: string]: ValidationMessage } };
private onDestroy$ = new Subject<boolean>();
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private _fb: UntypedFormBuilder, private _fb: UntypedFormBuilder,
@@ -191,12 +191,7 @@ export class LoginComponent implements OnInit, OnDestroy {
this.form = this._fb.group(this.fieldsValidation); this.form = this._fb.group(this.fieldsValidation);
} }
this.form.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((data) => this.onValueChanged(data)); this.form.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data) => this.onValueChanged(data));
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
} }
submit() { submit() {

View File

@@ -15,12 +15,20 @@
* limitations under the License. * 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 { NotificationService } from '../services/notification.service';
import { NOTIFICATION_TYPE, NotificationModel } from '../models/notification.model'; import { NOTIFICATION_TYPE, NotificationModel } from '../models/notification.model';
import { MatMenuModule, MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu'; 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 { StorageService } from '../../common/services/storage.service';
import { PaginationModel } from '../../models/pagination.model'; import { PaginationModel } from '../../models/pagination.model';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@@ -31,6 +39,7 @@ import { MatListModule } from '@angular/material/list';
import { NgForOf, NgIf } from '@angular/common'; import { NgForOf, NgIf } from '@angular/common';
import { InitialUsernamePipe, TimeAgoPipe } from '../../pipes'; import { InitialUsernamePipe, TimeAgoPipe } from '../../pipes';
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-notification-history', selector: 'adf-notification-history',
@@ -52,7 +61,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
], ],
encapsulation: ViewEncapsulation.None 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 MAX_NOTIFICATION_STACK_LENGTH = 100;
public static NOTIFICATION_STORAGE = 'notification-history'; public static NOTIFICATION_STORAGE = 'notification-history';
@@ -71,11 +80,12 @@ export class NotificationHistoryComponent implements OnDestroy, OnInit, AfterVie
@Input() @Input()
maxNotifications: number = 5; maxNotifications: number = 5;
onDestroy$ = new Subject<boolean>();
notifications: NotificationModel[] = []; notifications: NotificationModel[] = [];
paginatedNotifications: NotificationModel[] = []; paginatedNotifications: NotificationModel[] = [];
pagination: PaginationModel; pagination: PaginationModel;
private readonly destroyRef = inject(DestroyRef);
constructor(private notificationService: NotificationService, public storageService: StorageService, public cd: ChangeDetectorRef) {} constructor(private notificationService: NotificationService, public storageService: StorageService, public cd: ChangeDetectorRef) {}
ngOnInit() { ngOnInit() {
@@ -83,17 +93,12 @@ export class NotificationHistoryComponent implements OnDestroy, OnInit, AfterVie
} }
ngAfterViewInit(): void { 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.addNewNotification(notification);
this.cd.detectChanges(); this.cd.detectChanges();
}); });
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
addNewNotification(notification: NotificationModel) { addNewNotification(notification: NotificationModel) {
this.notifications.unshift(notification); this.notifications.unshift(notification);

View File

@@ -22,25 +22,25 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
DestroyRef,
EventEmitter, EventEmitter,
inject,
Input, Input,
OnInit, OnInit,
Output, Output,
OnDestroy,
ViewEncapsulation ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { PaginatedComponent } from './paginated-component.interface'; import { PaginatedComponent } from './paginated-component.interface';
import { Subject } from 'rxjs';
import { PaginationComponentInterface } from './pagination-component.interface'; import { PaginationComponentInterface } from './pagination-component.interface';
import { RequestPaginationModel } from '../models/request-pagination.model'; import { RequestPaginationModel } from '../models/request-pagination.model';
import { UserPreferencesService, UserPreferenceValues } from '../common/services/user-preferences.service'; import { UserPreferencesService, UserPreferenceValues } from '../common/services/user-preferences.service';
import { PaginationModel } from '../models/pagination.model'; import { PaginationModel } from '../models/pagination.model';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatProgressBarModule } from '@angular/material/progress-bar';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-infinite-pagination', selector: 'adf-infinite-pagination',
@@ -52,7 +52,7 @@ import { TranslateModule } from '@ngx-translate/core';
standalone: true, standalone: true,
imports: [CommonModule, MatButtonModule, MatProgressBarModule, TranslateModule] imports: [CommonModule, MatButtonModule, MatProgressBarModule, TranslateModule]
}) })
export class InfinitePaginationComponent implements OnInit, OnDestroy, PaginationComponentInterface { export class InfinitePaginationComponent implements OnInit, PaginationComponentInterface {
static DEFAULT_PAGINATION: PaginationModel = new PaginationModel({ static DEFAULT_PAGINATION: PaginationModel = new PaginationModel({
skipCount: 0, skipCount: 0,
maxItems: 25, maxItems: 25,
@@ -60,14 +60,13 @@ export class InfinitePaginationComponent implements OnInit, OnDestroy, Paginatio
}); });
_target: PaginatedComponent; _target: PaginatedComponent;
private onDestroy$ = new Subject<boolean>();
/** Component that provides custom pagination support. */ /** Component that provides custom pagination support. */
@Input() @Input()
set target(target: PaginatedComponent) { set target(target: PaginatedComponent) {
if (target) { if (target) {
this._target = target; this._target = target;
target.pagination.pipe(takeUntil(this.onDestroy$)).subscribe((pagination) => { target.pagination.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((pagination) => {
this.isLoading = false; this.isLoading = false;
this.pagination = pagination; this.pagination = pagination;
@@ -103,12 +102,14 @@ export class InfinitePaginationComponent implements OnInit, OnDestroy, Paginatio
merge: true merge: true
}; };
private readonly destroyRef = inject(DestroyRef);
constructor(private cdr: ChangeDetectorRef, private userPreferencesService: UserPreferencesService) {} constructor(private cdr: ChangeDetectorRef, private userPreferencesService: UserPreferencesService) {}
ngOnInit() { ngOnInit() {
this.userPreferencesService this.userPreferencesService
.select(UserPreferenceValues.PaginationSize) .select(UserPreferenceValues.PaginationSize)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((pageSize: number) => { .subscribe((pageSize: number) => {
this.pageSize = this.pageSize || pageSize; this.pageSize = this.pageSize || pageSize;
this.requestPaginationModel.maxItems = this.pageSize; this.requestPaginationModel.maxItems = this.pageSize;
@@ -137,9 +138,4 @@ export class InfinitePaginationComponent implements OnInit, OnDestroy, Paginatio
this._target.updatePagination(this.pagination); this._target.updatePagination(this.pagination);
} }
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -16,29 +16,29 @@
*/ */
import { import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component, Component,
DestroyRef,
ElementRef,
EventEmitter, EventEmitter,
inject,
Input, Input,
OnInit, OnInit,
Output, Output,
ViewEncapsulation, Renderer2,
OnDestroy, ViewEncapsulation
ElementRef,
ChangeDetectionStrategy,
ChangeDetectorRef,
Renderer2
} from '@angular/core'; } from '@angular/core';
import { PaginatedComponent } from './paginated-component.interface'; import { PaginatedComponent } from './paginated-component.interface';
import { PaginationComponentInterface } from './pagination-component.interface'; import { PaginationComponentInterface } from './pagination-component.interface';
import { Subject } from 'rxjs';
import { PaginationModel } from '../models/pagination.model'; import { PaginationModel } from '../models/pagination.model';
import { UserPreferencesService, UserPreferenceValues } from '../common/services/user-preferences.service'; import { UserPreferencesService, UserPreferenceValues } from '../common/services/user-preferences.service';
import { takeUntil } from 'rxjs/operators';
import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu'; 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'; export type PaginationAction = 'NEXT_PAGE' | 'PREV_PAGE' | 'CHANGE_PAGE_SIZE' | 'CHANGE_PAGE_NUMBER';
@@ -60,7 +60,7 @@ export const DEFAULT_PAGINATION: PaginationModel = {
standalone: true, standalone: true,
imports: [CommonModule, TranslateModule, MatButtonModule, MatIconModule, MatMenuModule] imports: [CommonModule, TranslateModule, MatButtonModule, MatIconModule, MatMenuModule]
}) })
export class PaginationComponent implements OnInit, OnDestroy, PaginationComponentInterface { export class PaginationComponent implements OnInit, PaginationComponentInterface {
private _pagination: PaginationModel; private _pagination: PaginationModel;
private _isEmpty = true; private _isEmpty = true;
private _hasItems = false; private _hasItems = false;
@@ -117,7 +117,7 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone
@Output() @Output()
prevPage = new EventEmitter<PaginationModel>(); prevPage = new EventEmitter<PaginationModel>();
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private elementRef: ElementRef, private elementRef: ElementRef,
@@ -130,7 +130,7 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone
ngOnInit() { ngOnInit() {
this.userPreferencesService this.userPreferencesService
.select(UserPreferenceValues.PaginationSize) .select(UserPreferenceValues.PaginationSize)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((maxItems) => { .subscribe((maxItems) => {
this.pagination = { this.pagination = {
...DEFAULT_PAGINATION, ...DEFAULT_PAGINATION,
@@ -144,7 +144,7 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone
} }
if (this.target) { 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) { if (pagination.count === 0 && !this.isFirstPage) {
this.goPrevious(); this.goPrevious();
} }
@@ -303,12 +303,6 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone
this.userPreferencesService.paginationSize = maxItems; this.userPreferencesService.paginationSize = maxItems;
this.handlePaginationEvent('CHANGE_PAGE_SIZE'); this.handlePaginationEvent('CHANGE_PAGE_SIZE');
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
handlePaginationEvent(action: PaginationAction) { handlePaginationEvent(action: PaginationAction) {
const paginationModel = { ...this.pagination }; const paginationModel = { ...this.pagination };

View File

@@ -16,11 +16,12 @@
*/ */
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { AppConfigService } from '../app-config/app-config.service';
import { UserPreferencesService } from '../common/services/user-preferences.service'; import { UserPreferencesService } from '../common/services/user-preferences.service';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { CoreTestingModule } from '../testing/core.testing.module'; import { CoreTestingModule } from '../testing/core.testing.module';
import { DecimalNumberPipe } from './decimal-number.pipe'; import { DecimalNumberPipe } from './decimal-number.pipe';
import { Injector, runInInjectionContext } from '@angular/core';
import { AppConfigService } from '@alfresco/adf-core';
describe('DecimalNumberPipe', () => { describe('DecimalNumberPipe', () => {
let pipe: DecimalNumberPipe; let pipe: DecimalNumberPipe;
@@ -31,8 +32,11 @@ describe('DecimalNumberPipe', () => {
imports: [CoreTestingModule] imports: [CoreTestingModule]
}); });
userPreferences = TestBed.inject(UserPreferencesService); userPreferences = TestBed.inject(UserPreferencesService);
const injector = TestBed.inject(Injector);
spyOn(userPreferences, 'select').and.returnValue(of('')); 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', () => { it('should return number localized and rounded following the default config', () => {

View File

@@ -16,19 +16,18 @@
*/ */
import { DecimalPipe } from '@angular/common'; 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 { AppConfigService } from '../app-config/app-config.service';
import { UserPreferencesService, UserPreferenceValues } from '../common/services/user-preferences.service'; import { UserPreferencesService, UserPreferenceValues } from '../common/services/user-preferences.service';
import { DecimalNumberModel } from '../models/decimal-number.model'; import { DecimalNumberModel } from '../models/decimal-number.model';
import { Subject } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { takeUntil } from 'rxjs/operators';
@Pipe({ @Pipe({
name: 'adfDecimalNumber', name: 'adfDecimalNumber',
pure: false, pure: false,
standalone: true standalone: true
}) })
export class DecimalNumberPipe implements PipeTransform, OnDestroy { export class DecimalNumberPipe implements PipeTransform {
static DEFAULT_LOCALE = 'en-US'; static DEFAULT_LOCALE = 'en-US';
static DEFAULT_MIN_INTEGER_DIGITS = 1; static DEFAULT_MIN_INTEGER_DIGITS = 1;
static DEFAULT_MIN_FRACTION_DIGITS = 0; static DEFAULT_MIN_FRACTION_DIGITS = 0;
@@ -38,14 +37,11 @@ export class DecimalNumberPipe implements PipeTransform, OnDestroy {
defaultMinIntegerDigits: number = DecimalNumberPipe.DEFAULT_MIN_INTEGER_DIGITS; defaultMinIntegerDigits: number = DecimalNumberPipe.DEFAULT_MIN_INTEGER_DIGITS;
defaultMinFractionDigits: number = DecimalNumberPipe.DEFAULT_MIN_FRACTION_DIGITS; defaultMinFractionDigits: number = DecimalNumberPipe.DEFAULT_MIN_FRACTION_DIGITS;
defaultMaxFractionDigits: number = DecimalNumberPipe.DEFAULT_MAX_FRACTION_DIGITS; defaultMaxFractionDigits: number = DecimalNumberPipe.DEFAULT_MAX_FRACTION_DIGITS;
onDestroy$: Subject<boolean> = new Subject<boolean>();
constructor(public userPreferenceService?: UserPreferencesService, public appConfig?: AppConfigService) { constructor(public userPreferenceService?: UserPreferencesService, public appConfig?: AppConfigService) {
if (this.userPreferenceService) { if (this.userPreferenceService) {
this.userPreferenceService this.userPreferenceService
.select(UserPreferenceValues.Locale) .select(UserPreferenceValues.Locale)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed())
.subscribe((locale) => { .subscribe((locale) => {
if (locale) { if (locale) {
this.defaultLocale = locale; this.defaultLocale = locale;
@@ -82,9 +78,4 @@ export class DecimalNumberPipe implements PipeTransform, OnDestroy {
return decimalPipe.transform(value, actualDigitsInfo); return decimalPipe.transform(value, actualDigitsInfo);
} }
} }
ngOnDestroy(): void {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -28,7 +28,6 @@ import { takeUntil } from 'rxjs/operators';
pure: false pure: false
}) })
export class LocalizedDatePipe implements PipeTransform, OnDestroy { export class LocalizedDatePipe implements PipeTransform, OnDestroy {
static DEFAULT_LOCALE = 'en-US'; static DEFAULT_LOCALE = 'en-US';
static DEFAULT_DATE_FORMAT = 'mediumDate'; static DEFAULT_DATE_FORMAT = 'mediumDate';
@@ -37,14 +36,12 @@ export class LocalizedDatePipe implements PipeTransform, OnDestroy {
private onDestroy$ = new Subject<boolean>(); private onDestroy$ = new Subject<boolean>();
constructor(public userPreferenceService?: UserPreferencesService, constructor(public userPreferenceService?: UserPreferencesService, public appConfig?: AppConfigService) {
public appConfig?: AppConfigService) {
if (this.userPreferenceService) { if (this.userPreferenceService) {
this.userPreferenceService this.userPreferenceService
.select(UserPreferenceValues.Locale) .select(UserPreferenceValues.Locale)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntil(this.onDestroy$))
.subscribe(locale => { .subscribe((locale) => {
if (locale) { if (locale) {
this.defaultLocale = locale; this.defaultLocale = locale;
} }
@@ -67,5 +64,4 @@ export class LocalizedDatePipe implements PipeTransform, OnDestroy {
this.onDestroy$.next(true); this.onDestroy$.next(true);
this.onDestroy$.complete(); this.onDestroy$.complete();
} }
} }

View File

@@ -21,6 +21,7 @@ import { AppConfigService } from '../app-config/app-config.service';
import { UserPreferencesService } from '../common/services/user-preferences.service'; import { UserPreferencesService } from '../common/services/user-preferences.service';
import { CoreTestingModule } from '../testing/core.testing.module'; import { CoreTestingModule } from '../testing/core.testing.module';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { Injector, runInInjectionContext } from '@angular/core';
describe('TimeAgoPipe', () => { describe('TimeAgoPipe', () => {
let pipe: TimeAgoPipe; let pipe: TimeAgoPipe;
@@ -31,8 +32,11 @@ describe('TimeAgoPipe', () => {
imports: [CoreTestingModule] imports: [CoreTestingModule]
}); });
userPreferences = TestBed.inject(UserPreferencesService); userPreferences = TestBed.inject(UserPreferencesService);
const injector = TestBed.inject(Injector);
spyOn(userPreferences, 'select').and.returnValue(of('')); 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', () => { it('should return time difference for a given date', () => {

View File

@@ -15,58 +15,46 @@
* limitations under the License. * 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 { 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 { DatePipe } from '@angular/common';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { differenceInDays, formatDistance } from 'date-fns'; import { differenceInDays, formatDistance } from 'date-fns';
import * as Locales from 'date-fns/locale'; import * as Locales from 'date-fns/locale';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Pipe({ @Pipe({
standalone: true, standalone: true,
name: 'adfTimeAgo' name: 'adfTimeAgo'
}) })
export class TimeAgoPipe implements PipeTransform, OnDestroy { export class TimeAgoPipe implements PipeTransform {
static DEFAULT_LOCALE = 'en-US'; static DEFAULT_LOCALE = 'en-US';
static DEFAULT_DATE_TIME_FORMAT = 'dd/MM/yyyy HH:mm'; static DEFAULT_DATE_TIME_FORMAT = 'dd/MM/yyyy HH:mm';
defaultLocale: string; defaultLocale: string;
defaultDateTimeFormat: string; defaultDateTimeFormat: string;
private onDestroy$ = new Subject<boolean>(); constructor(public userPreferenceService: UserPreferencesService, public appConfig: AppConfigService) {
constructor(
public userPreferenceService: UserPreferencesService,
public appConfig: AppConfigService
) {
this.userPreferenceService this.userPreferenceService
.select(UserPreferenceValues.Locale) .select(UserPreferenceValues.Locale)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed())
.subscribe(locale => { .subscribe((locale) => {
this.defaultLocale = locale || TimeAgoPipe.DEFAULT_LOCALE; this.defaultLocale = locale || TimeAgoPipe.DEFAULT_LOCALE;
}); });
this.defaultDateTimeFormat = this.appConfig.get<string>('dateValues.defaultDateTimeFormat', TimeAgoPipe.DEFAULT_DATE_TIME_FORMAT); this.defaultDateTimeFormat = this.appConfig.get<string>('dateValues.defaultDateTimeFormat', TimeAgoPipe.DEFAULT_DATE_TIME_FORMAT);
} }
transform(value: Date, locale?: string) { transform(value: Date, locale?: string) {
if (value !== null && value !== undefined ) { if (value !== null && value !== undefined) {
const actualLocale = locale || this.defaultLocale; const actualLocale = locale || this.defaultLocale;
const diff = differenceInDays(new Date(), new Date(value)); const diff = differenceInDays(new Date(), new Date(value));
if ( diff > 7) { if (diff > 7) {
const datePipe: DatePipe = new DatePipe(actualLocale); const datePipe: DatePipe = new DatePipe(actualLocale);
return datePipe.transform(value, this.defaultDateTimeFormat); return datePipe.transform(value, this.defaultDateTimeFormat);
} else { } 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 ''; return '';
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -17,7 +17,19 @@
import { Direction } from '@angular/cdk/bidi'; import { Direction } from '@angular/cdk/bidi';
import { NgClass, NgIf } from '@angular/common'; 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 { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
@@ -25,11 +37,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { Observable, Subject, Subscription } from 'rxjs'; import { Observable, Subject, Subscription } from 'rxjs';
import { debounceTime, filter, takeUntil } from 'rxjs/operators'; import { debounceTime, filter } from 'rxjs/operators';
import { UserPreferencesService } from '../common'; import { UserPreferencesService } from '../common';
import { searchAnimation } from './animations'; import { searchAnimation } from './animations';
import { SearchAnimationDirection, SearchAnimationState, SearchTextStateEnum } from './models/search-text-input.model'; import { SearchAnimationDirection, SearchAnimationState, SearchTextStateEnum } from './models/search-text-input.model';
import { SearchTriggerDirective } from './search-trigger.directive'; import { SearchTriggerDirective } from './search-trigger.directive';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-search-text-input', selector: 'adf-search-text-input',
@@ -162,7 +175,6 @@ export class SearchTextInputComponent implements OnInit, OnDestroy {
}; };
private dir = 'ltr'; private dir = 'ltr';
private onDestroy$ = new Subject<boolean>();
private toggleSearch = new Subject<any>(); private toggleSearch = new Subject<any>();
private focusSubscription: Subscription; private focusSubscription: Subscription;
private valueChange = new Subject<string>(); private valueChange = new Subject<string>();
@@ -170,8 +182,10 @@ export class SearchTextInputComponent implements OnInit, OnDestroy {
toggle$ = this.toggleSearch.asObservable(); toggle$ = this.toggleSearch.asObservable();
private readonly destroyRef = inject(DestroyRef);
constructor(private userPreferencesService: UserPreferencesService) { 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) { if (this.expandable) {
this.subscriptAnimationState = this.toggleAnimation(); this.subscriptAnimationState = this.toggleAnimation();
if (this.subscriptAnimationState.value === 'inactive') { if (this.subscriptAnimationState.value === 'inactive') {
@@ -189,7 +203,7 @@ export class SearchTextInputComponent implements OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.userPreferencesService this.userPreferencesService
.select('textOrientation') .select('textOrientation')
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((direction: Direction) => { .subscribe((direction: Direction) => {
this.dir = direction; this.dir = direction;
this.subscriptAnimationState = this.getDefaultState(this.dir); this.subscriptAnimationState = this.getDefaultState(this.dir);
@@ -246,7 +260,7 @@ export class SearchTextInputComponent implements OnInit, OnDestroy {
filter( filter(
($event: any) => this.isSearchBarActive() && ($event.type === 'blur' || $event.type === 'focusout' || $event.type === 'focus') ($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) => { this.focusSubscription = focusEvents.subscribe((event: FocusEvent) => {
@@ -260,7 +274,7 @@ export class SearchTextInputComponent implements OnInit, OnDestroy {
} }
private setValueChangeHandler() { 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); this.searchChange.emit(value);
}); });
} }
@@ -315,9 +329,6 @@ export class SearchTextInputComponent implements OnInit, OnDestroy {
this.focusSubscription = null; this.focusSubscription = null;
this.focusListener = null; this.focusListener = null;
} }
this.onDestroy$.next(true);
this.onDestroy$.complete();
} }
canShowClearSearch(): boolean { canShowClearSearch(): boolean {

View File

@@ -18,12 +18,13 @@
/* eslint-disable @angular-eslint/no-input-rename, @typescript-eslint/no-use-before-define, @angular-eslint/no-input-rename */ /* 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 { 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 { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { Observable, Subject, Subscription, merge, of, fromEvent } from 'rxjs'; import { fromEvent, merge, Observable, of, Subject, Subscription } from 'rxjs';
import { filter, switchMap, takeUntil } from 'rxjs/operators'; import { filter, switchMap } from 'rxjs/operators';
import { SearchComponentInterface } from '../common/interface/search-component.interface'; import { SearchComponentInterface } from '../common/interface/search-component.interface';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export const SEARCH_AUTOCOMPLETE_VALUE_ACCESSOR: any = { export const SEARCH_AUTOCOMPLETE_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
@@ -50,8 +51,6 @@ export const SEARCH_AUTOCOMPLETE_VALUE_ACCESSOR: any = {
providers: [SEARCH_AUTOCOMPLETE_VALUE_ACCESSOR] providers: [SEARCH_AUTOCOMPLETE_VALUE_ACCESSOR]
}) })
export class SearchTriggerDirective implements ControlValueAccessor, OnDestroy { export class SearchTriggerDirective implements ControlValueAccessor, OnDestroy {
private onDestroy$: Subject<boolean> = new Subject<boolean>();
@Input('searchAutocomplete') @Input('searchAutocomplete')
searchPanel: SearchComponentInterface; searchPanel: SearchComponentInterface;
@@ -66,6 +65,8 @@ export class SearchTriggerDirective implements ControlValueAccessor, OnDestroy {
onTouched = () => {}; onTouched = () => {};
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private element: ElementRef, private element: ElementRef,
private ngZone: NgZone, private ngZone: NgZone,
@@ -74,9 +75,6 @@ export class SearchTriggerDirective implements ControlValueAccessor, OnDestroy {
) {} ) {}
ngOnDestroy() { ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
if (this.escapeEventStream) { if (this.escapeEventStream) {
this.escapeEventStream = null; this.escapeEventStream = null;
} }
@@ -118,7 +116,7 @@ export class SearchTriggerDirective implements ControlValueAccessor, OnDestroy {
const clickTarget = event.target as HTMLElement; const clickTarget = event.target as HTMLElement;
return this._panelOpen && clickTarget !== this.element.nativeElement; 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(); this.searchPanel.setVisibility();
return this.panelClosingActions; return this.panelClosingActions;
}), }),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((event) => this.setValueAndClose(event)); .subscribe((event) => this.setValueAndClose(event));
} }

View File

@@ -134,7 +134,6 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
cMapPacked: true cMapPacked: true
}; };
private pdfjsWorkerDestroy$ = new Subject<boolean>(); private pdfjsWorkerDestroy$ = new Subject<boolean>();
private onDestroy$ = new Subject<boolean>();
constructor(private dialog: MatDialog, private renderingQueueServices: RenderingQueueServices, private appConfigService: AppConfigService) { constructor(private dialog: MatDialog, private renderingQueueServices: RenderingQueueServices, private appConfigService: AppConfigService) {
// needed to preserve "this" context // needed to preserve "this" context
@@ -276,9 +275,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
if (this.loadingTask) { if (this.loadingTask) {
this.pdfjsWorkerDestroy$.next(true); this.pdfjsWorkerDestroy$.next(true);
} }
this.onDestroy$.next(true);
this.pdfjsWorkerDestroy$.complete(); this.pdfjsWorkerDestroy$.complete();
this.onDestroy$.complete();
} }
private destroyPdJsWorker() { private destroyPdJsWorker() {

View File

@@ -15,13 +15,22 @@
* limitations under the License. * 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 { 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 { MatDialog } from '@angular/material/dialog';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { Subject } from 'rxjs';
import { Track } from '../../models/viewer.model'; import { Track } from '../../models/viewer.model';
import { ViewUtilService } from '../../services/view-util.service'; import { ViewUtilService } from '../../services/view-util.service';
import { ImgViewerComponent } from '../img-viewer/img-viewer.component'; import { ImgViewerComponent } from '../img-viewer/img-viewer.component';
@@ -55,7 +64,7 @@ import { UnknownFormatComponent } from '../unknown-format/unknown-format.compone
], ],
providers: [ViewUtilService] 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 * 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. * 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'; cacheTypeForContent = 'no-cache';
private onDestroy$ = new Subject<boolean>();
constructor( constructor(
private viewUtilService: ViewUtilService, private viewUtilService: ViewUtilService,
private extensionService: AppExtensionService, private extensionService: AppExtensionService,
@@ -182,11 +189,6 @@ export class ViewerRenderComponent implements OnChanges, OnInit, OnDestroy {
this.cacheTypeForContent = 'no-cache'; this.cacheTypeForContent = 'no-cache';
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
ngOnChanges() { ngOnChanges() {
this.isLoading = !this.blobFile && !this.urlFile; this.isLoading = !this.blobFile && !this.urlFile;

View File

@@ -20,6 +20,7 @@ import { NgIf, NgTemplateOutlet } from '@angular/common';
import { import {
Component, Component,
ContentChild, ContentChild,
DestroyRef,
ElementRef, ElementRef,
EventEmitter, EventEmitter,
HostListener, HostListener,
@@ -38,8 +39,8 @@ import { MatDialog } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { fromEvent, Subject } from 'rxjs'; import { fromEvent } from 'rxjs';
import { filter, first, skipWhile, takeUntil } from 'rxjs/operators'; import { filter, first, skipWhile } from 'rxjs/operators';
import { AppConfigService } from '../../app-config'; import { AppConfigService } from '../../app-config';
import { ToolbarComponent, ToolbarDividerComponent, ToolbarTitleComponent } from '../../toolbar'; import { ToolbarComponent, ToolbarDividerComponent, ToolbarTitleComponent } from '../../toolbar';
import { DownloadPromptActions } from '../models/download-prompt.actions'; 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 { ViewerToolbarCustomActionsComponent } from './viewer-toolbar-custom-actions.component';
import { IconComponent } from '../../icon'; import { IconComponent } from '../../icon';
import { ThumbnailService } from '../../common'; import { ThumbnailService } from '../../common';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
const DEFAULT_NON_PREVIEW_CONFIG = { const DEFAULT_NON_PREVIEW_CONFIG = {
enableDownloadPrompt: false, enableDownloadPrompt: false,
@@ -284,8 +286,6 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
@Output() @Output()
submitFile = new EventEmitter<Blob>(); submitFile = new EventEmitter<Blob>();
private onDestroy$ = new Subject<boolean>();
private closeViewer = true; private closeViewer = true;
private keyDown$ = fromEvent<KeyboardEvent>(document, 'keydown'); private keyDown$ = fromEvent<KeyboardEvent>(document, 'keydown');
private isDialogVisible: boolean = false; private isDialogVisible: boolean = false;
@@ -293,6 +293,8 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
public downloadPromptReminderTimer: number; public downloadPromptReminderTimer: number;
public mimeTypeIconUrl: string; public mimeTypeIconUrl: string;
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private el: ElementRef, private el: ElementRef,
public dialog: MatDialog, public dialog: MatDialog,
@@ -326,14 +328,14 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
this.dialog.afterOpened this.dialog.afterOpened
.pipe( .pipe(
skipWhile(() => !this.overlayMode), skipWhile(() => !this.overlayMode),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe(() => (this.closeViewer = false)); .subscribe(() => (this.closeViewer = false));
this.dialog.afterAllClosed this.dialog.afterAllClosed
.pipe( .pipe(
skipWhile(() => !this.overlayMode), skipWhile(() => !this.overlayMode),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe(() => (this.closeViewer = true)); .subscribe(() => (this.closeViewer = true));
@@ -341,7 +343,7 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
.pipe( .pipe(
skipWhile(() => !this.overlayMode), skipWhile(() => !this.overlayMode),
filter((e: KeyboardEvent) => e.keyCode === 27), filter((e: KeyboardEvent) => e.keyCode === 27),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((event: KeyboardEvent) => { .subscribe((event: KeyboardEvent) => {
event.preventDefault(); event.preventDefault();
@@ -429,8 +431,6 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
ngOnDestroy() { ngOnDestroy() {
this.clearDownloadPromptTimeouts(); this.clearDownloadPromptTimeouts();
this.onDestroy$.next(true);
this.onDestroy$.complete();
} }
private configureAndInitDownloadPrompt() { private configureAndInitDownloadPrompt() {

View File

@@ -15,16 +15,15 @@
* limitations under the License. * 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 { ViewerRenderComponent } from '../components/viewer-render/viewer-render.component';
import { Subject } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { takeUntil } from 'rxjs/operators';
@Directive({ @Directive({
selector: 'adf-viewer-extension', selector: 'adf-viewer-extension',
standalone: true standalone: true
}) })
export class ViewerExtensionDirective implements AfterContentInit, OnDestroy { export class ViewerExtensionDirective implements AfterContentInit {
@ContentChild(TemplateRef) @ContentChild(TemplateRef)
template: any; template: any;
@@ -39,7 +38,7 @@ export class ViewerExtensionDirective implements AfterContentInit, OnDestroy {
templateModel: any; templateModel: any;
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor(private viewerComponent: ViewerRenderComponent) {} constructor(private viewerComponent: ViewerRenderComponent) {}
@@ -48,16 +47,11 @@ export class ViewerExtensionDirective implements AfterContentInit, OnDestroy {
this.viewerComponent.extensionsSupportedByTemplates.push(...this.supportedExtensions); this.viewerComponent.extensionsSupportedByTemplates.push(...this.supportedExtensions);
this.viewerComponent.extensionTemplates.push(this.templateModel); 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); 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` * Check if the current extension in the viewer is compatible with this extension checking against `supportedExtensions`
* *

View File

@@ -19,10 +19,11 @@ import { ADF_DATE_FORMATS, AdfDateFnsAdapter, DownloadService, ToolbarComponent,
import { import {
AfterContentChecked, AfterContentChecked,
Component, Component,
DestroyRef,
EventEmitter, EventEmitter,
inject,
Input, Input,
OnChanges, OnChanges,
OnDestroy,
OnInit, OnInit,
Output, Output,
SimpleChanges, SimpleChanges,
@@ -35,8 +36,6 @@ import { ReportParameterDetailsModel } from '../../diagram/models/report/report-
import { ReportParametersModel } from '../../diagram/models/report/report-parameters.model'; import { ReportParametersModel } from '../../diagram/models/report/report-parameters.model';
import { ReportQuery } from '../../diagram/models/report/report-query.model'; import { ReportQuery } from '../../diagram/models/report/report-query.model';
import { AnalyticsService } from '../services/analytics.service'; import { AnalyticsService } from '../services/analytics.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
@@ -47,6 +46,7 @@ import { WIDGET_DIRECTIVES } from './widgets';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { ButtonsMenuComponent } from './buttons-menu/buttons-menu.component'; import { ButtonsMenuComponent } from './buttons-menu/buttons-menu.component';
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
const FORMAT_DATE_ACTIVITI = 'YYYY-MM-DD'; const FORMAT_DATE_ACTIVITI = 'YYYY-MM-DD';
@@ -133,7 +133,7 @@ export interface ReportFormValues {
styleUrls: ['./analytics-report-parameters.component.scss'], styleUrls: ['./analytics-report-parameters.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class AnalyticsReportParametersComponent implements OnInit, OnChanges, OnDestroy, AfterContentChecked { export class AnalyticsReportParametersComponent implements OnInit, OnChanges, AfterContentChecked {
/** appId ID of the target app. */ /** appId ID of the target app. */
@Input() @Input()
appId: number; appId: number;
@@ -186,7 +186,8 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On
formValidState: boolean = false; formValidState: boolean = false;
private hideParameters: boolean = true; private hideParameters: boolean = true;
private onDestroy$ = new Subject<boolean>();
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private analyticsService: AnalyticsService, private analyticsService: AnalyticsService,
@@ -197,14 +198,14 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On
) {} ) {}
ngOnInit() { 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); const paramDependOn = this.reportParameters.definition.parameters.find((param) => param.dependsOn === field.id);
if (paramDependOn) { if (paramDependOn) {
this.retrieveParameterOptions(this.reportParameters.definition.parameters, this.appId, this.reportId, field.value); 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()) { if (report.hasParameters()) {
this.retrieveParameterOptions(report.definition.parameters, this.appId); this.retrieveParameterOptions(report.definition.parameters, this.appId);
this.generateFormGroupFromParameter(report.definition.parameters); this.generateFormGroupFromParameter(report.definition.parameters);
@@ -310,11 +311,6 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On
return reportParamQuery; return reportParamQuery;
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
editEnable() { editEnable() {
this.isEditable = true; this.isEditable = true;
} }

View File

@@ -16,35 +16,35 @@
*/ */
import { import {
ChangeDetectorRef,
Component, Component,
DestroyRef,
EventEmitter, EventEmitter,
HostListener,
inject,
Input, Input,
OnChanges, OnChanges,
Output,
SimpleChanges,
OnDestroy,
HostListener,
OnInit, OnInit,
ChangeDetectorRef, Output,
inject SimpleChanges
} from '@angular/core'; } from '@angular/core';
import { Observable, of, forkJoin, Subject, Subscription } from 'rxjs'; import { forkJoin, Observable, of, Subscription } from 'rxjs';
import { switchMap, takeUntil, map, filter } from 'rxjs/operators'; import { filter, map, switchMap } from 'rxjs/operators';
import { import {
ConfirmDialogComponent,
ContentLinkModel,
FORM_FIELD_VALIDATORS,
FormBaseComponent, FormBaseComponent,
FormEvent,
FormFieldModel, FormFieldModel,
FormFieldValidator,
FormModel,
FormOutcomeEvent, FormOutcomeEvent,
FormOutcomeModel, FormOutcomeModel,
WidgetVisibilityService,
FormService, FormService,
FORM_FIELD_VALIDATORS,
FormFieldValidator,
FormValues, FormValues,
FormModel,
ContentLinkModel,
UploadWidgetContentLinkModel, UploadWidgetContentLinkModel,
FormEvent, WidgetVisibilityService
ConfirmDialogComponent
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { FormCloudService } from '../services/form-cloud.service'; import { FormCloudService } from '../services/form-cloud.service';
import { TaskVariableCloud } from '../models/task-variable-cloud.model'; 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 { FormCloudDisplayMode, FormCloudDisplayModeConfiguration } from '../../services/form-fields.interfaces';
import { FormCloudSpinnerService } from '../services/spinner/form-cloud-spinner.service'; import { FormCloudSpinnerService } from '../services/spinner/form-cloud-spinner.service';
import { DisplayModeService } from '../services/display-mode.service'; import { DisplayModeService } from '../services/display-mode.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-cloud-form', selector: 'adf-cloud-form',
templateUrl: './form-cloud.component.html', templateUrl: './form-cloud.component.html',
styleUrls: ['./form-cloud.component.scss'] 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. */ /** App name to fetch corresponding form and values. */
@Input() @Input()
appName: string = ''; appName: string = '';
@@ -131,8 +132,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
nodeId: string; nodeId: string;
formCloudRepresentationJSON: any; formCloudRepresentationJSON: any;
protected onDestroy$ = new Subject<boolean>();
readonly id: string; readonly id: string;
displayMode: string; displayMode: string;
displayConfiguration: FormCloudDisplayModeConfiguration = DisplayModeService.DEFAULT_DISPLAY_MODE_CONFIGURATIONS[0]; displayConfiguration: FormCloudDisplayModeConfiguration = DisplayModeService.DEFAULT_DISPLAY_MODE_CONFIGURATIONS[0];
@@ -146,14 +145,16 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
protected displayModeService = inject(DisplayModeService); protected displayModeService = inject(DisplayModeService);
protected changeDetector = inject(ChangeDetectorRef); protected changeDetector = inject(ChangeDetectorRef);
private readonly destroyRef = inject(DestroyRef);
constructor() { constructor() {
super(); super();
this.spinnerService.initSpinnerHandling(this.onDestroy$); this.spinnerService.initSpinnerHandling(this.destroyRef);
this.id = uuidGeneration(); this.id = uuidGeneration();
this.formService.formContentClicked.pipe(takeUntil(this.onDestroy$)).subscribe((content) => { this.formService.formContentClicked.pipe(takeUntilDestroyed()).subscribe((content) => {
if (content instanceof UploadWidgetContentLinkModel) { if (content instanceof UploadWidgetContentLinkModel) {
this.form.setNodeIdValueForViewersLinkedToUploadWidget(content); this.form.setNodeIdValueForViewersLinkedToUploadWidget(content);
this.onFormDataRefreshed(this.form); 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.form.addValuesNotPresent(valuesToSetIfNotPresent);
this.onFormDataRefreshed(this.form); this.onFormDataRefreshed(this.form);
}); });
this.formService.formFieldValueChanged.pipe(takeUntil(this.onDestroy$)).subscribe(() => { this.formService.formFieldValueChanged.pipe(takeUntilDestroyed()).subscribe(() => {
if (this.disableSaveButton) { if (this.disableSaveButton) {
this.disableSaveButton = false; this.disableSaveButton = false;
} }
@@ -222,7 +223,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
DisplayModeService.displayMode$ DisplayModeService.displayMode$
.pipe( .pipe(
filter((change) => change.id === this.id), filter((change) => change.id === this.id),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((displayModeChange) => { .subscribe((displayModeChange) => {
const oldDisplayMode = this.displayMode; const oldDisplayMode = this.displayMode;
@@ -281,7 +282,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
getFormByTaskId(appName: string, taskId: string, version?: number): Promise<FormModel> { getFormByTaskId(appName: string, taskId: string, version?: number): Promise<FormModel> {
return new Promise<FormModel>((resolve) => { return new Promise<FormModel>((resolve) => {
forkJoin(this.formCloudService.getTaskForm(appName, taskId, version), this.formCloudService.getTaskVariables(appName, taskId)) forkJoin(this.formCloudService.getTaskForm(appName, taskId, version), this.formCloudService.getTaskVariables(appName, taskId))
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe( .subscribe(
(data) => { (data) => {
this.formCloudRepresentationJSON = data[0]; this.formCloudRepresentationJSON = data[0];
@@ -314,7 +315,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
delete flattenForm.formDefinition; delete flattenForm.formDefinition;
return flattenForm; return flattenForm;
}), }),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe( .subscribe(
(form) => { (form) => {
@@ -337,7 +338,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
if (this.form && this.appName && this.taskId) { if (this.form && this.appName && this.taskId) {
this.formCloudService this.formCloudService
.saveTaskForm(this.appName, this.taskId, this.processInstanceId, `${this.form.id}`, this.form.values) .saveTaskForm(this.appName, this.taskId, this.processInstanceId, `${this.form.id}`, this.form.values)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe( .subscribe(
() => { () => {
this.onTaskSaved(this.form); this.onTaskSaved(this.form);
@@ -372,7 +373,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
if (this.form && this.appName && this.taskId) { if (this.form && this.appName && this.taskId) {
this.formCloudService this.formCloudService
.completeTaskForm(this.appName, this.taskId, this.processInstanceId, `${this.form.id}`, this.form.values, outcome, this.appVersion) .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( .subscribe(
() => { () => {
this.onTaskCompleted(this.form); this.onTaskCompleted(this.form);
@@ -478,11 +479,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
protected storeFormAsMetadata() {} protected storeFormAsMetadata() {}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
switchToDisplayMode(newDisplayMode?: string) { switchToDisplayMode(newDisplayMode?: string) {
this.displayModeService.switchToDisplayMode(this.id, FormCloudDisplayMode[newDisplayMode], this.displayMode, this.displayModeConfigurations); this.displayModeService.switchToDisplayMode(this.id, FormCloudDisplayMode[newDisplayMode], this.displayMode, this.displayModeConfigurations);
} }

View File

@@ -21,47 +21,47 @@ import { ContentCloudNodeSelectorService } from '../../../services/content-cloud
import { ProcessCloudContentService } from '../../../services/process-cloud-content.service'; import { ProcessCloudContentService } from '../../../services/process-cloud-content.service';
import { AttachFileCloudWidgetComponent } from './attach-file-cloud-widget.component'; import { AttachFileCloudWidgetComponent } from './attach-file-cloud-widget.component';
import { import {
FormFieldModel,
FormModel,
FormFieldTypes,
FormService,
DownloadService,
AppConfigService, AppConfigService,
UploadWidgetContentLinkModel, ContentLinkModel,
DownloadService,
FormFieldModel,
FormFieldTypes,
FormModel,
FormService,
LocalizedDatePipe, LocalizedDatePipe,
NotificationService, NotificationService,
ContentLinkModel UploadWidgetContentLinkModel
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { import {
allSourceParams, allSourceParams,
contentSourceParam,
fakeNode,
mockNodeId,
fakeLocalPngResponse,
onlyLocalParams,
allSourceWithWrongAliasParams,
allSourceWithNoAliasParams,
fakeNodeWithProperties,
menuTestSourceParam,
expectedValues,
fakeLocalPngAnswer,
allSourceWithStringTypeEmptyValue,
mockNodeIdBasedOnStringVariableValue,
mockAllFileSourceWithStringVariablePathType,
mockAllFileSourceWithFolderVariablePathType,
mockContentFileSource,
mockAllFileSourceWithStaticPathType,
formVariables,
processVariables,
mockAllFileSourceWithRenamedFolderVariablePathType,
allSourceParamsWithRelativePath, allSourceParamsWithRelativePath,
fakeLocalPhysicalRecordResponse, allSourceWithNoAliasParams,
allSourceWithStringTypeEmptyValue,
allSourceWithWrongAliasParams,
contentSourceParam,
displayableCMParams, displayableCMParams,
expectedValues,
fakeLocalPhysicalRecordResponse,
fakeLocalPngAnswer,
fakeLocalPngHavingCMProperties, fakeLocalPngHavingCMProperties,
mockMyNodeId fakeLocalPngResponse,
fakeNode,
fakeNodeWithProperties,
formVariables,
menuTestSourceParam,
mockAllFileSourceWithFolderVariablePathType,
mockAllFileSourceWithRenamedFolderVariablePathType,
mockAllFileSourceWithStaticPathType,
mockAllFileSourceWithStringVariablePathType,
mockContentFileSource,
mockMyNodeId,
mockNodeId,
mockNodeIdBasedOnStringVariableValue,
onlyLocalParams,
processVariables
} from '../../../mocks/attach-file-cloud-widget.mock'; } from '../../../mocks/attach-file-cloud-widget.mock';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module'; 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 { import {
ContentModule, ContentModule,
ContentNodeSelectorPanelService, ContentNodeSelectorPanelService,
@@ -162,9 +162,12 @@ describe('AttachFileCloudWidgetComponent', () => {
contentCloudNodeSelectorService = TestBed.inject(ContentCloudNodeSelectorService); contentCloudNodeSelectorService = TestBed.inject(ContentCloudNodeSelectorService);
appConfigService = TestBed.inject(AppConfigService); appConfigService = TestBed.inject(AppConfigService);
formService = TestBed.inject(FormService); formService = TestBed.inject(FormService);
const injector = TestBed.inject(Injector);
contentNodeSelectorPanelService = TestBed.inject(ContentNodeSelectorPanelService); contentNodeSelectorPanelService = TestBed.inject(ContentNodeSelectorPanelService);
openUploadFileDialogSpy = spyOn(contentCloudNodeSelectorService, 'openUploadFileDialog').and.returnValue(of([fakeNode])); openUploadFileDialogSpy = spyOn(contentCloudNodeSelectorService, 'openUploadFileDialog').and.returnValue(of([fakeNode]));
localizedDataPipe = new LocalizedDatePipe(); runInInjectionContext(injector, () => {
localizedDataPipe = new LocalizedDatePipe();
});
}); });
afterEach(() => { afterEach(() => {

View File

@@ -17,19 +17,17 @@
/* eslint-disable @angular-eslint/component-selector */ /* 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 { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { import {
WidgetComponent, ADF_DATE_FORMATS,
FormService,
AdfDateFnsAdapter, AdfDateFnsAdapter,
DateFnsUtils, DateFnsUtils,
ADF_DATE_FORMATS, DEFAULT_DATE_FORMAT,
ErrorWidgetComponent,
ErrorMessageModel, ErrorMessageModel,
DEFAULT_DATE_FORMAT ErrorWidgetComponent,
FormService,
WidgetComponent
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
import { addDays, parseISO } from 'date-fns'; import { addDays, parseISO } from 'date-fns';
@@ -38,6 +36,7 @@ import { NgIf } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'date-widget', selector: 'date-widget',
@@ -62,7 +61,7 @@ import { MatInputModule } from '@angular/material/input';
}, },
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class DateCloudWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { export class DateCloudWidgetComponent extends WidgetComponent implements OnInit {
typeId = 'DateCloudWidgetComponent'; typeId = 'DateCloudWidgetComponent';
minDate: Date = null; minDate: Date = null;
@@ -71,9 +70,9 @@ export class DateCloudWidgetComponent extends WidgetComponent implements OnInit,
dateInputControl: FormControl<Date> = new FormControl<Date>(null); dateInputControl: FormControl<Date> = new FormControl<Date>(null);
private onDestroy$ = new Subject<void>();
public readonly formService = inject(FormService); public readonly formService = inject(FormService);
private readonly destroyRef = inject(DestroyRef);
private readonly dateAdapter = inject(DateAdapter); private readonly dateAdapter = inject(DateAdapter);
ngOnInit(): void { ngOnInit(): void {
@@ -101,7 +100,7 @@ export class DateCloudWidgetComponent extends WidgetComponent implements OnInit,
} }
private subscribeToDateChanges(): void { 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.field.value = newDate;
this.updateField(); this.updateField();
}); });
@@ -198,9 +197,4 @@ export class DateCloudWidgetComponent extends WidgetComponent implements OnInit,
this.maxDate = parseISO(this.field.maxValue); this.maxDate = parseISO(this.field.maxValue);
} }
} }
ngOnDestroy(): void {
this.onDestroy$.next();
this.onDestroy$.complete();
}
} }

View File

@@ -29,15 +29,16 @@ import {
WidgetComponent WidgetComponent
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { AsyncPipe, NgFor, NgIf } from '@angular/common'; 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 { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { BehaviorSubject, Subject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { filter, map, takeUntil } from 'rxjs/operators'; import { filter, map } from 'rxjs/operators';
import { TaskVariableCloud } from '../../../models/task-variable-cloud.model'; import { TaskVariableCloud } from '../../../models/task-variable-cloud.model';
import { FormCloudService } from '../../../services/form-cloud.service'; import { FormCloudService } from '../../../services/form-cloud.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export const DEFAULT_OPTION = { export const DEFAULT_OPTION = {
id: 'empty', id: 'empty',
@@ -65,7 +66,7 @@ export const HIDE_FILTER_LIMIT = 5;
SelectFilterInputComponent SelectFilterInputComponent
] ]
}) })
export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit {
public formService = inject(FormService); public formService = inject(FormService);
private formCloudService = inject(FormCloudService); private formCloudService = inject(FormCloudService);
private appConfig = inject(AppConfigService); private appConfig = inject(AppConfigService);
@@ -84,8 +85,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
private readonly defaultVariableOptionId = 'id'; private readonly defaultVariableOptionId = 'id';
private readonly defaultVariableOptionLabel = 'name'; private readonly defaultVariableOptionLabel = 'name';
private readonly defaultVariableOptionPath = 'data'; private readonly defaultVariableOptionPath = 'data';
private readonly destroyRef = inject(DestroyRef);
protected onDestroy$ = new Subject<boolean>();
get showRequiredMessage(): boolean { get showRequiredMessage(): boolean {
return this.dropdownControl.touched && this.dropdownControl.errors?.required && !this.isRestApiFailed && !this.variableOptionsFailed; 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 { compareDropdownValues(opt1: FormFieldOption | string, opt2: FormFieldOption | string): boolean {
if (!opt1 || !opt2) { if (!opt1 || !opt2) {
return false; return false;
@@ -186,7 +181,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
this.dropdownControl.valueChanges this.dropdownControl.valueChanges
.pipe( .pipe(
filter(() => !!this.field), filter(() => !!this.field),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((value) => { .subscribe((value) => {
this.setOptionValue(value, this.field); this.setOptionValue(value, this.field);
@@ -216,7 +211,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
map((search) => map((search) =>
search ? this.field.options.filter(({ name }) => name.toLowerCase().includes(search.toLowerCase())) : this.field.options 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)); .subscribe((result) => this.list$.next(result));
} }
@@ -333,7 +328,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
const bodyParam = this.buildBodyParam(); const bodyParam = this.buildBodyParam();
this.formCloudService this.formCloudService
.getRestWidgetData(this.field.form.id, this.field.id, bodyParam) .getRestWidgetData(this.field.form.id, this.field.id, bodyParam)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({ .subscribe({
next: (result: FormFieldOption[]) => { next: (result: FormFieldOption[]) => {
this.resetRestApiErrorMessage(); this.resetRestApiErrorMessage();
@@ -366,7 +361,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
this.formService.formFieldValueChanged this.formService.formFieldValueChanged
.pipe( .pipe(
filter((event: FormFieldEvent) => this.isFormFieldEventOfTypeDropdown(event) && this.isParentFormFieldEvent(event)), filter((event: FormFieldEvent) => this.isFormFieldEventOfTypeDropdown(event) && this.isParentFormFieldEvent(event)),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((event: FormFieldEvent) => { .subscribe((event: FormFieldEvent) => {
const valueOfParentWidget = event.field.value; const valueOfParentWidget = event.field.value;

View File

@@ -15,13 +15,13 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core';
import { WidgetComponent, FormService } from '@alfresco/adf-core'; import { FormService, WidgetComponent } from '@alfresco/adf-core';
import { UntypedFormControl } from '@angular/forms'; import { UntypedFormControl } from '@angular/forms';
import { filter, takeUntil } from 'rxjs/operators'; import { filter } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { ComponentSelectionMode } from '../../../../types'; import { ComponentSelectionMode } from '../../../../types';
import { IdentityGroupModel } from '../../../../group/models/identity-group.model'; import { IdentityGroupModel } from '../../../../group/models/identity-group.model';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
/* eslint-disable @angular-eslint/component-selector */ /* eslint-disable @angular-eslint/component-selector */
@@ -41,9 +41,7 @@ import { IdentityGroupModel } from '../../../../group/models/identity-group.mode
}, },
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class GroupCloudWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { export class GroupCloudWidgetComponent extends WidgetComponent implements OnInit {
private onDestroy$ = new Subject<boolean>();
typeId = 'GroupCloudWidgetComponent'; typeId = 'GroupCloudWidgetComponent';
roles: string[]; roles: string[];
@@ -53,6 +51,8 @@ export class GroupCloudWidgetComponent extends WidgetComponent implements OnInit
search: UntypedFormControl; search: UntypedFormControl;
validate = false; validate = false;
private readonly destroyRef = inject(DestroyRef);
constructor(formService: FormService) { constructor(formService: FormService) {
super(formService); super(formService);
} }
@@ -71,7 +71,7 @@ export class GroupCloudWidgetComponent extends WidgetComponent implements OnInit
this.search.statusChanges this.search.statusChanges
.pipe( .pipe(
filter((value: string) => value === 'INVALID'), filter((value: string) => value === 'INVALID'),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe(() => { .subscribe(() => {
this.field.markAsInvalid(); this.field.markAsInvalid();
@@ -81,19 +81,13 @@ export class GroupCloudWidgetComponent extends WidgetComponent implements OnInit
this.search.statusChanges this.search.statusChanges
.pipe( .pipe(
filter((value: string) => value === 'VALID'), filter((value: string) => value === 'VALID'),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe(() => { .subscribe(() => {
this.field.validate(); this.field.validate();
this.field.form.validateForm(); this.field.form.validateForm();
}); });
} }
ngOnDestroy(): void {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
onChangedGroup(groups: IdentityGroupModel[]): void { onChangedGroup(groups: IdentityGroupModel[]): void {
this.field.value = groups?.length ? [...groups] : null; this.field.value = groups?.length ? [...groups] : null;
this.onFieldChanged(this.field); this.onFieldChanged(this.field);

View File

@@ -15,14 +15,14 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core';
import { WidgetComponent, FormService } from '@alfresco/adf-core'; import { FormService, WidgetComponent } from '@alfresco/adf-core';
import { UntypedFormControl } from '@angular/forms'; import { UntypedFormControl } from '@angular/forms';
import { filter, takeUntil } from 'rxjs/operators'; import { filter } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { ComponentSelectionMode } from '../../../../types'; import { ComponentSelectionMode } from '../../../../types';
import { IdentityUserModel } from '../../../../people/models/identity-user.model'; import { IdentityUserModel } from '../../../../people/models/identity-user.model';
import { IdentityUserService } from '../../../../people/services/identity-user.service'; import { IdentityUserService } from '../../../../people/services/identity-user.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
/* eslint-disable @angular-eslint/component-selector */ /* eslint-disable @angular-eslint/component-selector */
@@ -42,9 +42,7 @@ import { IdentityUserService } from '../../../../people/services/identity-user.s
}, },
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class PeopleCloudWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { export class PeopleCloudWidgetComponent extends WidgetComponent implements OnInit {
private onDestroy$ = new Subject<boolean>();
typeId = 'PeopleCloudWidgetComponent'; typeId = 'PeopleCloudWidgetComponent';
appName: string; appName: string;
@@ -56,6 +54,8 @@ export class PeopleCloudWidgetComponent extends WidgetComponent implements OnIni
groupsRestriction: string[]; groupsRestriction: string[];
validate = false; validate = false;
private readonly destroyRef = inject(DestroyRef);
constructor(formService: FormService, private identityUserService: IdentityUserService) { constructor(formService: FormService, private identityUserService: IdentityUserService) {
super(formService); super(formService);
} }
@@ -75,7 +75,7 @@ export class PeopleCloudWidgetComponent extends WidgetComponent implements OnIni
this.search.statusChanges this.search.statusChanges
.pipe( .pipe(
filter((value: string) => value === 'INVALID'), filter((value: string) => value === 'INVALID'),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe(() => { .subscribe(() => {
this.field.markAsInvalid(); this.field.markAsInvalid();
@@ -85,7 +85,7 @@ export class PeopleCloudWidgetComponent extends WidgetComponent implements OnIni
this.search.statusChanges this.search.statusChanges
.pipe( .pipe(
filter((value: string) => value === 'VALID'), filter((value: string) => value === 'VALID'),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe(() => { .subscribe(() => {
this.field.validate(); 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 { onChangedUser(users: IdentityUserModel[]): void {
this.field.value = users?.length ? [...users] : null; this.field.value = users?.length ? [...users] : null;
this.onFieldChanged(this.field); this.onFieldChanged(this.field);

View File

@@ -17,12 +17,11 @@
/* eslint-disable @angular-eslint/component-selector */ /* eslint-disable @angular-eslint/component-selector */
import { Component, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core';
import { WidgetComponent, FormService, FormFieldOption, ErrorMessageModel } from '@alfresco/adf-core'; import { ErrorMessageModel, FormFieldOption, FormService, WidgetComponent } from '@alfresco/adf-core';
import { FormCloudService } from '../../../services/form-cloud.service'; import { FormCloudService } from '../../../services/form-cloud.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'radio-buttons-cloud-widget', selector: 'radio-buttons-cloud-widget',
@@ -45,7 +44,7 @@ export class RadioButtonsCloudWidgetComponent extends WidgetComponent implements
typeId = 'RadioButtonsCloudWidgetComponent'; typeId = 'RadioButtonsCloudWidgetComponent';
restApiError: ErrorMessageModel; restApiError: ErrorMessageModel;
protected onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor(public formService: FormService, private formCloudService: FormCloudService, private translateService: TranslateService) { constructor(public formService: FormService, private formCloudService: FormCloudService, private translateService: TranslateService) {
super(formService); super(formService);
@@ -60,7 +59,7 @@ export class RadioButtonsCloudWidgetComponent extends WidgetComponent implements
getValuesFromRestApi() { getValuesFromRestApi() {
this.formCloudService this.formCloudService
.getRestWidgetData(this.field.form.id, this.field.id) .getRestWidgetData(this.field.form.id, this.field.id)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe( .subscribe(
(result: FormFieldOption[]) => { (result: FormFieldOption[]) => {
this.field.options = result; this.field.options = result;

View File

@@ -21,7 +21,7 @@ import { OverlayModule } from '@angular/cdk/overlay';
import { FormService, FormSpinnerEvent } from '@alfresco/adf-core'; import { FormService, FormSpinnerEvent } from '@alfresco/adf-core';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { TranslateModule } from '@ngx-translate/core'; 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 { FormSpinnerComponent } from '../../components/spinner/form-spinner.component';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
@@ -33,18 +33,20 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
selector: 'adf-cloud-overlay-test', selector: 'adf-cloud-overlay-test',
template: `<div>adf-cloud-overlay-test</div>` template: `<div>adf-cloud-overlay-test</div>`
}) })
class SpinnerTestComponent {} class SpinnerTestComponent {
destroyRef = inject(DestroyRef)
}
describe('FormCloudSpinnerService', () => { describe('FormCloudSpinnerService', () => {
let fixture: ComponentFixture<SpinnerTestComponent>; let fixture: ComponentFixture<SpinnerTestComponent>;
let rootLoader: HarnessLoader; let rootLoader: HarnessLoader;
let spinnerService: FormCloudSpinnerService; let spinnerService: FormCloudSpinnerService;
let formService: FormService; let formService: FormService;
let destroyRef: DestroyRef;
const showSpinnerEvent = new FormSpinnerEvent('toggle-spinner', { showSpinner: true, message: 'LOAD_SPINNER_MESSAGE' }); const showSpinnerEvent = new FormSpinnerEvent('toggle-spinner', { showSpinner: true, message: 'LOAD_SPINNER_MESSAGE' });
const hideSpinnerEvent = new FormSpinnerEvent('toggle-spinner', { showSpinner: false }); const hideSpinnerEvent = new FormSpinnerEvent('toggle-spinner', { showSpinner: false });
const onDestroy$ = new Subject<boolean>();
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
@@ -65,10 +67,11 @@ describe('FormCloudSpinnerService', () => {
rootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture); rootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture);
spinnerService = TestBed.inject(FormCloudSpinnerService); spinnerService = TestBed.inject(FormCloudSpinnerService);
formService = TestBed.inject(FormService); formService = TestBed.inject(FormService);
destroyRef = fixture.componentInstance.destroyRef
}); });
it('should toggle spinner', async () => { it('should toggle spinner', async () => {
spinnerService.initSpinnerHandling(onDestroy$); spinnerService.initSpinnerHandling(destroyRef);
formService.toggleFormSpinner.next(showSpinnerEvent); formService.toggleFormSpinner.next(showSpinnerEvent);
fixture.detectChanges(); fixture.detectChanges();

View File

@@ -15,13 +15,12 @@
* limitations under the License. * 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 { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal'; import { ComponentPortal } from '@angular/cdk/portal';
import { FormService, FormSpinnerEvent } from '@alfresco/adf-core'; 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 { FormSpinnerComponent } from '../../components/spinner/form-spinner.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Injectable() @Injectable()
export class FormCloudSpinnerService { export class FormCloudSpinnerService {
@@ -30,8 +29,8 @@ export class FormCloudSpinnerService {
private overlayRef?: OverlayRef; private overlayRef?: OverlayRef;
initSpinnerHandling(onDestroy$: Observable<boolean>): void { initSpinnerHandling(destroyRef: DestroyRef): void {
this.formService.toggleFormSpinner.pipe(takeUntil(onDestroy$)).subscribe((event: FormSpinnerEvent) => { this.formService.toggleFormSpinner.pipe(takeUntilDestroyed(destroyRef)).subscribe((event: FormSpinnerEvent) => {
if (event?.payload.showSpinner) { if (event?.payload.showSpinner) {
this.overlayRef = this.overlay.create({ this.overlayRef = this.overlay.create({
hasBackdrop: true hasBackdrop: true

View File

@@ -17,26 +17,28 @@
import { import {
Component, Component,
DestroyRef,
ElementRef, ElementRef,
EventEmitter,
Inject,
inject,
Input,
OnChanges,
OnInit, OnInit,
Output, Output,
EventEmitter,
ViewChild,
ViewEncapsulation,
Input,
SimpleChanges, SimpleChanges,
OnChanges, ViewChild,
OnDestroy, ViewEncapsulation
Inject
} from '@angular/core'; } from '@angular/core';
import { UntypedFormControl } from '@angular/forms'; import { UntypedFormControl } from '@angular/forms';
import { trigger, state, style, transition, animate } from '@angular/animations'; import { animate, state, style, transition, trigger } from '@angular/animations';
import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, switchMap, mergeMap, filter, tap, takeUntil, debounceTime } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, filter, mergeMap, switchMap, tap } from 'rxjs/operators';
import { ComponentSelectionMode } from '../../types'; import { ComponentSelectionMode } from '../../types';
import { IdentityGroupModel } from '../models/identity-group.model'; import { IdentityGroupModel } from '../models/identity-group.model';
import { IdentityGroupServiceInterface } from '../services/identity-group.service.interface'; import { IdentityGroupServiceInterface } from '../services/identity-group.service.interface';
import { IDENTITY_GROUP_SERVICE_TOKEN } from '../services/identity-group-service.token'; import { IDENTITY_GROUP_SERVICE_TOKEN } from '../services/identity-group-service.token';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-cloud-group', selector: 'adf-cloud-group',
@@ -50,7 +52,7 @@ import { IDENTITY_GROUP_SERVICE_TOKEN } from '../services/identity-group-service
], ],
encapsulation: ViewEncapsulation.None 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. */ /** Name of the application. If specified this shows the groups who have access to the app. */
@Input() @Input()
appName: string; appName: string;
@@ -119,7 +121,6 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
private groupInput: ElementRef<HTMLInputElement>; private groupInput: ElementRef<HTMLInputElement>;
private searchGroups: IdentityGroupModel[] = []; private searchGroups: IdentityGroupModel[] = [];
private onDestroy$ = new Subject<boolean>();
selectedGroups: IdentityGroupModel[] = []; selectedGroups: IdentityGroupModel[] = [];
invalidGroups: IdentityGroupModel[] = []; invalidGroups: IdentityGroupModel[] = [];
@@ -137,6 +138,8 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
typingUniqueValueNotEmpty$: Observable<any>; typingUniqueValueNotEmpty$: Observable<any>;
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
@Inject(IDENTITY_GROUP_SERVICE_TOKEN) @Inject(IDENTITY_GROUP_SERVICE_TOKEN)
private identityGroupService: IdentityGroupServiceInterface private identityGroupService: IdentityGroupServiceInterface
@@ -172,7 +175,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
return groups; return groups;
}), }),
filter((group) => !this.isGroupAlreadySelected(group)), filter((group) => !this.isGroupAlreadySelected(group)),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((searchedGroup: IdentityGroupModel) => { .subscribe((searchedGroup: IdentityGroupModel) => {
this.searchGroups.push(searchedGroup); this.searchGroups.push(searchedGroup);
@@ -467,9 +470,4 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
getValidationMinLength(): string { getValidationMinLength(): string {
return this.searchGroupsControl.errors.minlength.requiredLength; return this.searchGroupsControl.errors.minlength.requiredLength;
} }
ngOnDestroy(): void {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -17,29 +17,31 @@
import { UntypedFormControl } from '@angular/forms'; import { UntypedFormControl } from '@angular/forms';
import { import {
AfterViewInit,
Component, Component,
DestroyRef,
ElementRef,
EventEmitter,
Inject,
inject,
Input,
OnChanges,
OnInit, OnInit,
Output, Output,
EventEmitter,
ViewEncapsulation,
Input,
SimpleChanges, SimpleChanges,
OnChanges,
OnDestroy,
ViewChild, ViewChild,
ElementRef, ViewEncapsulation
Inject,
AfterViewInit
} from '@angular/core'; } from '@angular/core';
import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { switchMap, debounceTime, distinctUntilChanged, mergeMap, tap, filter, takeUntil } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, filter, mergeMap, switchMap, tap } from 'rxjs/operators';
import { FullNamePipe } from '@alfresco/adf-core'; 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 { ComponentSelectionMode } from '../../types';
import { IdentityUserModel } from '../models/identity-user.model'; import { IdentityUserModel } from '../models/identity-user.model';
import { IdentityUserServiceInterface } from '../services/identity-user.service.interface'; import { IdentityUserServiceInterface } from '../services/identity-user.service.interface';
import { IDENTITY_USER_SERVICE_TOKEN } from '../services/identity-user-service.token'; import { IDENTITY_USER_SERVICE_TOKEN } from '../services/identity-user-service.token';
import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-cloud-people', selector: 'adf-cloud-people',
@@ -54,7 +56,7 @@ import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-
providers: [FullNamePipe], providers: [FullNamePipe],
encapsulation: ViewEncapsulation.None 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. */ /** Name of the application. If specified, this shows the users who have access to the app. */
@Input() @Input()
appName: string; appName: string;
@@ -175,7 +177,6 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy, After
private userInput: ElementRef<HTMLInputElement>; private userInput: ElementRef<HTMLInputElement>;
private searchUsers: IdentityUserModel[] = []; private searchUsers: IdentityUserModel[] = [];
private onDestroy$ = new Subject<boolean>();
selectedUsers: IdentityUserModel[] = []; selectedUsers: IdentityUserModel[] = [];
invalidUsers: IdentityUserModel[] = []; invalidUsers: IdentityUserModel[] = [];
@@ -193,6 +194,8 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy, After
typingUniqueValueNotEmpty$: Observable<string>; typingUniqueValueNotEmpty$: Observable<string>;
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
@Inject(IDENTITY_USER_SERVICE_TOKEN) @Inject(IDENTITY_USER_SERVICE_TOKEN)
private identityUserService: IdentityUserServiceInterface private identityUserService: IdentityUserServiceInterface
@@ -245,7 +248,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy, After
return users; return users;
}), }),
filter((user) => !this.isUserAlreadySelected(user) && !this.isExcludedUser(user)), filter((user) => !this.isUserAlreadySelected(user) && !this.isExcludedUser(user)),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((user: IdentityUserModel) => { .subscribe((user: IdentityUserModel) => {
this.searchUsers.push(user); this.searchUsers.push(user);
@@ -563,9 +566,4 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy, After
getValidationMinLength(): string { getValidationMinLength(): string {
return this.searchUserCtrl.errors.minlength.requiredLength; return this.searchUserCtrl.errors.minlength.requiredLength;
} }
ngOnDestroy(): void {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -15,18 +15,17 @@
* limitations under the License. * 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 { 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 { ProcessInstanceCloud } from '../start-process/models/process-instance-cloud.model';
import { IdentityUserService } from '../../people/services/identity-user.service'; import { IdentityUserService } from '../../people/services/identity-user.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Directive({ @Directive({
// eslint-disable-next-line @angular-eslint/directive-selector // eslint-disable-next-line @angular-eslint/directive-selector
selector: '[adf-cloud-cancel-process]' selector: '[adf-cloud-cancel-process]'
}) })
export class CancelProcessDirective implements OnInit, OnDestroy { export class CancelProcessDirective implements OnInit {
/** Emitted when the process is cancelled. */ /** Emitted when the process is cancelled. */
@Output() @Output()
@@ -40,7 +39,7 @@ export class CancelProcessDirective implements OnInit, OnDestroy {
canCancelProcess = false; canCancelProcess = false;
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private elementRef: ElementRef, private elementRef: ElementRef,
@@ -49,7 +48,7 @@ export class CancelProcessDirective implements OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.processCloudService.dataChangesDetected this.processCloudService.dataChangesDetected
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((processDetails) => { .subscribe((processDetails) => {
this.processInstanceDetails = processDetails; this.processInstanceDetails = processDetails;
this.canCancelProcess = this.checkCanCancelProcess(); 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'); this.error.emit('Permission denied, only process initiator can cancel the process');
} }
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -15,18 +15,18 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, OnDestroy, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { FormBuilder, AbstractControl, FormGroup, FormControl } from '@angular/forms'; import { AbstractControl, FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { DateAdapter } from '@angular/material/core'; import { DateAdapter } from '@angular/material/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { debounceTime, filter, takeUntil, finalize, switchMap, tap } from 'rxjs/operators'; import { debounceTime, filter, finalize, switchMap, tap } from 'rxjs/operators';
import { Subject, Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service'; import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service';
import { import {
ProcessFilterCloudModel,
ProcessFilterProperties,
ProcessFilterAction, ProcessFilterAction,
ProcessFilterCloudModel,
ProcessFilterOptions, ProcessFilterOptions,
ProcessFilterProperties,
ProcessSortFilterProperty ProcessSortFilterProperty
} from '../models/process-filter-cloud.model'; } from '../models/process-filter-cloud.model';
import { DateFnsUtils, TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core'; 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 { IdentityUserModel } from '../../../people/models/identity-user.model';
import { Environment } from '../../../common/interface/environment.interface'; import { Environment } from '../../../common/interface/environment.interface';
import { endOfDay, isValid, startOfDay } from 'date-fns'; 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 = 'save';
export const PROCESS_FILTER_ACTION_SAVE_AS = 'saveAs'; export const PROCESS_FILTER_ACTION_SAVE_AS = 'saveAs';
@@ -71,7 +72,7 @@ interface ProcessFilterFormProps {
styleUrls: ['./edit-process-filter-cloud.component.scss'], styleUrls: ['./edit-process-filter-cloud.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDestroy { export class EditProcessFilterCloudComponent implements OnInit, OnChanges {
/** The name of the application. */ /** The name of the application. */
@Input() @Input()
appName: string = ''; appName: string = '';
@@ -184,10 +185,11 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
appVersionOptions: ProcessFilterOptions[] = []; appVersionOptions: ProcessFilterOptions[] = [];
initiatorOptions: IdentityUserModel[] = []; initiatorOptions: IdentityUserModel[] = [];
private onDestroy$ = new Subject<boolean>();
isLoading: boolean = false; isLoading: boolean = false;
private filterChangeSub: Subscription; private filterChangeSub: Subscription;
private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
public dialog: MatDialog, public dialog: MatDialog,
@@ -202,7 +204,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
ngOnInit() { ngOnInit() {
this.userPreferencesService this.userPreferencesService
.select(UserPreferenceValues.Locale) .select(UserPreferenceValues.Locale)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((locale) => this.dateAdapter.setLocale(locale)); .subscribe((locale) => this.dateAdapter.setLocale(locale));
} }
@@ -212,12 +214,6 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
this.retrieveProcessFilterAndBuildForm(); this.retrieveProcessFilterAndBuildForm();
} }
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
filterTracker(_index: number, item: ProcessFilterProperties) { filterTracker(_index: number, item: ProcessFilterProperties) {
return item.key; return item.key;
} }
@@ -284,7 +280,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
.pipe( .pipe(
debounceTime(500), debounceTime(500),
filter(() => this.isFormValid()), filter(() => this.isFormValid()),
takeUntil(this.onDestroy$) takeUntilDestroyed(this.destroyRef)
) )
.subscribe((formValues: Partial<ProcessFilterCloudModel>) => { .subscribe((formValues: Partial<ProcessFilterCloudModel>) => {
this.setLastModifiedFromFilter(formValues); this.setLastModifiedFromFilter(formValues);

View File

@@ -15,16 +15,28 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, OnDestroy, OnInit, ViewEncapsulation, inject } from '@angular/core'; import {
import { Observable, Subject } from 'rxjs'; 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 { ProcessFilterCloudService } from '../services/process-filter-cloud.service';
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model'; import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
import { AppConfigService, TranslationService } from '@alfresco/adf-core'; import { AppConfigService, TranslationService } from '@alfresco/adf-core';
import { FilterParamsModel } from '../../../task/task-filters/models/filter-cloud.model'; 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 { ProcessListCloudService } from '../../../process/process-list/services/process-list-cloud.service';
import { PROCESS_SEARCH_API_METHOD_TOKEN } from '../../../services/cloud-token.service'; import { PROCESS_SEARCH_API_METHOD_TOKEN } from '../../../services/cloud-token.service';
import { ProcessFilterCloudAdapter } from '../../process-list/models/process-cloud-query-request.model'; import { ProcessFilterCloudAdapter } from '../../process-list/models/process-cloud-query-request.model';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'adf-cloud-process-filters', selector: 'adf-cloud-process-filters',
@@ -32,7 +44,7 @@ import { ProcessFilterCloudAdapter } from '../../process-list/models/process-clo
styleUrls: ['./process-filters-cloud.component.scss'], styleUrls: ['./process-filters-cloud.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestroy { export class ProcessFiltersCloudComponent implements OnInit, OnChanges {
/** (required) The application name */ /** (required) The application name */
@Input() @Input()
appName: string = ''; appName: string = '';
@@ -73,8 +85,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro
currentFiltersValues: { [key: string]: number } = {}; currentFiltersValues: { [key: string]: number } = {};
updatedFiltersSet = new Set<string>(); updatedFiltersSet = new Set<string>();
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
private readonly processFilterCloudService = inject(ProcessFilterCloudService); private readonly processFilterCloudService = inject(ProcessFilterCloudService);
private readonly translationService = inject(TranslationService); private readonly translationService = inject(TranslationService);
private readonly appConfigService = inject(AppConfigService); private readonly appConfigService = inject(AppConfigService);
@@ -108,7 +119,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro
getFilters(appName: string): void { getFilters(appName: string): void {
this.filters$ = this.processFilterCloudService.getProcessFilters(appName); this.filters$ = this.processFilterCloudService.getProcessFilters(appName);
this.filters$.pipe(takeUntil(this.onDestroy$)).subscribe({ this.filters$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
next: (res) => { next: (res) => {
this.resetFilter(); this.resetFilter();
this.filters = res || []; this.filters = res || [];
@@ -240,11 +251,6 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro
this.currentFilter = undefined; this.currentFilter = undefined;
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
isActiveFilter(filter: ProcessFilterCloudModel): boolean { isActiveFilter(filter: ProcessFilterCloudModel): boolean {
return this.currentFilter.name === filter.name; return this.currentFilter.name === filter.name;
} }
@@ -253,7 +259,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro
if (this.appName && this.enableNotifications) { if (this.appName && this.enableNotifications) {
this.processFilterCloudService this.processFilterCloudService
.getProcessNotificationSubscription(this.appName) .getProcessNotificationSubscription(this.appName)
.pipe(debounceTime(1000), takeUntil(this.onDestroy$)) .pipe(debounceTime(1000), takeUntilDestroyed(this.destroyRef))
.subscribe(() => { .subscribe(() => {
this.updateFilterCounters(); this.updateFilterCounters();
}); });
@@ -310,7 +316,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro
* *
*/ */
getFilterKeysAfterExternalRefreshing(): void { 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); this.updatedFiltersSet.delete(filterKey);
}); });
} }

View File

@@ -15,19 +15,28 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, Input, OnChanges, OnInit, OnDestroy, EventEmitter, ViewEncapsulation, Output } from '@angular/core';
import { import {
Component,
DestroyRef,
EventEmitter,
inject,
Input,
OnChanges,
OnInit,
Output,
ViewEncapsulation
} from '@angular/core';
import {
AppConfigService,
CardViewBaseItemModel,
CardViewDateItemModel,
CardViewItem, CardViewItem,
CardViewTextItemModel, CardViewTextItemModel,
TranslationService, TranslationService
AppConfigService,
CardViewDateItemModel,
CardViewBaseItemModel
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { ProcessInstanceCloud } from '../../start-process/models/process-instance-cloud.model'; import { ProcessInstanceCloud } from '../../start-process/models/process-instance-cloud.model';
import { ProcessCloudService } from '../../services/process-cloud.service'; import { ProcessCloudService } from '../../services/process-cloud.service';
import { takeUntil } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Subject } from 'rxjs';
@Component({ @Component({
selector: 'adf-cloud-process-header', selector: 'adf-cloud-process-header',
@@ -36,7 +45,7 @@ import { Subject } from 'rxjs';
styleUrls: ['./process-header-cloud.component.scss'], styleUrls: ['./process-header-cloud.component.scss'],
host: { class: 'adf-cloud-process-header' } 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. */ /** (Required) The name of the application. */
@Input() @Input()
appName: string = ''; appName: string = '';
@@ -54,7 +63,7 @@ export class ProcessHeaderCloudComponent implements OnChanges, OnInit, OnDestroy
dateFormat: string; dateFormat: string;
dateLocale: string; dateLocale: string;
private onDestroy$ = new Subject<boolean>(); private readonly destroyRef = inject(DestroyRef);
constructor( constructor(
private processCloudService: ProcessCloudService, 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.dateFormat = this.appConfig.get('adf-cloud-process-header.defaultDateFormat');
this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale'); 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() { ngOnChanges() {
@@ -152,9 +161,4 @@ export class ProcessHeaderCloudComponent implements OnChanges, OnInit, OnDestroy
private isValidSelection(filteredProperties: string[], cardItem: CardViewBaseItemModel): boolean { private isValidSelection(filteredProperties: string[], cardItem: CardViewBaseItemModel): boolean {
return filteredProperties ? filteredProperties.indexOf(cardItem.key) >= 0 : true; return filteredProperties ? filteredProperties.indexOf(cardItem.key) >= 0 : true;
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -16,47 +16,53 @@
*/ */
import { import {
Component,
ViewEncapsulation,
OnChanges,
AfterContentInit, AfterContentInit,
Component,
ContentChild, ContentChild,
Output,
EventEmitter, EventEmitter,
SimpleChanges,
Input,
ViewChild,
Inject, Inject,
OnDestroy, Input,
Optional OnChanges,
Optional,
Output,
SimpleChanges,
ViewChild,
ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { import {
DataTableSchema,
PaginatedComponent,
CustomEmptyContentTemplateDirective,
AppConfigService, AppConfigService,
UserPreferencesService, CustomEmptyContentTemplateDirective,
PaginationModel,
UserPreferenceValues,
DataRowEvent,
CustomLoadingContentTemplateDirective, CustomLoadingContentTemplateDirective,
DataCellEvent, DataCellEvent,
DataColumn,
DataRowActionEvent, DataRowActionEvent,
DataRowEvent,
DataTableComponent, DataTableComponent,
DataColumn DataTableSchema,
PaginatedComponent,
PaginationModel,
UserPreferencesService,
UserPreferenceValues
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { ProcessListCloudService } from '../services/process-list-cloud.service'; import { ProcessListCloudService } from '../services/process-list-cloud.service';
import { BehaviorSubject, combineLatest, Subject } from 'rxjs'; import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { processCloudPresetsDefaultModel } from '../models/process-cloud-preset.model'; import { processCloudPresetsDefaultModel } from '../models/process-cloud-preset.model';
import { ProcessListRequestModel, ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model'; import { ProcessListRequestModel, ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.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 { 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 { ProcessListCloudPreferences } from '../models/process-cloud-preferences';
import { ProcessListDatatableAdapter } from '../datatable/process-list-datatable-adapter'; 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 { VariableMapperService } from '../../../services/variable-mapper.sevice';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
const PRESET_KEY = 'adf-cloud-process-list.presets'; 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'], styleUrls: ['./process-list-cloud.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataColumnCustomData> implements OnChanges, AfterContentInit, PaginatedComponent, OnDestroy { export class ProcessListCloudComponent
extends DataTableSchema<ProcessListDataColumnCustomData>
implements OnChanges, AfterContentInit, PaginatedComponent {
@ViewChild(DataTableComponent) dataTable: DataTableComponent; @ViewChild(DataTableComponent) dataTable: DataTableComponent;
@@ -255,8 +263,6 @@ export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataCo
@Output() @Output()
success: EventEmitter<any> = new EventEmitter<any>(); success: EventEmitter<any> = new EventEmitter<any>();
private onDestroy$ = new Subject<boolean>();
pagination: BehaviorSubject<PaginationModel>; pagination: BehaviorSubject<PaginationModel>;
size: number; size: number;
skipCount: number = 0; skipCount: number = 0;
@@ -299,9 +305,7 @@ export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataCo
this.isColumnSchemaCreated$, this.isColumnSchemaCreated$,
this.fetchProcessesTrigger$ this.fetchProcessesTrigger$
]).pipe( ]).pipe(
filter(([isColumnSchemaCreated]) => { filter(([isColumnSchemaCreated]) => isColumnSchemaCreated),
return isColumnSchemaCreated;
}),
switchMap(() => { switchMap(() => {
if (this.searchMethod === 'POST') { if (this.searchMethod === 'POST') {
const requestNode = this.createProcessListRequestNode(); const requestNode = this.createProcessListRequestNode();
@@ -313,7 +317,7 @@ export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataCo
return this.processListCloudService.getProcessByRequest(requestNode).pipe(take(1)); return this.processListCloudService.getProcessByRequest(requestNode).pipe(take(1));
} }
}), }),
takeUntil(this.onDestroy$) takeUntilDestroyed()
).subscribe({ ).subscribe({
next: (processes) => { next: (processes) => {
this.rows = this.variableMapperService.mapVariablesByColumnTitle(processes.list.entries, this.columns); this.rows = this.variableMapperService.mapVariablesByColumnTitle(processes.list.entries, this.columns);
@@ -367,12 +371,6 @@ export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataCo
this.createDatatableSchema(); this.createDatatableSchema();
}); });
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
if (this.isPropertyChanged(changes, 'sorting')) { if (this.isPropertyChanged(changes, 'sorting')) {
this.formatSorting(changes['sorting'].currentValue); this.formatSorting(changes['sorting'].currentValue);

Some files were not shown because too many files have changed in this diff Show More