diff --git a/projects/natural/src/lib/classes/abstract-controller.ts b/projects/natural/src/lib/classes/abstract-controller.ts deleted file mode 100644 index afb500a6..00000000 --- a/projects/natural/src/lib/classes/abstract-controller.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {Directive, OnDestroy} from '@angular/core'; -import {Subject} from 'rxjs'; - -@Directive({standalone: true}) -export class NaturalAbstractController implements OnDestroy { - /** - * Usage: - * - * ```ts - * import { takeUntil } from 'rxjs/operators'; - * .pipe(takeUntil(this.ngUnsubscribe)) // as first pipe on observables that should be destroyed on component destroy - * ``` - * - * @deprecated Instead of this, you should create the observable in the constructor or field initializers and use - * Angular native `.pipe(takeUntilDestroyed())`. And most likely subscribe at a later point. We keep this method until - * all existing usages (typically in `ngOnInit()`) are migrated away. - */ - protected readonly ngUnsubscribe = new Subject(); - - public ngOnDestroy(): void { - this.ngUnsubscribe.next(); // unsubscribe everybody - this.ngUnsubscribe.complete(); // complete the stream, because we will never emit again - } -} diff --git a/projects/natural/src/lib/classes/abstract-detail.ts b/projects/natural/src/lib/classes/abstract-detail.ts index 8e925fdb..94ed3b0e 100644 --- a/projects/natural/src/lib/classes/abstract-detail.ts +++ b/projects/natural/src/lib/classes/abstract-detail.ts @@ -1,4 +1,4 @@ -import {DestroyRef, Directive, inject, OnInit} from '@angular/core'; +import {Directive, inject, OnInit} from '@angular/core'; import {UntypedFormGroup} from '@angular/forms'; import {ActivatedRoute, Router} from '@angular/router'; import {kebabCase} from 'lodash-es'; @@ -85,8 +85,6 @@ export class NaturalAbstractDetail< private readonly _dialogData: unknown = inject(MAT_DIALOG_DATA, {optional: true}); - private readonly destroyRef = inject(DestroyRef); - /** * Once set, this must not change anymore, especially not right after the creation mutation, * so the form does not switch from creation mode to update mode without an actual reload of diff --git a/projects/natural/src/lib/classes/abstract-editable-list.ts b/projects/natural/src/lib/classes/abstract-editable-list.ts index a768099b..a1776e16 100644 --- a/projects/natural/src/lib/classes/abstract-editable-list.ts +++ b/projects/natural/src/lib/classes/abstract-editable-list.ts @@ -2,7 +2,6 @@ import {AbstractControl, UntypedFormArray, UntypedFormGroup} from '@angular/form import {MatTableDataSource} from '@angular/material/table'; import {merge} from 'lodash-es'; import {NaturalAbstractModelService} from '../services/abstract-model.service'; -import {NaturalAbstractController} from './abstract-controller'; import {NaturalQueryVariablesManager, QueryVariables} from './query-variable-manager'; import {ExtractTallOne, ExtractVall, Literal} from '../types/types'; import {validateAllFormControls} from './validators'; @@ -46,15 +45,13 @@ export class NaturalAbstractEditableList< // The Literal here is a bit too loose. Ideally we would like to express // "it must be a union and one of the type in the union must be ExtractTallOne" T extends Literal = ExtractTallOne, -> extends NaturalAbstractController { +> { public readonly form: UntypedFormGroup; public readonly formArray = new UntypedFormArray([]); public readonly variablesManager = new NaturalQueryVariablesManager>(); public readonly dataSource = new MatTableDataSource(); public constructor(protected readonly service: TService) { - super(); - // Create a form group with a line attributes that contain an array of formGroups (one by line = one by model) this.form = new UntypedFormGroup({rows: this.formArray}); this.dataSource.data = this.formArray.controls; diff --git a/projects/natural/src/lib/classes/abstract-list.ts b/projects/natural/src/lib/classes/abstract-list.ts index def932d8..62d098db 100644 --- a/projects/natural/src/lib/classes/abstract-list.ts +++ b/projects/natural/src/lib/classes/abstract-list.ts @@ -1,5 +1,5 @@ import {SelectionModel} from '@angular/cdk/collections'; -import {Directive, inject, Input, OnDestroy, OnInit} from '@angular/core'; +import {Directive, inject, Input, OnInit} from '@angular/core'; import {PageEvent} from '@angular/material/paginator'; import {Sort} from '@angular/material/sort'; import {ActivatedRoute, Data, NavigationEnd, NavigationExtras, NavigationStart, Router} from '@angular/router'; @@ -23,9 +23,10 @@ import { } from './query-variable-manager'; import {ExtractTall, ExtractVall, Literal} from '../types/types'; import {NavigableItem} from './abstract-navigable-list'; -import {filter, takeUntil} from 'rxjs/operators'; +import {filter} from 'rxjs/operators'; import {AvailableColumn} from '../modules/columns-picker/types'; import {validateColumns, validatePagination, validateSorting} from './utility'; +import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; type MaybeNavigable = Literal | NavigableItem; @@ -71,7 +72,7 @@ export class NaturalAbstractList< Tall extends PaginatedData = ExtractTall, > extends NaturalAbstractPanel - implements OnInit, OnDestroy + implements OnInit { /** * Whether search should be loaded from url/storage and persisted in it too. @@ -196,7 +197,7 @@ export class NaturalAbstractList< let isPopState = false; this.router.events .pipe( - takeUntil(this.ngUnsubscribe), + takeUntilDestroyed(this.destroyRef), filter(event => event instanceof NavigationStart && event.navigationTrigger === 'popstate'), ) .subscribe(() => { @@ -205,7 +206,7 @@ export class NaturalAbstractList< this.router.events .pipe( - takeUntil(this.ngUnsubscribe), + takeUntilDestroyed(this.destroyRef), filter(event => event instanceof NavigationEnd && isPopState), ) .subscribe(() => { @@ -477,7 +478,7 @@ export class NaturalAbstractList< // the casting and resolve things in a better way, but that's too much work for now return this.service .watchAll(this.variablesManager as unknown as any) - .pipe(takeUntil(this.ngUnsubscribe)) as unknown as Observable; + .pipe(takeUntilDestroyed(this.destroyRef)) as unknown as Observable; } protected initFromPersisted(): void { diff --git a/projects/natural/src/lib/classes/abstract-navigable-list.ts b/projects/natural/src/lib/classes/abstract-navigable-list.ts index 678783b3..876227ee 100644 --- a/projects/natural/src/lib/classes/abstract-navigable-list.ts +++ b/projects/natural/src/lib/classes/abstract-navigable-list.ts @@ -1,6 +1,6 @@ -import {Directive, Input, OnDestroy, OnInit} from '@angular/core'; +import {Directive, Input, OnInit} from '@angular/core'; import {NavigationExtras, RouterLink} from '@angular/router'; -import {map, takeUntil} from 'rxjs/operators'; +import {map} from 'rxjs/operators'; import {NaturalSearchSelections} from '../modules/search/types/values'; import {NaturalAbstractModelService} from '../services/abstract-model.service'; import {NaturalAbstractList} from './abstract-list'; @@ -9,6 +9,7 @@ import {NaturalQueryVariablesManager, QueryVariables} from './query-variable-man import {ExtractTall, ExtractTallOne, ExtractVall, Literal} from '../types/types'; import {first, Observable} from 'rxjs'; import {FilterGroupCondition} from '../modules/search/classes/graphql-doctrine.types'; +import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; type BreadcrumbItem = { id: string; @@ -42,7 +43,7 @@ export class NaturalAbstractNavigableList< >, > extends NaturalAbstractList['items'][0]>>> - implements OnInit, OnDestroy + implements OnInit { /** * Name of filter for child items to access ancestor item @@ -99,7 +100,7 @@ export class NaturalAbstractNavigableList< protected override getDataObservable(): Observable>>> { return this.service.watchAll(this.variablesManager as unknown as any).pipe( - takeUntil(this.ngUnsubscribe), + takeUntilDestroyed(this.destroyRef), map(result => { // On each data arriving, we query children count to show/hide chevron const navigableItems: NavigableItem>[] = result.items.map(item => { diff --git a/projects/natural/src/lib/modules/common/directives/linkable-tab.directive.ts b/projects/natural/src/lib/modules/common/directives/linkable-tab.directive.ts index c9fd43bc..100a1a03 100644 --- a/projects/natural/src/lib/modules/common/directives/linkable-tab.directive.ts +++ b/projects/natural/src/lib/modules/common/directives/linkable-tab.directive.ts @@ -1,9 +1,7 @@ -import {AfterViewInit, Directive, Input} from '@angular/core'; +import {AfterViewInit, DestroyRef, Directive, inject, Input} from '@angular/core'; import {MatTab, MatTabGroup} from '@angular/material/tabs'; import {ActivatedRoute, RouteConfigLoadEnd, RouteConfigLoadStart, Router} from '@angular/router'; import {clone} from 'lodash-es'; -import {takeUntil} from 'rxjs/operators'; -import {NaturalAbstractController} from '../../../classes/abstract-controller'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; /** @@ -28,7 +26,8 @@ function getTabId(tab: MatTab): string { selector: 'mat-tab-group[naturalLinkableTab]', standalone: true, }) -export class NaturalLinkableTabDirective extends NaturalAbstractController implements AfterViewInit { +export class NaturalLinkableTabDirective implements AfterViewInit { + private readonly destroyRef = inject(DestroyRef); /** * If false, disables the persistent navigation */ @@ -40,8 +39,6 @@ export class NaturalLinkableTabDirective extends NaturalAbstractController imple private readonly route: ActivatedRoute, private readonly router: Router, ) { - super(); - router.events.pipe(takeUntilDestroyed()).subscribe(event => { if (event instanceof RouteConfigLoadStart) { this.isLoadingRouteConfig = true; @@ -57,7 +54,7 @@ export class NaturalLinkableTabDirective extends NaturalAbstractController imple } // When url params change, update the mat-tab-group selected tab - this.route.fragment.pipe(takeUntil(this.ngUnsubscribe)).subscribe(fragment => { + this.route.fragment.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(fragment => { // Get index of tab that matches wanted name const tabIndex = this.getTabIndex(fragment); @@ -69,7 +66,7 @@ export class NaturalLinkableTabDirective extends NaturalAbstractController imple }); // When mat-tab-groups selected tab change, update url - this.component.selectedTabChange.pipe(takeUntil(this.ngUnsubscribe)).subscribe(event => { + this.component.selectedTabChange.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(event => { if (this.isLoadingRouteConfig) { return; } diff --git a/projects/natural/src/lib/modules/dropdown-components/type-select/type-select.component.ts b/projects/natural/src/lib/modules/dropdown-components/type-select/type-select.component.ts index df0f54c1..f2fad13f 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-select/type-select.component.ts +++ b/projects/natural/src/lib/modules/dropdown-components/type-select/type-select.component.ts @@ -1,16 +1,16 @@ -import {AfterViewInit, Component, Inject, OnDestroy, ViewChild} from '@angular/core'; -import {MatSelectionList, MatListModule} from '@angular/material/list'; +import {AfterViewInit, Component, DestroyRef, inject, Inject, ViewChild} from '@angular/core'; +import {MatListModule, MatSelectionList} from '@angular/material/list'; import {BehaviorSubject, merge, Observable, of} from 'rxjs'; import {FilterGroupConditionField, Scalar} from '../../search/classes/graphql-doctrine.types'; import {NATURAL_DROPDOWN_DATA, NaturalDropdownData} from '../../search/dropdown-container/dropdown.service'; import {DropdownComponent} from '../../search/types/dropdown-component'; -import {FormControl, FormGroup, ValidatorFn, Validators, FormsModule, ReactiveFormsModule} from '@angular/forms'; -import {NaturalAbstractController} from '../../../classes/abstract-controller'; -import {map, startWith, takeUntil} from 'rxjs/operators'; +import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, ValidatorFn, Validators} from '@angular/forms'; +import {map, startWith} from 'rxjs/operators'; import {PossibleDiscreteOperatorKeys, possibleDiscreteOperators} from '../types'; import {MatOptionModule} from '@angular/material/core'; import {MatSelectModule} from '@angular/material/select'; import {MatFormFieldModule} from '@angular/material/form-field'; +import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; export type TypeSelectItem = | Scalar @@ -37,10 +37,8 @@ export type TypeSelectConfiguration = { standalone: true, imports: [FormsModule, ReactiveFormsModule, MatFormFieldModule, MatSelectModule, MatOptionModule, MatListModule], }) -export class TypeSelectComponent - extends NaturalAbstractController - implements DropdownComponent, AfterViewInit, OnDestroy -{ +export class TypeSelectComponent implements DropdownComponent, AfterViewInit { + private readonly destroyRef = inject(DestroyRef); public readonly renderedValue = new BehaviorSubject(''); @ViewChild(MatSelectionList, {static: false}) public list!: MatSelectionList; public requireValueCtrl = false; @@ -62,7 +60,6 @@ export class TypeSelectComponent }; public constructor(@Inject(NATURAL_DROPDOWN_DATA) data: NaturalDropdownData) { - super(); this.configuration = {...this.defaults, ...data.configuration}; // Immediately initValidators and everytime the operator change later @@ -156,7 +153,7 @@ export class TypeSelectComponent : this.configuration.items; return items$.pipe( - takeUntil(this.ngUnsubscribe), + takeUntilDestroyed(this.destroyRef), map(items => { this.items = items; diff --git a/projects/natural/src/lib/modules/file/abstract-file.ts b/projects/natural/src/lib/modules/file/abstract-file.ts index 7d334eb5..fd202995 100644 --- a/projects/natural/src/lib/modules/file/abstract-file.ts +++ b/projects/natural/src/lib/modules/file/abstract-file.ts @@ -22,7 +22,6 @@ import { stopEvent, } from './utils'; import {NaturalFileService} from './file.service'; -import {NaturalAbstractController} from '../../classes/abstract-controller'; import {DOCUMENT} from '@angular/common'; import {forkJoin, map, Observable, ObservableInput, of, tap} from 'rxjs'; @@ -53,7 +52,7 @@ export type FileSelection = { * @dynamic */ @Directive({standalone: true}) -export abstract class NaturalAbstractFile extends NaturalAbstractController implements OnInit, OnDestroy, OnChanges { +export abstract class NaturalAbstractFile implements OnInit, OnDestroy, OnChanges { private fileElement?: HTMLInputElement; /** @@ -112,12 +111,9 @@ export abstract class NaturalAbstractFile extends NaturalAbstractController impl private readonly element: ElementRef, protected readonly naturalFileService: NaturalFileService, @Inject(DOCUMENT) private readonly document: Document, - ) { - super(); - } + ) {} - public override ngOnDestroy(): void { - super.ngOnDestroy(); + public ngOnDestroy(): void { delete this.fileElement; // faster memory release of dom element } diff --git a/projects/natural/src/lib/modules/file/file-drop.directive.ts b/projects/natural/src/lib/modules/file/file-drop.directive.ts index 719e3720..3bcd2b38 100644 --- a/projects/natural/src/lib/modules/file/file-drop.directive.ts +++ b/projects/natural/src/lib/modules/file/file-drop.directive.ts @@ -1,8 +1,9 @@ -import {Directive, EventEmitter, HostBinding, HostListener, OnInit, Output} from '@angular/core'; +import {DestroyRef, Directive, EventEmitter, HostBinding, HostListener, inject, OnInit, Output} from '@angular/core'; import {NaturalAbstractFile} from './abstract-file'; import {eventToFiles, stopEvent} from './utils'; import {asyncScheduler, Subject} from 'rxjs'; -import {takeUntil, throttleTime} from 'rxjs/operators'; +import {throttleTime} from 'rxjs/operators'; +import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; /** * This directive has all options to select files, and adds support for drag'n'drop. @@ -22,6 +23,7 @@ import {takeUntil, throttleTime} from 'rxjs/operators'; standalone: true, }) export class NaturalFileDropDirective extends NaturalAbstractFile implements OnInit { + private readonly destroyRef = inject(DestroyRef); @HostBinding('class.natural-file-over') public fileOverClass = false; /** @@ -40,7 +42,7 @@ export class NaturalFileDropDirective extends NaturalAbstractFile implements OnI // still see flicker, but it should be better for most normal usages. this.rawFileOver .pipe( - takeUntil(this.ngUnsubscribe), + takeUntilDestroyed(this.destroyRef), throttleTime(200, asyncScheduler, { leading: true, trailing: true, diff --git a/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.ts b/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.ts index bf10c6b0..15791b1b 100644 --- a/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.ts +++ b/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.ts @@ -1,10 +1,19 @@ import {SelectionModel} from '@angular/cdk/collections'; import {FlatTreeControl} from '@angular/cdk/tree'; -import {Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges} from '@angular/core'; +import { + Component, + DestroyRef, + EventEmitter, + inject, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges, +} from '@angular/core'; import {MatTreeFlatDataSource, MatTreeFlattener, MatTreeModule} from '@angular/material/tree'; import {Observable} from 'rxjs'; -import {finalize, takeUntil} from 'rxjs/operators'; -import {NaturalAbstractController} from '../../../classes/abstract-controller'; +import {finalize} from 'rxjs/operators'; import {QueryVariables} from '../../../classes/query-variable-manager'; import {Literal} from '../../../types/types'; import {toGraphQLDoctrineFilter} from '../../search/classes/graphql-doctrine'; @@ -24,6 +33,7 @@ import {MatButtonModule} from '@angular/material/button'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; import {CommonModule} from '@angular/common'; import {NaturalSearchComponent} from '../../search/search/search.component'; +import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; @Component({ selector: 'natural-hierarchic-selector', @@ -43,7 +53,8 @@ import {NaturalSearchComponent} from '../../search/search/search.component'; MatChipsModule, ], }) -export class NaturalHierarchicSelectorComponent extends NaturalAbstractController implements OnInit, OnChanges { +export class NaturalHierarchicSelectorComponent implements OnInit, OnChanges { + private readonly destroyRef = inject(DestroyRef); /** * Function that receives a model and returns a string for display value */ @@ -117,9 +128,7 @@ export class NaturalHierarchicSelectorComponent extends NaturalAbstractControlle */ private flatNodeMap: Map = new Map(); - public constructor(private readonly hierarchicSelectorService: NaturalHierarchicSelectorService) { - super(); - } + public constructor(private readonly hierarchicSelectorService: NaturalHierarchicSelectorService) {} /** * Angular OnChange implementation @@ -157,7 +166,7 @@ export class NaturalHierarchicSelectorComponent extends NaturalAbstractControlle // Update dataSource when receiving new list -> we assign the whole tree // The treeControl and treeFlattener will generate the displayed tree this.hierarchicSelectorService.dataChange - .pipe(takeUntil(this.ngUnsubscribe)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(data => (this.dataSource.data = data)); // Prevent empty screen on first load and init NaturalHierarchicSelectorService with inputted configuration diff --git a/projects/natural/src/lib/modules/panels/abstract-panel.ts b/projects/natural/src/lib/modules/panels/abstract-panel.ts index 016ad011..f4c08535 100644 --- a/projects/natural/src/lib/modules/panels/abstract-panel.ts +++ b/projects/natural/src/lib/modules/panels/abstract-panel.ts @@ -1,11 +1,12 @@ -import {Directive, HostBinding, HostListener} from '@angular/core'; -import {NaturalAbstractController} from '../../classes/abstract-controller'; +import {DestroyRef, Directive, HostBinding, HostListener, inject} from '@angular/core'; import {NaturalPanelsService} from './panels.service'; import {NaturalPanelData} from './types'; -import {Observable, takeUntil} from 'rxjs'; +import {Observable} from 'rxjs'; +import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; @Directive({standalone: true}) -export class NaturalAbstractPanel extends NaturalAbstractController { +export class NaturalAbstractPanel { + protected readonly destroyRef = inject(DestroyRef); /** * The data property is the container where the resolved content is stored * When loading a component from a panel opening (dialog), receives the data provided by the service @@ -50,7 +51,7 @@ export class NaturalAbstractPanel extends NaturalAbstractController { if (this.panelData?.data) { if (this.panelData.data.model instanceof Observable) { // Subscribe to model to know when Apollo cache is changed, so we can reflect it into `data.model` - this.panelData.data.model.pipe(takeUntil(this.ngUnsubscribe)).subscribe(model => { + this.panelData.data.model.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(model => { this.data = { ...this.data, ...this.panelData?.data, diff --git a/projects/natural/src/lib/modules/relations/relations.component.ts b/projects/natural/src/lib/modules/relations/relations.component.ts index 7f3f1e8e..71f8cabd 100644 --- a/projects/natural/src/lib/modules/relations/relations.component.ts +++ b/projects/natural/src/lib/modules/relations/relations.component.ts @@ -1,7 +1,9 @@ import { Component, ContentChild, + DestroyRef, EventEmitter, + inject, Input, OnChanges, OnInit, @@ -10,7 +12,6 @@ import { ViewChild, } from '@angular/core'; import {MatPaginatorModule, PageEvent} from '@angular/material/paginator'; -import {NaturalAbstractController} from '../../classes/abstract-controller'; import {NaturalDataSource, PaginatedData} from '../../classes/data-source'; import {NaturalQueryVariablesManager, PaginationInput, QueryVariables} from '../../classes/query-variable-manager'; import {HierarchicFiltersConfiguration} from '../../modules/hierarchic-selector/classes/hierarchic-filters-configuration'; @@ -28,7 +29,8 @@ import {MatTooltipModule} from '@angular/material/tooltip'; import {MatButtonModule} from '@angular/material/button'; import {MatTableModule} from '@angular/material/table'; import {CommonModule} from '@angular/common'; -import {finalize, forkJoin, takeUntil, tap} from 'rxjs'; +import {finalize, forkJoin, tap} from 'rxjs'; +import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; /** * Custom template usage : @@ -72,9 +74,9 @@ export class NaturalRelationsComponent< any >, > - extends NaturalAbstractController implements OnInit, OnChanges { + private readonly destroyRef = inject(DestroyRef); @ViewChild(NaturalSelectComponent) private select?: NaturalSelectComponent; @ContentChild(TemplateRef) public itemTemplate?: TemplateRef; @@ -89,7 +91,7 @@ export class NaturalRelationsComponent< this._service = service; this.loading = true; const items$ = this._service.watchAll(this.variablesManager).pipe( - takeUntil(this.ngUnsubscribe), + takeUntilDestroyed(this.destroyRef), tap({ next: () => (this.loading = false), complete: () => (this.loading = false), @@ -172,9 +174,7 @@ export class NaturalRelationsComponent< public constructor( private readonly linkMutationService: NaturalLinkMutationService, private readonly hierarchicSelectorDialog: NaturalHierarchicSelectorDialogService, - ) { - super(); - } + ) {} /** * The filter used to filter relations diff --git a/projects/natural/src/lib/modules/select/abstract-select.component.ts b/projects/natural/src/lib/modules/select/abstract-select.component.ts index bfd91834..ea401ffa 100644 --- a/projects/natural/src/lib/modules/select/abstract-select.component.ts +++ b/projects/natural/src/lib/modules/select/abstract-select.component.ts @@ -1,5 +1,5 @@ import {coerceBooleanProperty} from '@angular/cdk/coercion'; -import {Directive, DoCheck, EventEmitter, Input, OnDestroy, OnInit, Optional, Output, Self} from '@angular/core'; +import {Directive, DoCheck, EventEmitter, Input, OnInit, Optional, Output, Self} from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -10,7 +10,6 @@ import { Validators, } from '@angular/forms'; import {ErrorStateMatcher} from '@angular/material/core'; -import {NaturalAbstractController} from '../../classes/abstract-controller'; /** * This will completely ignore internal formControl and instead use the one from the component @@ -34,10 +33,7 @@ class ExternalFormControlMatcher extends ErrorStateMatcher { } @Directive({standalone: true}) -export abstract class AbstractSelect - extends NaturalAbstractController - implements OnInit, OnDestroy, ControlValueAccessor, DoCheck -{ +export abstract class AbstractSelect implements OnInit, ControlValueAccessor, DoCheck { @Input() public placeholder?: string; /** @@ -127,8 +123,6 @@ export abstract class AbstractSelect public readonly matcher: ExternalFormControlMatcher; public constructor(@Optional() @Self() public readonly ngControl: NgControl | null) { - super(); - if (this.ngControl) { this.ngControl.valueAccessor = this; } diff --git a/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.ts b/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.ts index e4425c1f..f79cdb49 100644 --- a/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.ts +++ b/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.ts @@ -1,5 +1,5 @@ -import {Component, Input, OnDestroy, OnInit, Optional, Self} from '@angular/core'; -import {ControlValueAccessor, NgControl, FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {Component, Input, OnInit, Optional, Self} from '@angular/core'; +import {ControlValueAccessor, FormsModule, NgControl, ReactiveFormsModule} from '@angular/forms'; import {MatDialogConfig} from '@angular/material/dialog'; import {Literal} from '../../../types/types'; import {HierarchicFiltersConfiguration} from '../../hierarchic-selector/classes/hierarchic-filters-configuration'; @@ -64,7 +64,7 @@ function defaultDisplayFn(item: Literal | null): string { }) export class NaturalSelectHierarchicComponent extends AbstractSelect - implements OnInit, OnDestroy, ControlValueAccessor + implements OnInit, ControlValueAccessor { /** * If provided cause a new select button to appear diff --git a/projects/natural/src/lib/modules/select/select/select.component.ts b/projects/natural/src/lib/modules/select/select/select.component.ts index b95d2c54..7cafb316 100644 --- a/projects/natural/src/lib/modules/select/select/select.component.ts +++ b/projects/natural/src/lib/modules/select/select/select.component.ts @@ -1,9 +1,19 @@ -import {AfterViewInit, Component, ContentChild, Input, OnDestroy, OnInit, TemplateRef, ViewChild} from '@angular/core'; +import { + AfterViewInit, + Component, + ContentChild, + DestroyRef, + inject, + Input, + OnInit, + TemplateRef, + ViewChild, +} from '@angular/core'; import {ControlValueAccessor, FormsModule, ReactiveFormsModule} from '@angular/forms'; import {MatAutocompleteModule, MatAutocompleteTrigger} from '@angular/material/autocomplete'; import {merge} from 'lodash-es'; import {Observable} from 'rxjs'; -import {debounceTime, distinctUntilChanged, finalize, map, takeUntil} from 'rxjs/operators'; +import {debounceTime, distinctUntilChanged, finalize, map} from 'rxjs/operators'; import {PaginatedData} from '../../../classes/data-source'; import {NaturalQueryVariablesManager, QueryVariables} from '../../../classes/query-variable-manager'; import {NaturalAbstractModelService} from '../../../services/abstract-model.service'; @@ -19,6 +29,7 @@ import {MatInputModule} from '@angular/material/input'; import {MatFormFieldModule} from '@angular/material/form-field'; import {MatOptionModule} from '@angular/material/core'; import {CommonModule} from '@angular/common'; +import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; type V = string | ExtractTallOne; @@ -90,8 +101,9 @@ export class NaturalSelectComponent< >, > extends AbstractSelect, V> - implements OnInit, OnDestroy, ControlValueAccessor, AfterViewInit + implements OnInit, ControlValueAccessor, AfterViewInit { + private readonly destroyRef = inject(DestroyRef); @ViewChild(MatAutocompleteTrigger) public autoTrigger!: MatAutocompleteTrigger; @ContentChild(TemplateRef) public itemTemplate?: TemplateRef; @@ -174,7 +186,7 @@ export class NaturalSelectComponent< public ngAfterViewInit(): void { this.internalCtrl.valueChanges - .pipe(takeUntil(this.ngUnsubscribe), distinctUntilChanged(), debounceTime(300)) + .pipe(takeUntilDestroyed(this.destroyRef), distinctUntilChanged(), debounceTime(300)) .subscribe(val => this.search(val)); } @@ -244,7 +256,7 @@ export class NaturalSelectComponent< // Init query, and when query results arrive, finish loading, and count items this.items = this.service.watchAll(this.variablesManager).pipe( - takeUntil(this.ngUnsubscribe), + takeUntilDestroyed(this.destroyRef), finalize(() => (this.loading = false)), map(data => { this.loading = false; diff --git a/projects/natural/src/lib/modules/sidenav/sidenav.service.ts b/projects/natural/src/lib/modules/sidenav/sidenav.service.ts index 8ab14bf9..f2d5fdb8 100644 --- a/projects/natural/src/lib/modules/sidenav/sidenav.service.ts +++ b/projects/natural/src/lib/modules/sidenav/sidenav.service.ts @@ -1,12 +1,12 @@ -import {Inject, Injectable} from '@angular/core'; +import {DestroyRef, Inject, inject, Injectable} from '@angular/core'; import {MatDrawerMode} from '@angular/material/sidenav'; import {NavigationEnd, Router} from '@angular/router'; -import {filter, takeUntil} from 'rxjs/operators'; -import {NaturalAbstractController} from '../../classes/abstract-controller'; +import {filter} from 'rxjs/operators'; import {NaturalSidenavContainerComponent} from './sidenav-container/sidenav-container.component'; import {NaturalStorage, SESSION_STORAGE} from '../common/services/memory-storage'; import {NaturalSidenavStackService} from './sidenav-stack.service'; import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout'; +import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; /** * Assert that given value is not null @@ -24,7 +24,8 @@ function assert(value: T): asserts value { * Maybe the better is to wait next release */ @Injectable({providedIn: 'root'}) -export class NaturalSidenavService extends NaturalAbstractController { +export class NaturalSidenavService { + private readonly destroyRef = inject(DestroyRef); /** * Navigation modes * First is for desktop view @@ -72,9 +73,7 @@ export class NaturalSidenavService extends NaturalAbstractController { private readonly router: Router, @Inject(SESSION_STORAGE) private readonly sessionStorage: NaturalStorage, private readonly naturalSidenavStackService: NaturalSidenavStackService, - ) { - super(); - } + ) {} public get activeMode(): MatDrawerMode { return this.mode; @@ -111,7 +110,7 @@ export class NaturalSidenavService extends NaturalAbstractController { this.breakpointObserver .observe([Breakpoints.XSmall, Breakpoints.Small]) - .pipe(takeUntil(this.ngUnsubscribe)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(r => { this._isMobileView = r.matches; const isBig = !this._isMobileView; @@ -137,7 +136,7 @@ export class NaturalSidenavService extends NaturalAbstractController { if (autoClose) { this.router.events .pipe( - takeUntil(this.ngUnsubscribe), + takeUntilDestroyed(this.destroyRef), filter(e => e instanceof NavigationEnd), ) .subscribe(() => { diff --git a/projects/natural/src/public-api.ts b/projects/natural/src/public-api.ts index eaa1e784..2972d134 100644 --- a/projects/natural/src/public-api.ts +++ b/projects/natural/src/public-api.ts @@ -4,7 +4,6 @@ * Public API Surface of natural */ -export * from './lib/classes/abstract-controller'; export * from './lib/classes/abstract-detail'; export * from './lib/classes/abstract-editable-list'; export * from './lib/classes/abstract-list';