From 150e5829361faefad7dbbe2ed8705ec8d04231b0 Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Sun, 29 Dec 2024 17:17:31 +0100 Subject: [PATCH] refactor: migrate to signals for querries --- src/app/app.component.ts | 6 +- .../main-header/main-header.component.ts | 13 +-- .../side-nav-item/side-nav-item.component.ts | 7 +- .../core-ui/side-nav/side-nav.component.ts | 28 +++--- .../bookmark-bar/bookmark-bar.component.ts | 2 + .../config-section.component.ts | 12 +-- .../issue-panel-calendar-agenda.component.ts | 4 +- .../issue-provider-tab.component.ts | 8 +- src/app/features/note/note/note.component.ts | 4 +- .../features/note/notes/notes.component.ts | 4 +- .../dialog-schedule-task.component.ts | 10 +- .../planner-task/planner-task.component.ts | 9 +- .../create-task-placeholder.component.ts | 16 ++-- .../schedule-event.component.ts | 9 +- .../schedule/schedule/schedule.component.ts | 6 +- .../search-bar/search-bar.component.ts | 18 ++-- .../tag/tag-edit/tag-edit.component.ts | 21 +++-- .../add-task-bar/add-task-bar.component.ts | 24 +++-- .../task-context-menu-inner.component.ts | 11 ++- .../task-context-menu.component.ts | 10 +- .../task-detail-panel.component.ts | 39 ++++---- .../tasks/task-list/task-list.component.ts | 8 +- src/app/features/tasks/task/task.component.ts | 92 ++++++++++--------- .../work-view/split/split.component.ts | 4 +- .../features/work-view/work-view.component.ts | 2 + src/app/imex/file-imex/file-imex.component.ts | 13 +-- .../better-drawer-container.component.ts | 2 + .../better-simple-drawer.component.ts | 2 + .../chip-list-input.component.ts | 18 ++-- .../ui/context-menu/context-menu.component.ts | 12 ++- .../input-duration-formly.component.ts | 11 ++- .../input-duration-slider.component.ts | 22 +++-- .../formly-translated-template.component.ts | 11 ++- .../ui/inline-input/inline-input.component.ts | 14 +-- .../inline-markdown.component.ts | 57 ++++++------ .../inline-multiline-input.component.ts | 21 +++-- .../progress-circle.component.ts | 9 +- 37 files changed, 293 insertions(+), 266 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b32f202cad..215a321e8c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -4,8 +4,8 @@ import { HostBinding, HostListener, OnDestroy, - ViewChild, ViewContainerRef, + viewChild, } from '@angular/core'; import { ChromeExtensionInterfaceService } from './core/chrome-extension-interface/chrome-extension-interface.service'; import { ShortcutService } from './core-ui/shortcut/shortcut.service'; @@ -65,8 +65,8 @@ export class AppComponent implements OnDestroy { @HostBinding('@.disabled') isDisableAnimations = false; - @ViewChild('notesElRef', { read: ViewContainerRef }) notesElRef?: ViewContainerRef; - @ViewChild('sideNavElRef', { read: ViewContainerRef }) sideNavElRef?: ViewContainerRef; + readonly notesElRef = viewChild('notesElRef', { read: ViewContainerRef }); + readonly sideNavElRef = viewChild('sideNavElRef', { read: ViewContainerRef }); isRTL: boolean = false; diff --git a/src/app/core-ui/main-header/main-header.component.ts b/src/app/core-ui/main-header/main-header.component.ts index 4747de1080..f9df4d7505 100644 --- a/src/app/core-ui/main-header/main-header.component.ts +++ b/src/app/core-ui/main-header/main-header.component.ts @@ -5,7 +5,7 @@ import { OnDestroy, OnInit, Renderer2, - ViewChild, + viewChild, } from '@angular/core'; import { ProjectService } from '../../features/project/project.service'; import { LayoutService } from '../layout/layout.service'; @@ -44,7 +44,7 @@ export class MainHeaderComponent implements OnInit, OnDestroy { circumference: number = this.progressCircleRadius * Math.PI * 2; isShowSimpleCounterBtnsMobile: boolean = false; - @ViewChild('circleSvg', { static: true }) circleSvg?: ElementRef; + readonly circleSvg = viewChild('circleSvg'); currentTaskContext$: Observable = this.taskService.currentTaskParentOrCurrent$.pipe( @@ -104,17 +104,14 @@ export class MainHeaderComponent implements OnInit, OnDestroy { ngOnInit(): void { this.taskService.currentTaskProgress$.subscribe((progressIN) => { - if (this.circleSvg) { + const circleSvg = this.circleSvg(); + if (circleSvg) { let progress = progressIN || 1; if (progress > 1) { progress = 1; } const dashOffset = this.circumference * -1 * progress; - this._renderer.setStyle( - this.circleSvg.nativeElement, - 'stroke-dashoffset', - dashOffset, - ); + this._renderer.setStyle(circleSvg.nativeElement, 'stroke-dashoffset', dashOffset); } }); } diff --git a/src/app/core-ui/side-nav/side-nav-item/side-nav-item.component.ts b/src/app/core-ui/side-nav/side-nav-item/side-nav-item.component.ts index f2886c7782..d7e68ac3f2 100644 --- a/src/app/core-ui/side-nav/side-nav-item/side-nav-item.component.ts +++ b/src/app/core-ui/side-nav/side-nav-item/side-nav-item.component.ts @@ -4,7 +4,7 @@ import { ElementRef, HostBinding, input, - ViewChild, + viewChild, } from '@angular/core'; import { RouterLink, RouterModule } from '@angular/router'; import { UiModule } from '../../../ui/ui.module'; @@ -41,8 +41,7 @@ export class SideNavItemComponent { contextMenuPosition: { x: string; y: string } = { x: '0px', y: '0px' }; - @ViewChild('routeBtn', { static: true, read: ElementRef }) - routeBtn!: ElementRef; + readonly routeBtn = viewChild.required('routeBtn', { read: ElementRef }); @HostBinding('class.hasTasks') get workContextHasTasks(): boolean { @@ -60,6 +59,6 @@ export class SideNavItemComponent { } focus(): void { - this.routeBtn.nativeElement.focus(); + this.routeBtn().nativeElement.focus(); } } diff --git a/src/app/core-ui/side-nav/side-nav.component.ts b/src/app/core-ui/side-nav/side-nav.component.ts index 6090fee24d..d8dd402b41 100644 --- a/src/app/core-ui/side-nav/side-nav.component.ts +++ b/src/app/core-ui/side-nav/side-nav.component.ts @@ -5,9 +5,8 @@ import { HostBinding, HostListener, OnDestroy, - QueryList, - ViewChild, - ViewChildren, + viewChildren, + viewChild, } from '@angular/core'; import { ProjectService } from '../../features/project/project.service'; import { T } from '../../t.const'; @@ -53,13 +52,13 @@ import { updateProject } from '../../features/project/store/project.actions'; standalone: false, }) export class SideNavComponent implements OnDestroy { - @ViewChildren('menuEntry') navEntries?: QueryList; + readonly navEntries = viewChildren('menuEntry'); IS_MOUSE_PRIMARY = IS_MOUSE_PRIMARY; IS_TOUCH_PRIMARY = IS_TOUCH_PRIMARY; DRAG_DELAY_FOR_TOUCH_LONGER = DRAG_DELAY_FOR_TOUCH_LONGER; keyboardFocusTimeout?: number; - @ViewChild('projectExpandBtn', { read: ElementRef }) projectExpandBtn?: ElementRef; + readonly projectExpandBtn = viewChild('projectExpandBtn', { read: ElementRef }); isProjectsExpanded: boolean = this.fetchProjectListState(); isProjectsExpanded$: BehaviorSubject = new BehaviorSubject( this.isProjectsExpanded, @@ -77,7 +76,7 @@ export class SideNavComponent implements OnDestroy { ), ); - @ViewChild('tagExpandBtn', { read: ElementRef }) tagExpandBtn?: ElementRef; + readonly tagExpandBtn = viewChild('tagExpandBtn', { read: ElementRef }); isTagsExpanded: boolean = this.fetchTagListState(); isTagsExpanded$: BehaviorSubject = new BehaviorSubject( this.isTagsExpanded, @@ -123,9 +122,10 @@ export class SideNavComponent implements OnDestroy { this._subs.add( this._layoutService.isShowSideNav$.subscribe((isShow) => { - if (this.navEntries && isShow) { + const navEntries = this.navEntries(); + if (navEntries && isShow) { this.keyManager = new FocusKeyManager( - this.navEntries, + navEntries, ).withVerticalOrientation(true); window.clearTimeout(this.keyboardFocusTimeout); this.keyboardFocusTimeout = window.setTimeout(() => { @@ -190,9 +190,9 @@ export class SideNavComponent implements OnDestroy { } checkFocusProject(ev: KeyboardEvent): void { - if (ev.key === 'ArrowLeft' && this.projectExpandBtn?.nativeElement) { - const targetIndex = this.navEntries?.toArray().findIndex((value) => { - return value._getHostElement() === this.projectExpandBtn?.nativeElement; + if (ev.key === 'ArrowLeft' && this.projectExpandBtn()?.nativeElement) { + const targetIndex = this.navEntries().findIndex((value) => { + return value._getHostElement() === this.projectExpandBtn()?.nativeElement; }); if (targetIndex) { this.keyManager?.setActiveItem(targetIndex); @@ -217,9 +217,9 @@ export class SideNavComponent implements OnDestroy { } checkFocusTag(ev: KeyboardEvent): void { - if (ev.key === 'ArrowLeft' && this.tagExpandBtn?.nativeElement) { - const targetIndex = this.navEntries?.toArray().findIndex((value) => { - return value._getHostElement() === this.tagExpandBtn?.nativeElement; + if (ev.key === 'ArrowLeft' && this.tagExpandBtn()?.nativeElement) { + const targetIndex = this.navEntries().findIndex((value) => { + return value._getHostElement() === this.tagExpandBtn()?.nativeElement; }); if (targetIndex) { this.keyManager?.setActiveItem(targetIndex); diff --git a/src/app/features/bookmark/bookmark-bar/bookmark-bar.component.ts b/src/app/features/bookmark/bookmark-bar/bookmark-bar.component.ts index 253e54ca78..4325d43d83 100644 --- a/src/app/features/bookmark/bookmark-bar/bookmark-bar.component.ts +++ b/src/app/features/bookmark/bookmark-bar/bookmark-bar.component.ts @@ -36,6 +36,8 @@ export class BookmarkBarComponent { private readonly _matDialog: MatDialog, ) {} + // TODO: Skipped for migration because: + // Accessor queries cannot be migrated as they are too complex. @ViewChild('bookmarkBar', { read: ElementRef }) set bookmarkBarEl(content: ElementRef) { if (content && content.nativeElement) { this.bookmarkBarHeight = content.nativeElement.offsetHeight; diff --git a/src/app/features/config/config-section/config-section.component.ts b/src/app/features/config/config-section/config-section.component.ts index 6bcdf5c241..13f67107a8 100644 --- a/src/app/features/config/config-section/config-section.component.ts +++ b/src/app/features/config/config-section/config-section.component.ts @@ -9,8 +9,8 @@ import { OnDestroy, OnInit, Output, - ViewChild, ViewContainerRef, + viewChild, } from '@angular/core'; import { expandAnimation } from '../../../ui/animations/expand.ani'; import { @@ -40,8 +40,7 @@ export class ConfigSectionComponent implements OnInit, OnDestroy { sectionKey: GlobalConfigSectionKey | ProjectCfgFormKey | TagCfgFormKey; config: any; }> = new EventEmitter(); - @ViewChild('customForm', { read: ViewContainerRef, static: true }) - customFormRef?: ViewContainerRef; + readonly customFormRef = viewChild('customForm', { read: ViewContainerRef }); isExpanded: boolean = false; private _subs: Subscription = new Subscription(); private _instance?: Component; @@ -84,13 +83,14 @@ export class ConfigSectionComponent implements OnInit, OnDestroy { this._workContextService.onWorkContextChange$.subscribe(() => { this._cd.markForCheck(); + const customFormRef = this.customFormRef(); if ( this.section && this.section.customSection && - this.customFormRef && + customFormRef && this.section.customSection ) { - this.customFormRef.clear(); + customFormRef.clear(); // dirty trick to make sure data is actually there this._viewDestroyTimeout = window.setTimeout(() => { this._loadCustomSection((this.section as any).customSection); @@ -126,7 +126,7 @@ export class ConfigSectionComponent implements OnInit, OnDestroy { if (componentToRender) { const factory: ComponentFactory = this._componentFactoryResolver.resolveComponentFactory(componentToRender as any); - const ref = exists(this.customFormRef).createComponent(factory); + const ref = exists(this.customFormRef()).createComponent(factory); // NOTE: important that this is set only if we actually have a value // otherwise the default fallback will be overwritten diff --git a/src/app/features/issue-panel/issue-panel-calendar-agenda/issue-panel-calendar-agenda.component.ts b/src/app/features/issue-panel/issue-panel-calendar-agenda/issue-panel-calendar-agenda.component.ts index c2479c9a65..a16a46c4d2 100644 --- a/src/app/features/issue-panel/issue-panel-calendar-agenda/issue-panel-calendar-agenda.component.ts +++ b/src/app/features/issue-panel/issue-panel-calendar-agenda/issue-panel-calendar-agenda.component.ts @@ -5,7 +5,7 @@ import { input, OnInit, signal, - ViewChild, + viewChild, } from '@angular/core'; import { ErrorCardComponent } from '../../../ui/error-card/error-card.component'; import { IssuePreviewItemComponent } from '../issue-preview-item/issue-preview-item.component'; @@ -39,7 +39,7 @@ export class IssuePanelCalendarAgendaComponent implements OnInit { error = signal(undefined); isLoading = signal(false); - @ViewChild(CdkDropList) dropList?: CdkDropList; + readonly dropList = viewChild(CdkDropList); agendaItems = signal< { diff --git a/src/app/features/issue-panel/issue-provider-tab/issue-provider-tab.component.ts b/src/app/features/issue-panel/issue-provider-tab/issue-provider-tab.component.ts index 1e58339f11..50d2c19171 100644 --- a/src/app/features/issue-panel/issue-provider-tab/issue-provider-tab.component.ts +++ b/src/app/features/issue-panel/issue-provider-tab/issue-provider-tab.component.ts @@ -8,7 +8,7 @@ import { input, OnDestroy, signal, - ViewChild, + viewChild, } from '@angular/core'; import { IssuePreviewItemComponent } from '../issue-preview-item/issue-preview-item.component'; import { MatIcon } from '@angular/material/icon'; @@ -173,8 +173,8 @@ export class IssueProviderTabComponent implements OnDestroy, AfterViewInit { ); issueItems = toSignal(this.issueItems$.pipe(map((v) => v.notAdded))); - @ViewChild(CdkDropList) dropList?: CdkDropList; - @ViewChild('searchTextEl') searchTextEl!: ElementRef; + readonly dropList = viewChild(CdkDropList); + readonly searchTextEl = viewChild.required('searchTextEl'); private _focusTimeout?: number; @@ -182,7 +182,7 @@ export class IssueProviderTabComponent implements OnDestroy, AfterViewInit { // this.dropListService.registerDropList(this.dropList!); if (this.searchText().length <= 1 && IS_MOUSE_PRIMARY) { this._focusTimeout = window.setTimeout(() => { - this.searchTextEl?.nativeElement.focus(); + this.searchTextEl()?.nativeElement.focus(); }, 500); } this.searchText.set(this.issueProvider().pinnedSearch || ''); diff --git a/src/app/features/note/note/note.component.ts b/src/app/features/note/note/note.component.ts index 2daeabe705..2e8559aae2 100644 --- a/src/app/features/note/note/note.component.ts +++ b/src/app/features/note/note/note.component.ts @@ -4,7 +4,7 @@ import { Input, OnChanges, SimpleChanges, - ViewChild, + viewChild, } from '@angular/core'; import { Note } from '../note.model'; import { NoteService } from '../note.service'; @@ -37,7 +37,7 @@ export class NoteComponent implements OnChanges { @Input() isFocus?: boolean; - @ViewChild('markdownEl') markdownEl?: HTMLElement; + readonly markdownEl = viewChild('markdownEl'); isLongNote?: boolean; shortenedNote?: string; diff --git a/src/app/features/note/notes/notes.component.ts b/src/app/features/note/notes/notes.component.ts index 9d4bbede15..f53c6d27f3 100644 --- a/src/app/features/note/notes/notes.component.ts +++ b/src/app/features/note/notes/notes.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, HostListener, - ViewChild, + viewChild, } from '@angular/core'; import { NoteService } from '../note.service'; import { MatButton } from '@angular/material/button'; @@ -30,7 +30,7 @@ export class NotesComponent { isDragOver: boolean = false; dragEnterTarget?: HTMLElement; - @ViewChild('buttonEl', { static: true }) buttonEl?: MatButton; + readonly buttonEl = viewChild('buttonEl'); constructor( public noteService: NoteService, diff --git a/src/app/features/planner/dialog-schedule-task/dialog-schedule-task.component.ts b/src/app/features/planner/dialog-schedule-task/dialog-schedule-task.component.ts index 8858c19526..dcde402dc1 100644 --- a/src/app/features/planner/dialog-schedule-task/dialog-schedule-task.component.ts +++ b/src/app/features/planner/dialog-schedule-task/dialog-schedule-task.component.ts @@ -4,7 +4,7 @@ import { ChangeDetectorRef, Component, Inject, - ViewChild, + viewChild, } from '@angular/core'; import { UiModule } from '../../../ui/ui.module'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; @@ -52,7 +52,7 @@ import { WorkContextService } from '../../work-context/work-context.service'; export class DialogScheduleTaskComponent implements AfterViewInit { T: typeof T = T; minDate = new Date(); - @ViewChild(MatCalendar, { static: true }) calendar!: MatCalendar; + readonly calendar = viewChild.required(MatCalendar); remindAvailableOptions: TaskReminderOption[] = TASK_REMINDER_OPTIONS; task: TaskCopy = this.data.task; @@ -124,7 +124,7 @@ export class DialogScheduleTaskComponent implements AfterViewInit { this.selectedDate = dateStrToUtcDate(this.data.targetDay); } - this.calendar.activeDate = new Date(this.selectedDate || new Date()); + this.calendar().activeDate = new Date(this.selectedDate || new Date()); this._cd.detectChanges(); setTimeout(() => { @@ -168,7 +168,7 @@ export class DialogScheduleTaskComponent implements AfterViewInit { if ( this.selectedDate && new Date(this.selectedDate).getTime() === - new Date(this.calendar.activeDate).getTime() + new Date(this.calendar().activeDate).getTime() ) { this.submit(); } @@ -211,7 +211,7 @@ export class DialogScheduleTaskComponent implements AfterViewInit { // we do the timeout is there to make sure this happens after our click handler setTimeout(() => { this.selectedDate = new Date(newDate); - this.calendar.activeDate = this.selectedDate; + this.calendar().activeDate = this.selectedDate; }); } diff --git a/src/app/features/planner/planner-task/planner-task.component.ts b/src/app/features/planner/planner-task/planner-task.component.ts index f578125e9c..d7320e09e2 100644 --- a/src/app/features/planner/planner-task/planner-task.component.ts +++ b/src/app/features/planner/planner-task/planner-task.component.ts @@ -7,7 +7,7 @@ import { Input, OnDestroy, OnInit, - ViewChild, + viewChild, } from '@angular/core'; import { TaskCopy } from '../../tasks/task.model'; import { EMPTY, Observable } from 'rxjs'; @@ -41,8 +41,9 @@ export class PlannerTaskComponent extends BaseComponent implements OnInit, OnDes moveToProjectList$!: Observable; - @ViewChild('taskContextMenu', { static: true, read: TaskContextMenuComponent }) - taskContextMenu?: TaskContextMenuComponent; + readonly taskContextMenu = viewChild('taskContextMenu', { + read: TaskContextMenuComponent, + }); @HostBinding('class.isDone') get isDone(): boolean { @@ -114,7 +115,7 @@ export class PlannerTaskComponent extends BaseComponent implements OnInit, OnDes } openContextMenu(event: TouchEvent | MouseEvent): void { - this.taskContextMenu?.open(event); + this.taskContextMenu()?.open(event); } estimateTimeClick(ev: MouseEvent): void { diff --git a/src/app/features/schedule/create-task-placeholder/create-task-placeholder.component.ts b/src/app/features/schedule/create-task-placeholder/create-task-placeholder.component.ts index aae53c52a9..4d530cb001 100644 --- a/src/app/features/schedule/create-task-placeholder/create-task-placeholder.component.ts +++ b/src/app/features/schedule/create-task-placeholder/create-task-placeholder.component.ts @@ -11,7 +11,7 @@ import { output, signal, Signal, - ViewChild, + viewChild, } from '@angular/core'; import { MatIcon } from '@angular/material/icon'; import { TaskService } from '../../tasks/task.service'; @@ -46,8 +46,7 @@ export class CreateTaskPlaceholderComponent implements OnDestroy { // time = computed(() => {}) editEnd = output(); - @ViewChild('textAreaElement', { static: true, read: ElementRef }) - textAreaElement?: ElementRef; + readonly textAreaElement = viewChild('textAreaElement', { read: ElementRef }); private _editEndTimeout: number | undefined; @@ -59,7 +58,7 @@ export class CreateTaskPlaceholderComponent implements OnDestroy { @HostListener('click', ['$event']) onClick(event: MouseEvent): void { event.stopPropagation(); - this.textAreaElement?.nativeElement.focus(); + this.textAreaElement()?.nativeElement.focus(); // cancel blur window.clearTimeout(this._editEndTimeout); } @@ -70,7 +69,7 @@ export class CreateTaskPlaceholderComponent implements OnDestroy { ) { effect(() => { if (this.isEditMode()) { - this.textAreaElement?.nativeElement.focus(); + this.textAreaElement()?.nativeElement.focus(); } }); } @@ -87,15 +86,16 @@ export class CreateTaskPlaceholderComponent implements OnDestroy { } async onKeyDown(event: KeyboardEvent): Promise { + const textAreaElement = this.textAreaElement(); if ( event.key === 'Enter' && typeof this.plannedAt() === 'number' && - this.textAreaElement?.nativeElement.value + textAreaElement?.nativeElement.value ) { this.editEnd.emit(); if (this.isForDayMode()) { const id = this._taskService.add( - this.textAreaElement?.nativeElement.value || '', + textAreaElement?.nativeElement.value || '', false, { timeEstimate: 30 * 60 * 1000, @@ -110,7 +110,7 @@ export class CreateTaskPlaceholderComponent implements OnDestroy { ); } else { this._taskService.addAndSchedule( - this.textAreaElement?.nativeElement.value || '', + textAreaElement?.nativeElement.value || '', { timeEstimate: 30 * 60 * 1000, }, diff --git a/src/app/features/schedule/schedule-event/schedule-event.component.ts b/src/app/features/schedule/schedule-event/schedule-event.component.ts index f2c63f4559..3e89a3fbd0 100644 --- a/src/app/features/schedule/schedule-event/schedule-event.component.ts +++ b/src/app/features/schedule/schedule-event/schedule-event.component.ts @@ -11,7 +11,7 @@ import { Input, LOCALE_ID, OnInit, - ViewChild, + viewChild, } from '@angular/core'; import { ScheduleEvent, ScheduleFromCalendarEvent } from '../schedule.model'; import { MatIcon } from '@angular/material/icon'; @@ -77,8 +77,9 @@ export class ScheduleEventComponent implements OnInit { contextMenuPosition: { x: string; y: string } = { x: '0px', y: '0px' }; - @ViewChild('taskContextMenu', { static: false, read: TaskContextMenuComponent }) - taskContextMenu?: TaskContextMenuComponent; + readonly taskContextMenu = viewChild('taskContextMenu', { + read: TaskContextMenuComponent, + }); protected readonly SVEType = SVEType; destroyRef = inject(DestroyRef); @@ -264,7 +265,7 @@ export class ScheduleEventComponent implements OnInit { } openContextMenu(event: TouchEvent | MouseEvent): void { - this.taskContextMenu?.open(event); + this.taskContextMenu()?.open(event); } deleteTask(): void { diff --git a/src/app/features/schedule/schedule/schedule.component.ts b/src/app/features/schedule/schedule/schedule.component.ts index e8e4d2765c..561e4aab94 100644 --- a/src/app/features/schedule/schedule/schedule.component.ts +++ b/src/app/features/schedule/schedule/schedule.component.ts @@ -8,7 +8,7 @@ import { Inject, LOCALE_ID, OnDestroy, - ViewChild, + viewChild, } from '@angular/core'; import { UiModule } from '../../../ui/ui.module'; import { BehaviorSubject, combineLatest, fromEvent, Observable } from 'rxjs'; @@ -258,7 +258,7 @@ export class ScheduleComponent implements AfterViewInit, OnDestroy { dragCloneEl: HTMLElement | null = null; destroyRef = inject(DestroyRef); - @ViewChild('gridContainer') gridContainer!: ElementRef; + readonly gridContainer = viewChild.required('gridContainer'); private _currentAniTimeout: number | undefined; @@ -332,7 +332,7 @@ export class ScheduleComponent implements AfterViewInit, OnDestroy { // console.log(ev); if (ev.target instanceof HTMLElement && ev.target.classList.contains('col')) { - const gridContainer = this.gridContainer.nativeElement; + const gridContainer = this.gridContainer().nativeElement; const gridStyles = window.getComputedStyle(gridContainer); const rowSizes = gridStyles.gridTemplateRows diff --git a/src/app/features/search-bar/search-bar.component.ts b/src/app/features/search-bar/search-bar.component.ts index a9eb2bbba2..1e5022515b 100644 --- a/src/app/features/search-bar/search-bar.component.ts +++ b/src/app/features/search-bar/search-bar.component.ts @@ -6,7 +6,7 @@ import { EventEmitter, OnDestroy, Output, - ViewChild, + viewChild, } from '@angular/core'; import { T } from '../../t.const'; import { UntypedFormControl } from '@angular/forms'; @@ -46,9 +46,9 @@ const MAX_RESULTS = 100; export class SearchBarComponent implements AfterViewInit, OnDestroy { @Output() blurred: EventEmitter = new EventEmitter(); - @ViewChild('inputEl') inputEl!: ElementRef; - @ViewChild('searchForm') searchForm!: ElementRef; - @ViewChild(MatAutocompleteTrigger) autocomplete!: MatAutocompleteTrigger; + readonly inputEl = viewChild.required('inputEl'); + readonly searchForm = viewChild.required('searchForm'); + readonly autocomplete = viewChild.required(MatAutocompleteTrigger); T: typeof T = T; isLoading$: BehaviorSubject = new BehaviorSubject(true); @@ -143,7 +143,7 @@ export class SearchBarComponent implements AfterViewInit, OnDestroy { ); this._attachKeyDownHandlerTimeout = window.setTimeout(() => { - this.inputEl.nativeElement.addEventListener('keydown', (ev: KeyboardEvent) => { + this.inputEl().nativeElement.addEventListener('keydown', (ev: KeyboardEvent) => { if (ev.key === 'Escape') { this.blurred.emit(); } else if ( @@ -176,15 +176,15 @@ export class SearchBarComponent implements AfterViewInit, OnDestroy { } private _shakeSearchForm(): void { - this.searchForm.nativeElement.classList.toggle('shake-form'); - this.searchForm.nativeElement.onanimationend = () => { - this.searchForm.nativeElement.classList.toggle('shake-form'); + this.searchForm().nativeElement.classList.toggle('shake-form'); + this.searchForm().nativeElement.onanimationend = () => { + this.searchForm().nativeElement.classList.toggle('shake-form'); }; } onAnimationEvent(event: AnimationEvent): void { if (event.fromState) { - this.inputEl.nativeElement.focus(); + this.inputEl().nativeElement.focus(); } } diff --git a/src/app/features/tag/tag-edit/tag-edit.component.ts b/src/app/features/tag/tag-edit/tag-edit.component.ts index 73b1eb7828..5f9b26ddf1 100644 --- a/src/app/features/tag/tag-edit/tag-edit.component.ts +++ b/src/app/features/tag/tag-edit/tag-edit.component.ts @@ -6,7 +6,7 @@ import { inject, input, output, - ViewChild, + viewChild, } from '@angular/core'; import { MatAutocomplete, @@ -70,8 +70,8 @@ export class TagEditComponent { inputCtrl: UntypedFormControl = new UntypedFormControl(); separatorKeysCodes: number[] = DEFAULT_SEPARATOR_KEY_CODES; - @ViewChild('inputElRef', { static: true }) inputEl?: ElementRef; - @ViewChild('autoElRef', { static: true }) matAutocomplete?: MatAutocomplete; + readonly inputEl = viewChild>('inputElRef'); + readonly matAutocomplete = viewChild('autoElRef'); inputVal = toSignal(this.inputCtrl.valueChanges); tagSuggestions = toSignal(this._tagService.tagsNoMyDayAndNoList$, { initialValue: [] }); @@ -99,11 +99,12 @@ export class TagEditComponent { }); add(event: MatChipInputEvent): void { - if (!this.matAutocomplete) { + const matAutocomplete = this.matAutocomplete(); + if (!matAutocomplete) { throw new Error('Auto complete undefined'); } - if (!this.matAutocomplete.isOpen) { + if (!matAutocomplete.isOpen) { const inp = event.input; const value = event.value; @@ -125,8 +126,9 @@ export class TagEditComponent { } focusInput(): void { - if (this.inputEl) { - this.inputEl.nativeElement.focus(); + const inputEl = this.inputEl(); + if (inputEl) { + inputEl.nativeElement.focus(); } } @@ -136,8 +138,9 @@ export class TagEditComponent { selected(event: MatAutocompleteSelectedEvent): void { this._add(event.option.value); - if (this.inputEl) { - this.inputEl.nativeElement.value = ''; + const inputEl = this.inputEl(); + if (inputEl) { + inputEl.nativeElement.value = ''; } this.inputCtrl.setValue(null); } diff --git a/src/app/features/tasks/add-task-bar/add-task-bar.component.ts b/src/app/features/tasks/add-task-bar/add-task-bar.component.ts index 74165c5796..36f64b0790 100644 --- a/src/app/features/tasks/add-task-bar/add-task-bar.component.ts +++ b/src/app/features/tasks/add-task-bar/add-task-bar.component.ts @@ -7,7 +7,7 @@ import { OnDestroy, output, signal, - ViewChild, + viewChild, } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { TaskService } from '../task.service'; @@ -56,7 +56,7 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy { isSearchIssueProviders = signal(false); isSearchIssueProviders$ = toObservable(this.isSearchIssueProviders); - @ViewChild('inputEl', { static: true }) inputEl?: ElementRef; + readonly inputEl = viewChild('inputEl'); T: typeof T = T; @@ -108,7 +108,7 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy { } this._attachKeyDownHandlerTimeout = window.setTimeout(() => { - (this.inputEl as ElementRef).nativeElement.addEventListener( + (this.inputEl() as ElementRef).nativeElement.addEventListener( 'keydown', (ev: KeyboardEvent) => { if (ev.key === 'Escape') { @@ -133,8 +133,8 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy { sessionStorage.setItem(SS.TODO_TMP, ''); this.taskSuggestionsCtrl.setValue(savedTodo); this._saveTmpTodoTimeout = window.setTimeout(() => { - (this.inputEl as ElementRef).nativeElement.value = savedTodo; - (this.inputEl as ElementRef).nativeElement.select(); + (this.inputEl() as ElementRef).nativeElement.value = savedTodo; + (this.inputEl() as ElementRef).nativeElement.select(); }); } } @@ -175,15 +175,13 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy { className.includes('shepherd-enabled'); } + const inputEl = this.inputEl(); if (!relatedTarget || (relatedTarget && !isUIelement)) { - sessionStorage.setItem( - SS.TODO_TMP, - (this.inputEl as ElementRef).nativeElement.value, - ); + sessionStorage.setItem(SS.TODO_TMP, (inputEl as ElementRef).nativeElement.value); } if (relatedTarget && isUIelement) { - (this.inputEl as ElementRef).nativeElement.focus(); + (inputEl as ElementRef).nativeElement.focus(); } else { // we need to wait since otherwise addTask is not working window.clearTimeout(this._delayBlurTimeout); @@ -270,14 +268,14 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy { private _focusInput(): void { if (IS_ANDROID_WEB_VIEW) { document.body.focus(); - (this.inputEl as ElementRef).nativeElement.focus(); + (this.inputEl() as ElementRef).nativeElement.focus(); this._autofocusTimeout = window.setTimeout(() => { document.body.focus(); - (this.inputEl as ElementRef).nativeElement.focus(); + (this.inputEl() as ElementRef).nativeElement.focus(); }, 1000); } else { this._autofocusTimeout = window.setTimeout(() => { - (this.inputEl as ElementRef).nativeElement.focus(); + (this.inputEl() as ElementRef).nativeElement.focus(); }); } } diff --git a/src/app/features/tasks/task-context-menu/task-context-menu-inner/task-context-menu-inner.component.ts b/src/app/features/tasks/task-context-menu/task-context-menu-inner/task-context-menu-inner.component.ts index dc4e41df42..00995652e6 100644 --- a/src/app/features/tasks/task-context-menu/task-context-menu-inner/task-context-menu-inner.component.ts +++ b/src/app/features/tasks/task-context-menu/task-context-menu-inner/task-context-menu-inner.component.ts @@ -6,8 +6,8 @@ import { input, Input, output, - ViewChild, ViewEncapsulation, + viewChild, } from '@angular/core'; import { AsyncPipe, DatePipe } from '@angular/common'; import { IssueModule } from '../../../issue/issue.module'; @@ -95,10 +95,11 @@ export class TaskContextMenuInnerComponent implements AfterViewInit { contextMenuPosition: { x: string; y: string } = { x: '100px', y: '100px' }; - @ViewChild('contextMenuTriggerEl', { static: true, read: MatMenuTrigger }) - contextMenuTrigger?: MatMenuTrigger; + readonly contextMenuTrigger = viewChild('contextMenuTriggerEl', { + read: MatMenuTrigger, + }); - @ViewChild('contextMenu', { static: true, read: MatMenu }) contextMenu?: MatMenu; + readonly contextMenu = viewChild('contextMenu', { read: MatMenu }); task!: TaskWithSubTasks | Task; @@ -186,7 +187,7 @@ export class TaskContextMenuInnerComponent implements AfterViewInit { } this._isOpenedFromKeyboard = isOpenedFromKeyBoard; - this.contextMenuTrigger?.openMenu(); + this.contextMenuTrigger()?.openMenu(); } focusRelatedTaskOrNext(): void { diff --git a/src/app/features/tasks/task-context-menu/task-context-menu.component.ts b/src/app/features/tasks/task-context-menu/task-context-menu.component.ts index 9954a2363d..9c3f52e136 100644 --- a/src/app/features/tasks/task-context-menu/task-context-menu.component.ts +++ b/src/app/features/tasks/task-context-menu/task-context-menu.component.ts @@ -3,7 +3,7 @@ import { ChangeDetectorRef, Component, input, - ViewChild, + viewChild, } from '@angular/core'; import { IssueModule } from '../../issue/issue.module'; import { TranslateModule } from '@ngx-translate/core'; @@ -23,17 +23,15 @@ export class TaskContextMenuComponent { isShowInner: boolean = false; - @ViewChild('taskContextMenuInner', { - static: false, + readonly taskContextMenuInner = viewChild('taskContextMenuInner', { read: TaskContextMenuInnerComponent, - }) - taskContextMenuInner?: TaskContextMenuInnerComponent; + }); constructor(private _cd: ChangeDetectorRef) {} open(ev: MouseEvent | KeyboardEvent | TouchEvent, isOpenedFromKeyBoard = false): void { this.isShowInner = true; this._cd.detectChanges(); - this.taskContextMenuInner?.open(ev, isOpenedFromKeyBoard); + this.taskContextMenuInner()?.open(ev, isOpenedFromKeyBoard); } } diff --git a/src/app/features/tasks/task-detail-panel/task-detail-panel.component.ts b/src/app/features/tasks/task-detail-panel/task-detail-panel.component.ts index e2b61b72d8..b7b30bfab8 100644 --- a/src/app/features/tasks/task-detail-panel/task-detail-panel.component.ts +++ b/src/app/features/tasks/task-detail-panel/task-detail-panel.component.ts @@ -8,9 +8,8 @@ import { Input, LOCALE_ID, OnDestroy, - QueryList, - ViewChild, - ViewChildren, + viewChildren, + viewChild, } from '@angular/core'; import { ShowSubTasksMode, TaskDetailTargetPanel, TaskWithSubTasks } from '../task.model'; import { IssueService } from '../../issue/issue.service'; @@ -94,10 +93,9 @@ export class TaskDetailPanelComponent implements AfterViewInit, OnDestroy { @Input() isOver: boolean = false; @Input() isDialogMode: boolean = false; - @ViewChildren(TaskDetailItemComponent) - itemEls?: QueryList; - @ViewChild('attachmentPanelElRef') - attachmentPanelElRef?: TaskDetailItemComponent; + readonly itemEls = viewChildren(TaskDetailItemComponent); + readonly attachmentPanelElRef = + viewChild('attachmentPanelElRef'); IS_TOUCH_PRIMARY = IS_TOUCH_PRIMARY; _onDestroy$ = new Subject(); @@ -377,11 +375,12 @@ export class TaskDetailPanelComponent implements AfterViewInit, OnDestroy { ) .subscribe(([v]) => { if (v === TaskDetailTargetPanel.Attachments) { - if (!this.attachmentPanelElRef) { + const attachmentPanelElRef = this.attachmentPanelElRef(); + if (!attachmentPanelElRef) { devError('this.attachmentPanelElRef not ready'); this._focusFirst(); } else { - this.focusItem(this.attachmentPanelElRef); + this.focusItem(attachmentPanelElRef); } } else { this._focusFirst(); @@ -458,30 +457,29 @@ export class TaskDetailPanelComponent implements AfterViewInit, OnDestroy { } onItemKeyPress(ev: KeyboardEvent): void { - if (!this.itemEls) { + const itemEls = this.itemEls(); + if (!itemEls) { throw new Error(); } if (ev.key === 'ArrowUp' && this.selectedItemIndex > 0) { this.selectedItemIndex--; - this.itemEls.toArray()[this.selectedItemIndex].focusEl(); - } else if ( - ev.key === 'ArrowDown' && - this.itemEls.toArray().length > this.selectedItemIndex + 1 - ) { + itemEls[this.selectedItemIndex].focusEl(); + } else if (ev.key === 'ArrowDown' && itemEls.length > this.selectedItemIndex + 1) { this.selectedItemIndex++; - this.itemEls.toArray()[this.selectedItemIndex].focusEl(); + itemEls[this.selectedItemIndex].focusEl(); } } focusItem(cmpInstance: TaskDetailItemComponent, timeoutDuration: number = 150): void { window.clearTimeout(this._focusTimeout); this._focusTimeout = window.setTimeout(() => { - if (!this.itemEls) { + const itemEls = this.itemEls(); + if (!itemEls) { throw new Error(); } - const i = this.itemEls.toArray().findIndex((el) => el === cmpInstance); + const i = itemEls.findIndex((el) => el === cmpInstance); if (i === -1) { this.focusItem(cmpInstance); } else { @@ -503,10 +501,11 @@ export class TaskDetailPanelComponent implements AfterViewInit, OnDestroy { private _focusFirst(): void { this._focusTimeout = window.setTimeout(() => { - if (!this.itemEls) { + const itemEls = this.itemEls(); + if (!itemEls) { throw new Error(); } - this.focusItem(this.itemEls.first, 0); + this.focusItem(itemEls.at(0)!, 0); }, 150); } diff --git a/src/app/features/tasks/task-list/task-list.component.ts b/src/app/features/tasks/task-list/task-list.component.ts index 0a17a92990..79ed24fa87 100644 --- a/src/app/features/tasks/task-list/task-list.component.ts +++ b/src/app/features/tasks/task-list/task-list.component.ts @@ -5,7 +5,7 @@ import { computed, input, OnDestroy, - ViewChild, + viewChild, } from '@angular/core'; import { DropListModelSource, Task, TaskCopy, TaskWithSubTasks } from '../task.model'; import { TaskService } from '../task.service'; @@ -86,7 +86,7 @@ export class TaskListComponent implements OnDestroy, AfterViewInit { }); allTasksLength = computed(() => this.tasks()?.length ?? 0); - @ViewChild(CdkDropList) dropList?: CdkDropList; + readonly dropList = viewChild(CdkDropList); T: typeof T = T; @@ -99,11 +99,11 @@ export class TaskListComponent implements OnDestroy, AfterViewInit { ) {} ngAfterViewInit(): void { - this.dropListService.registerDropList(this.dropList!, this.listId() === 'SUB'); + this.dropListService.registerDropList(this.dropList()!, this.listId() === 'SUB'); } ngOnDestroy(): void { - this.dropListService.unregisterDropList(this.dropList!); + this.dropListService.unregisterDropList(this.dropList()!); } trackByFn(i: number, task: Task): string { diff --git a/src/app/features/tasks/task/task.component.ts b/src/app/features/tasks/task/task.component.ts index f32acdedd8..8e07578400 100644 --- a/src/app/features/tasks/task/task.component.ts +++ b/src/app/features/tasks/task/task.component.ts @@ -8,7 +8,7 @@ import { input, OnDestroy, Renderer2, - ViewChild, + viewChild, } from '@angular/core'; import { TaskService } from '../task.service'; import { EMPTY, forkJoin, of } from 'rxjs'; @@ -112,14 +112,16 @@ export class TaskComponent implements OnDestroy, AfterViewInit { ShowSubTasksMode: typeof ShowSubTasksMode = ShowSubTasksMode; isFirstLineHover: boolean = false; - @ViewChild('taskTitleEditEl', { static: true }) taskTitleEditEl?: ElementRef; - @ViewChild('blockLeftEl') blockLeftElRef?: ElementRef; - @ViewChild('blockRightEl') blockRightElRef?: ElementRef; - @ViewChild('innerWrapperEl', { static: true }) innerWrapperElRef?: ElementRef; - @ViewChild('projectMenuTriggerEl', { static: false, read: MatMenuTrigger }) - projectMenuTrigger?: MatMenuTrigger; - @ViewChild('taskContextMenu', { static: true, read: TaskContextMenuComponent }) - taskContextMenu?: TaskContextMenuComponent; + readonly taskTitleEditEl = viewChild('taskTitleEditEl'); + readonly blockLeftElRef = viewChild('blockLeftEl'); + readonly blockRightElRef = viewChild('blockRightEl'); + readonly innerWrapperElRef = viewChild('innerWrapperEl'); + readonly projectMenuTrigger = viewChild('projectMenuTriggerEl', { + read: MatMenuTrigger, + }); + readonly taskContextMenu = viewChild('taskContextMenu', { + read: TaskContextMenuComponent, + }); private _task$ = toObservable(this.task); @@ -468,17 +470,18 @@ export class TaskComponent implements OnDestroy, AfterViewInit { } focusTitleForEdit(): void { - if (!this.taskTitleEditEl || !(this.taskTitleEditEl as any).textarea.nativeElement) { - console.log(this.taskTitleEditEl); + const taskTitleEditEl = this.taskTitleEditEl(); + if (!taskTitleEditEl || !(taskTitleEditEl as any).textarea.nativeElement) { + console.log(taskTitleEditEl); throw new Error('No el'); } - (this.taskTitleEditEl as any).textarea.nativeElement.focus(); + (taskTitleEditEl as any).textarea.nativeElement.focus(); // (this.taskTitleEditEl as any).textarea.nativeElement.focus(); } openContextMenu(event: TouchEvent | MouseEvent): void { - (this.taskTitleEditEl as any).textarea.nativeElement?.blur(); - this.taskContextMenu?.open(event); + (this.taskTitleEditEl() as any).textarea.nativeElement?.blur(); + this.taskContextMenu()?.open(event); } onTagsUpdated(tagIds: string[]): void { @@ -489,7 +492,8 @@ export class TaskComponent implements OnDestroy, AfterViewInit { if (!IS_TOUCH_PRIMARY) { return; } - if (!this.taskTitleEditEl) { + const taskTitleEditEl = this.taskTitleEditEl(); + if (!taskTitleEditEl) { throw new Error('No el'); } @@ -498,7 +502,7 @@ export class TaskComponent implements OnDestroy, AfterViewInit { if ( (targetEl.className.indexOf && targetEl.className.indexOf('drag-handle') > -1) || Math.abs(ev.deltaY) > Math.abs(ev.deltaX) || - document.activeElement === (this.taskTitleEditEl as any).textarea.nativeElement || + document.activeElement === (taskTitleEditEl as any).textarea.nativeElement || ev.isFinal ) { return; @@ -514,13 +518,15 @@ export class TaskComponent implements OnDestroy, AfterViewInit { if (!IS_TOUCH_PRIMARY || (!this.isLockPanLeft && !this.isLockPanRight)) { return; } - if (!this.blockLeftElRef || !this.blockRightElRef) { + const blockLeftElRef = this.blockLeftElRef(); + const blockRightElRef = this.blockRightElRef(); + if (!blockLeftElRef || !blockRightElRef) { throw new Error('No el'); } this.isPreventPointerEventsWhilePanning = false; - this._renderer.removeStyle(this.blockLeftElRef.nativeElement, 'transition'); - this._renderer.removeStyle(this.blockRightElRef.nativeElement, 'transition'); + this._renderer.removeStyle(blockLeftElRef.nativeElement, 'transition'); + this._renderer.removeStyle(blockRightElRef.nativeElement, 'transition'); if (this._currentPanTimeout) { window.clearTimeout(this._currentPanTimeout); @@ -528,11 +534,7 @@ export class TaskComponent implements OnDestroy, AfterViewInit { if (this.isActionTriggered) { if (this.isLockPanLeft) { - this._renderer.setStyle( - this.blockRightElRef.nativeElement, - 'transform', - `scaleX(1)`, - ); + this._renderer.setStyle(blockRightElRef.nativeElement, 'transform', `scaleX(1)`); this._currentPanTimeout = window.setTimeout(() => { if (this.workContextService.isToday) { if (this.task().repeatCfgId) { @@ -554,11 +556,7 @@ export class TaskComponent implements OnDestroy, AfterViewInit { this._resetAfterPan(); }, 100); } else if (this.isLockPanRight) { - this._renderer.setStyle( - this.blockLeftElRef.nativeElement, - 'transform', - `scaleX(1)`, - ); + this._renderer.setStyle(blockLeftElRef.nativeElement, 'transform', `scaleX(1)`); this._currentPanTimeout = window.setTimeout(() => { this.toggleTaskDone(); this._resetAfterPan(); @@ -696,11 +694,14 @@ export class TaskComponent implements OnDestroy, AfterViewInit { ) { return; } - if (!this.innerWrapperElRef) { + const innerWrapperElRef = this.innerWrapperElRef(); + if (!innerWrapperElRef) { throw new Error('No el'); } - const targetRef = this.isLockPanRight ? this.blockLeftElRef : this.blockRightElRef; + const targetRef = this.isLockPanRight + ? this.blockLeftElRef() + : this.blockRightElRef(); const MAGIC_FACTOR = 2; this.isPreventPointerEventsWhilePanning = true; @@ -720,7 +721,7 @@ export class TaskComponent implements OnDestroy, AfterViewInit { this._renderer.setStyle(targetRef.nativeElement, 'width', `${moveBy}px`); this._renderer.setStyle(targetRef.nativeElement, 'transition', `none`); this._renderer.setStyle( - this.innerWrapperElRef.nativeElement, + innerWrapperElRef.nativeElement, 'transform', `translateX(${ev.deltaX}px`, ); @@ -728,11 +729,14 @@ export class TaskComponent implements OnDestroy, AfterViewInit { } private _resetAfterPan(): void { + const blockLeftElRef = this.blockLeftElRef(); + const blockRightElRef = this.blockRightElRef(); + const innerWrapperElRef = this.innerWrapperElRef(); if ( - !this.taskTitleEditEl || - !this.blockLeftElRef || - !this.blockRightElRef || - !this.innerWrapperElRef + !this.taskTitleEditEl() || + !blockLeftElRef || + !blockRightElRef || + !innerWrapperElRef ) { throw new Error('No el'); } @@ -744,9 +748,9 @@ export class TaskComponent implements OnDestroy, AfterViewInit { // const scale = 0; // this._renderer.setStyle(this.blockLeftEl.nativeElement, 'transform', `scaleX(${scale})`); // this._renderer.setStyle(this.blockRightEl.nativeElement, 'transform', `scaleX(${scale})`); - this._renderer.removeClass(this.blockLeftElRef.nativeElement, 'isActive'); - this._renderer.removeClass(this.blockRightElRef.nativeElement, 'isActive'); - this._renderer.setStyle(this.innerWrapperElRef.nativeElement, 'transform', ``); + this._renderer.removeClass(blockLeftElRef.nativeElement, 'isActive'); + this._renderer.removeClass(blockRightElRef.nativeElement, 'isActive'); + this._renderer.setStyle(innerWrapperElRef.nativeElement, 'transform', ``); } get kb(): KeyboardConfig { @@ -793,16 +797,18 @@ export class TaskComponent implements OnDestroy, AfterViewInit { this.addAttachment(); } if (!t.parentId && checkKeyCombo(ev, keys.taskMoveToProject)) { - if (!this.projectMenuTrigger) { + const projectMenuTrigger = this.projectMenuTrigger(); + if (!projectMenuTrigger) { throw new Error('No el'); } - this.projectMenuTrigger.openMenu(); + projectMenuTrigger.openMenu(); } if (checkKeyCombo(ev, keys.taskOpenContextMenu)) { - if (!this.taskContextMenu) { + const taskContextMenu = this.taskContextMenu(); + if (!taskContextMenu) { throw new Error('No el'); } - this.taskContextMenu.open(ev, true); + taskContextMenu.open(ev, true); } if (checkKeyCombo(ev, keys.togglePlay)) { diff --git a/src/app/features/work-view/split/split.component.ts b/src/app/features/work-view/split/split.component.ts index e70e80ca33..b116de29d1 100644 --- a/src/app/features/work-view/split/split.component.ts +++ b/src/app/features/work-view/split/split.component.ts @@ -7,7 +7,7 @@ import { Input, Output, Renderer2, - ViewChild, + viewChild, } from '@angular/core'; import { fromEvent, Subscription } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -32,7 +32,7 @@ export class SplitComponent implements AfterViewInit { pos: number = 100; eventSubs?: Subscription; - @ViewChild('buttonEl', { static: true }) buttonEl?: ElementRef; + readonly buttonEl = viewChild('buttonEl'); private _isDrag: boolean = false; private _isViewInitialized: boolean = false; diff --git a/src/app/features/work-view/work-view.component.ts b/src/app/features/work-view/work-view.component.ts index ef43766b7a..a3a5c2d2eb 100644 --- a/src/app/features/work-view/work-view.component.ts +++ b/src/app/features/work-view/work-view.component.ts @@ -105,6 +105,8 @@ export class WorkViewComponent implements OnInit, OnDestroy, AfterContentInit { private _addTasksForTomorrowService: AddTasksForTomorrowService, ) {} + // TODO: Skipped for migration because: + // Accessor queries cannot be migrated as they are too complex. @ViewChild('splitTopEl', { read: ElementRef }) set splitTopElRef(ref: ElementRef) { if (ref) { this.splitTopEl$.next(ref.nativeElement); diff --git a/src/app/imex/file-imex/file-imex.component.ts b/src/app/imex/file-imex/file-imex.component.ts index a35f200a3b..83df8166b5 100644 --- a/src/app/imex/file-imex/file-imex.component.ts +++ b/src/app/imex/file-imex/file-imex.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, ElementRef, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, viewChild } from '@angular/core'; import { DataImportService } from '../sync/data-import.service'; import { SnackService } from '../../core/snack/snack.service'; import { AppDataComplete } from '../sync/sync.model'; @@ -16,7 +16,7 @@ import { privacyExport } from './privacy-export'; standalone: false, }) export class FileImexComponent { - @ViewChild('fileInput', { static: true }) fileInputRef?: ElementRef; + readonly fileInputRef = viewChild('fileInput'); T: typeof T = T; constructor( @@ -50,14 +50,15 @@ export class FileImexComponent { await this._dataImportService.importCompleteSyncData(data as AppDataComplete); } - if (!this.fileInputRef) { + const fileInputRef = this.fileInputRef(); + if (!fileInputRef) { throw new Error('No file input Ref element'); } // clear input - this.fileInputRef.nativeElement.value = ''; - this.fileInputRef.nativeElement.type = 'text'; - this.fileInputRef.nativeElement.type = 'file'; + fileInputRef.nativeElement.value = ''; + fileInputRef.nativeElement.type = 'text'; + fileInputRef.nativeElement.type = 'file'; }; reader.readAsText(file); } diff --git a/src/app/ui/better-drawer/better-drawer-container/better-drawer-container.component.ts b/src/app/ui/better-drawer/better-drawer-container/better-drawer-container.component.ts index 40a1cfcbc6..1258796dcb 100644 --- a/src/app/ui/better-drawer/better-drawer-container/better-drawer-container.component.ts +++ b/src/app/ui/better-drawer/better-drawer-container/better-drawer-container.component.ts @@ -71,6 +71,8 @@ export class BetterDrawerContainerComponent return this._isOver; } + // TODO: Skipped for migration because: + // Accessor queries cannot be migrated as they are too complex. @ViewChild('contentElRef', { read: ElementRef }) set setContentElRef(ref: ElementRef) { this.contentEl$.next(ref.nativeElement); } diff --git a/src/app/ui/better-simple-drawer/better-simple-drawer.component.ts b/src/app/ui/better-simple-drawer/better-simple-drawer.component.ts index 4623b6ce6b..ae6814110f 100644 --- a/src/app/ui/better-simple-drawer/better-simple-drawer.component.ts +++ b/src/app/ui/better-simple-drawer/better-simple-drawer.component.ts @@ -35,6 +35,8 @@ export class BetterSimpleDrawerComponent implements OnInit, OnDestroy { return this._isOpen; } + // TODO: Skipped for migration because: + // Accessor queries cannot be migrated as they are too complex. @ViewChild('contentElRef', { read: ElementRef }) set setContentElRef(ref: ElementRef) { this.contentEl$.next(ref.nativeElement); } diff --git a/src/app/ui/chip-list-input/chip-list-input.component.ts b/src/app/ui/chip-list-input/chip-list-input.component.ts index 9897242898..10bf81216e 100644 --- a/src/app/ui/chip-list-input/chip-list-input.component.ts +++ b/src/app/ui/chip-list-input/chip-list-input.component.ts @@ -7,7 +7,7 @@ import { Input, OnDestroy, Output, - ViewChild, + viewChild, } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { Observable } from 'rxjs'; @@ -56,8 +56,8 @@ export class ChipListInputComponent implements OnDestroy { inputCtrl: UntypedFormControl = new UntypedFormControl(); separatorKeysCodes: number[] = DEFAULT_SEPARATOR_KEY_CODES; isAutoFocus = false; - @ViewChild('inputElRef', { static: true }) inputEl?: ElementRef; - @ViewChild('autoElRef', { static: true }) matAutocomplete?: MatAutocomplete; + readonly inputEl = viewChild>('inputElRef'); + readonly matAutocomplete = viewChild('autoElRef'); private _modelIds: string[] = []; filteredSuggestions: Observable = this.inputCtrl.valueChanges.pipe( @@ -77,7 +77,7 @@ export class ChipListInputComponent implements OnDestroy { if (typeof autoFocus === 'string') { this.isAutoFocus = true; this._autoFocusTimeout = window.setTimeout(() => { - this.inputEl?.nativeElement.focus(); + this.inputEl()?.nativeElement.focus(); // NOTE: we need to wait a little for the tag dialog to be there }, 300); } @@ -100,11 +100,12 @@ export class ChipListInputComponent implements OnDestroy { } add(event: MatChipInputEvent): void { - if (!this.matAutocomplete) { + const matAutocomplete = this.matAutocomplete(); + if (!matAutocomplete) { throw new Error('Auto complete undefined'); } - if (!this.matAutocomplete.isOpen) { + if (!matAutocomplete.isOpen) { const input = event.input; const value = event.value; @@ -125,8 +126,9 @@ export class ChipListInputComponent implements OnDestroy { selected(event: MatAutocompleteSelectedEvent): void { this._add(event.option.value); - if (this.inputEl) { - this.inputEl.nativeElement.value = ''; + const inputEl = this.inputEl(); + if (inputEl) { + inputEl.nativeElement.value = ''; } this.inputCtrl.setValue(null); } diff --git a/src/app/ui/context-menu/context-menu.component.ts b/src/app/ui/context-menu/context-menu.component.ts index 8e35c8547d..f099eb383f 100644 --- a/src/app/ui/context-menu/context-menu.component.ts +++ b/src/app/ui/context-menu/context-menu.component.ts @@ -4,7 +4,7 @@ import { input, OnInit, TemplateRef, - ViewChild, + viewChild, } from '@angular/core'; import { MatMenu, @@ -27,8 +27,9 @@ export class ContextMenuComponent implements OnInit { rightClickTriggerEl = input.required(); contextMenu = input.required>(); - @ViewChild('contextMenuTriggerEl', { static: true, read: MatMenuTrigger }) - contextMenuTriggerEl!: MatMenuTrigger; + readonly contextMenuTriggerEl = viewChild.required('contextMenuTriggerEl', { + read: MatMenuTrigger, + }); contextMenuPosition: { x: string; y: string } = { x: '0px', y: '0px' }; ngOnInit(): void { @@ -61,10 +62,11 @@ export class ContextMenuComponent implements OnInit { ('touches' in event ? event.touches[0].clientX : event.clientX) + 'px'; this.contextMenuPosition.y = ('touches' in event ? event.touches[0].clientY : event.clientY) + 'px'; - this.contextMenuTriggerEl.menuData = { + const contextMenuTriggerEl = this.contextMenuTriggerEl(); + contextMenuTriggerEl.menuData = { x: this.contextMenuPosition.x, y: this.contextMenuPosition.y, }; - this.contextMenuTriggerEl.openMenu(); + contextMenuTriggerEl.openMenu(); } } diff --git a/src/app/ui/duration/input-duration-formly/input-duration-formly.component.ts b/src/app/ui/duration/input-duration-formly/input-duration-formly.component.ts index 8a747890f8..2d3ac4e986 100644 --- a/src/app/ui/duration/input-duration-formly/input-duration-formly.component.ts +++ b/src/app/ui/duration/input-duration-formly/input-duration-formly.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, ElementRef, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, viewChild } from '@angular/core'; import { FieldType } from '@ngx-formly/material'; import { FormlyFieldConfig } from '@ngx-formly/core'; import { stringToMs } from '../string-to-ms.pipe'; @@ -11,7 +11,7 @@ import { stringToMs } from '../string-to-ms.pipe'; standalone: false, }) export class InputDurationFormlyComponent extends FieldType { - @ViewChild('inputEl', { static: true, read: ElementRef }) input!: ElementRef; + readonly input = viewChild.required('inputEl', { read: ElementRef }); // @ViewChild(MatInput, {static: true}) formFieldControl?: MatInput; onInputValueChange(ev: Event): void { @@ -30,10 +30,11 @@ export class InputDurationFormlyComponent extends FieldType { private _updateValue(val: string): void { this.formControl.setValue(val ? stringToMs(val) : null); - this.input.nativeElement.value = val; + this.input().nativeElement.value = val; setTimeout(() => { - if (this.input.nativeElement.value !== val) { - this.input.nativeElement.value = val; + const input = this.input(); + if (input.nativeElement.value !== val) { + input.nativeElement.value = val; } }); } diff --git a/src/app/ui/duration/input-duration-slider/input-duration-slider.component.ts b/src/app/ui/duration/input-duration-slider/input-duration-slider.component.ts index ebbc13d1ef..c70f9e35f4 100644 --- a/src/app/ui/duration/input-duration-slider/input-duration-slider.component.ts +++ b/src/app/ui/duration/input-duration-slider/input-duration-slider.component.ts @@ -8,7 +8,7 @@ import { OnDestroy, OnInit, Output, - ViewChild, + viewChild, } from '@angular/core'; import { nanoid } from 'nanoid'; import moment from 'moment'; @@ -34,7 +34,7 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy { endHandler?: () => void; moveHandler?: (ev: any) => void; - @ViewChild('circleEl', { static: true }) circleEl?: ElementRef; + readonly circleEl = viewChild('circleEl'); @Input() label: string = ''; @Output() modelChange: EventEmitter = new EventEmitter(); @@ -57,7 +57,7 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy { ngOnInit(): void { this.startHandler = (ev) => { - if (!this.endHandler || !this.moveHandler || !this.circleEl) { + if (!this.endHandler || !this.moveHandler || !this.circleEl()) { throw new Error(); } @@ -83,7 +83,8 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy { ) { return; } - if (!this.endHandler || !this.moveHandler || !this.circleEl) { + const circleEl = this.circleEl(); + if (!this.endHandler || !this.moveHandler || !circleEl) { throw new Error(); } @@ -92,8 +93,8 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy { const convertThetaToCssDegrees = (thetaIN: number): number => 90 - thetaIN; - const centerX = this.circleEl.nativeElement.offsetWidth / 2; - const centerY = this.circleEl.nativeElement.offsetHeight / 2; + const centerX = circleEl.nativeElement.offsetWidth / 2; + const centerY = circleEl.nativeElement.offsetHeight / 2; let offsetX; @@ -117,7 +118,7 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy { }; this.endHandler = () => { - if (!this.endHandler || !this.moveHandler || !this.circleEl) { + if (!this.endHandler || !this.moveHandler || !this.circleEl()) { throw new Error(); } @@ -138,7 +139,7 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy { } ngOnDestroy(): void { - if (!this.endHandler || !this.moveHandler || !this.startHandler || !this.circleEl) { + if (!this.endHandler || !this.moveHandler || !this.startHandler || !this.circleEl()) { throw new Error(); } @@ -154,10 +155,11 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy { } setCircleRotation(cssDegrees: number): void { - if (!this.circleEl) { + const circleEl = this.circleEl(); + if (!circleEl) { throw new Error(); } - this.circleEl.nativeElement.style.transform = 'rotate(' + cssDegrees + 'deg)'; + circleEl.nativeElement.style.transform = 'rotate(' + cssDegrees + 'deg)'; } setDots(hours: number = 0): void { diff --git a/src/app/ui/formly-translated-template/formly-translated-template.component.ts b/src/app/ui/formly-translated-template/formly-translated-template.component.ts index 8bfd44a057..cbe3e5fd49 100644 --- a/src/app/ui/formly-translated-template/formly-translated-template.component.ts +++ b/src/app/ui/formly-translated-template/formly-translated-template.component.ts @@ -4,7 +4,7 @@ import { ElementRef, OnDestroy, OnInit, - ViewChild, + viewChild, } from '@angular/core'; import { FieldType } from '@ngx-formly/core'; import { Subscription } from 'rxjs'; @@ -21,7 +21,7 @@ export class FormlyTranslatedTemplateComponent extends FieldType implements OnInit, OnDestroy { - @ViewChild('tplWrapper', { static: true }) tplWrapper?: ElementRef; + readonly tplWrapper = viewChild('tplWrapper'); private _el?: HTMLElement; private _subs: Subscription = new Subscription(); @@ -54,11 +54,12 @@ export class FormlyTranslatedTemplateComponent } private _createTag(): void { - if (!this.field.templateOptions || !this.tplWrapper) { + const tplWrapper = this.tplWrapper(); + if (!this.field.templateOptions || !tplWrapper) { throw new Error(); } const tag = this.field.templateOptions.tag || 'div'; - const tplWrapperEl = this.tplWrapper.nativeElement; + const tplWrapperEl = tplWrapper.nativeElement; if (tplWrapperEl) { this._el = document.createElement(tag); @@ -67,7 +68,7 @@ export class FormlyTranslatedTemplateComponent (this._el as HTMLElement).classList.add(this.field.templateOptions.class); } - this.tplWrapper.nativeElement.append(this._el); + tplWrapper.nativeElement.append(this._el); } } } diff --git a/src/app/ui/inline-input/inline-input.component.ts b/src/app/ui/inline-input/inline-input.component.ts index 21b5f28bbb..c9d33a2ede 100644 --- a/src/app/ui/inline-input/inline-input.component.ts +++ b/src/app/ui/inline-input/inline-input.component.ts @@ -7,7 +7,7 @@ import { HostBinding, Input, Output, - ViewChild, + viewChild, } from '@angular/core'; @Component({ @@ -24,8 +24,8 @@ export class InlineInputComponent implements AfterViewInit { @Input() newValue?: string | number; @Output() changed: EventEmitter = new EventEmitter(); - @ViewChild('inputEl') inputEl?: ElementRef; - @ViewChild('inputElDuration') inputElDuration?: ElementRef; + readonly inputEl = viewChild('inputEl'); + readonly inputElDuration = viewChild('inputElDuration'); @HostBinding('class.isFocused') isFocused: boolean = false; @@ -36,15 +36,15 @@ export class InlineInputComponent implements AfterViewInit { ngAfterViewInit(): void { this.activeInputEl = this.type === 'duration' - ? (this.inputElDuration as ElementRef).nativeElement - : (this.inputEl as ElementRef).nativeElement; + ? (this.inputElDuration() as ElementRef).nativeElement + : (this.inputEl() as ElementRef).nativeElement; } focusInput(): void { this.activeInputEl = this.type === 'duration' - ? (this.inputElDuration as ElementRef).nativeElement - : (this.inputEl as ElementRef).nativeElement; + ? (this.inputElDuration() as ElementRef).nativeElement + : (this.inputEl() as ElementRef).nativeElement; this.isFocused = true; (this.activeInputEl as HTMLElement).focus(); diff --git a/src/app/ui/inline-markdown/inline-markdown.component.ts b/src/app/ui/inline-markdown/inline-markdown.component.ts index f363f43452..ef51fc6802 100644 --- a/src/app/ui/inline-markdown/inline-markdown.component.ts +++ b/src/app/ui/inline-markdown/inline-markdown.component.ts @@ -9,7 +9,7 @@ import { OnDestroy, OnInit, Output, - ViewChild, + viewChild, } from '@angular/core'; import { fadeAnimation } from '../animations/fade.ani'; import { MarkdownComponent } from 'ngx-markdown'; @@ -40,9 +40,9 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy { @Output() focused: EventEmitter = new EventEmitter(); @Output() blurred: EventEmitter = new EventEmitter(); @Output() keyboardUnToggle: EventEmitter = new EventEmitter(); - @ViewChild('wrapperEl', { static: true }) wrapperEl: ElementRef | undefined; - @ViewChild('textareaEl') textareaEl: ElementRef | undefined; - @ViewChild('previewEl') previewEl: MarkdownComponent | undefined; + readonly wrapperEl = viewChild('wrapperEl'); + readonly textareaEl = viewChild('textareaEl'); + readonly previewEl = viewChild('previewEl'); isHideOverflow: boolean = false; isChecklistMode: boolean = false; @@ -144,10 +144,11 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy { this.resizeParsedToFit(); this.isShowEdit = false; } - if (!this.textareaEl) { + const textareaEl = this.textareaEl(); + if (!textareaEl) { throw new Error('Textarea not visible'); } - this.modelCopy = this.textareaEl.nativeElement.value; + this.modelCopy = textareaEl.nativeElement.value; if (this.modelCopy !== this.model) { this.model = this.modelCopy; @@ -157,17 +158,17 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy { resizeTextareaToFit(): void { this._hideOverflow(); - if (!this.textareaEl) { + const textareaEl = this.textareaEl(); + if (!textareaEl) { throw new Error('Textarea not visible'); } - if (!this.wrapperEl) { + const wrapperEl = this.wrapperEl(); + if (!wrapperEl) { throw new Error('Wrapper el not visible'); } - this.textareaEl.nativeElement.style.height = 'auto'; - this.textareaEl.nativeElement.style.height = - this.textareaEl.nativeElement.scrollHeight + 'px'; - this.wrapperEl.nativeElement.style.height = - this.textareaEl.nativeElement.offsetHeight + 'px'; + textareaEl.nativeElement.style.height = 'auto'; + textareaEl.nativeElement.style.height = textareaEl.nativeElement.scrollHeight + 'px'; + wrapperEl.nativeElement.style.height = textareaEl.nativeElement.offsetHeight + 'px'; } openFullScreen(): void { @@ -193,20 +194,22 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy { this._hideOverflow(); setTimeout(() => { - if (!this.previewEl) { - if (this.textareaEl) { + const previewEl = this.previewEl(); + if (!previewEl) { + if (this.textareaEl()) { this.resizeTextareaToFit(); } return; } - if (!this.wrapperEl) { + const wrapperEl = this.wrapperEl(); + if (!wrapperEl) { throw new Error('Wrapper el not visible'); } - this.previewEl.element.nativeElement.style.height = 'auto'; + previewEl.element.nativeElement.style.height = 'auto'; // NOTE: somehow this pixel seem to help - this.wrapperEl.nativeElement.style.height = - this.previewEl.element.nativeElement.offsetHeight + 'px'; - this.previewEl.element.nativeElement.style.height = ''; + wrapperEl.nativeElement.style.height = + previewEl.element.nativeElement.offsetHeight + 'px'; + previewEl.element.nativeElement.style.height = ''; }); } @@ -234,11 +237,12 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy { this.isShowEdit = true; this.modelCopy = this.model || ''; setTimeout(() => { - if (!this.textareaEl) { + const textareaEl = this.textareaEl(); + if (!textareaEl) { throw new Error('Textarea not visible'); } - this.textareaEl.nativeElement.value = this.modelCopy; - this.textareaEl.nativeElement.focus(); + textareaEl.nativeElement.value = this.modelCopy; + textareaEl.nativeElement.focus(); this.resizeTextareaToFit(); }); } @@ -256,10 +260,11 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy { } private _makeLinksWorkForElectron(): void { - if (!this.wrapperEl) { + const wrapperEl = this.wrapperEl(); + if (!wrapperEl) { throw new Error('Wrapper el not visible'); } - this.wrapperEl.nativeElement.addEventListener('click', (ev: MouseEvent) => { + wrapperEl.nativeElement.addEventListener('click', (ev: MouseEvent) => { const target = ev.target as HTMLElement; if (target.tagName && target.tagName.toLowerCase() === 'a') { const href = target.getAttribute('href'); @@ -273,7 +278,7 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy { private _handleCheckboxClick(targetEl: HTMLElement): void { const allCheckboxes = - this.previewEl?.element.nativeElement.querySelectorAll('.checkbox-wrapper'); + this.previewEl()?.element.nativeElement.querySelectorAll('.checkbox-wrapper'); const checkIndex = Array.from(allCheckboxes || []).findIndex((el) => el === targetEl); if (checkIndex !== -1 && this._model) { diff --git a/src/app/ui/inline-multiline-input/inline-multiline-input.component.ts b/src/app/ui/inline-multiline-input/inline-multiline-input.component.ts index 53b18ecaf6..0e5e9c6727 100644 --- a/src/app/ui/inline-multiline-input/inline-multiline-input.component.ts +++ b/src/app/ui/inline-multiline-input/inline-multiline-input.component.ts @@ -6,7 +6,7 @@ import { HostBinding, Input, Output, - ViewChild, + viewChild, } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { T } from 'src/app/t.const'; @@ -47,7 +47,8 @@ export class InlineMultilineInputComponent { lastExternalValue?: string; tmpValue?: string; - @ViewChild('textAreaElement') textarea!: ElementRef; + readonly textarea = + viewChild.required>('textAreaElement'); @Output() valueEdited = new EventEmitter<{ newVal: string; @@ -78,7 +79,7 @@ export class InlineMultilineInputComponent { this._setTxtHeight(); try { window.setTimeout(() => { - const el = this.textarea.nativeElement; + const el = this.textarea().nativeElement; el.setSelectionRange(el.value.length, el.value.length); el.selectionStart = el.selectionEnd = el.value.length; }); @@ -124,7 +125,7 @@ export class InlineMultilineInputComponent { } private _forceBlur(): void { - this.textarea.nativeElement.blur(); + this.textarea().nativeElement.blur(); } private _submit(): void { @@ -153,15 +154,15 @@ export class InlineMultilineInputComponent { private _setTxtHeight(): void { try { // reset height - this.textarea.nativeElement.style.height = 'auto'; - this.textarea.nativeElement.style.height = - this.textarea.nativeElement.scrollHeight + 'px'; + const textarea = this.textarea(); + textarea.nativeElement.style.height = 'auto'; + textarea.nativeElement.style.height = textarea.nativeElement.scrollHeight + 'px'; } catch (e) { setTimeout(() => { // reset height - this.textarea.nativeElement.style.height = 'auto'; - this.textarea.nativeElement.style.height = - this.textarea.nativeElement.scrollHeight + 'px'; + const textarea = this.textarea(); + textarea.nativeElement.style.height = 'auto'; + textarea.nativeElement.style.height = textarea.nativeElement.scrollHeight + 'px'; }); } } diff --git a/src/app/ui/progress-circle/progress-circle.component.ts b/src/app/ui/progress-circle/progress-circle.component.ts index 143838a390..27903ec728 100644 --- a/src/app/ui/progress-circle/progress-circle.component.ts +++ b/src/app/ui/progress-circle/progress-circle.component.ts @@ -4,7 +4,7 @@ import { ElementRef, Input, Renderer2, - ViewChild, + viewChild, } from '@angular/core'; @Component({ @@ -16,21 +16,22 @@ import { }) export class ProgressCircleComponent { @Input() set progress(progressIN: number) { - if (this.progressCircle) { + const progressCircle = this.progressCircle(); + if (progressCircle) { let progress = progressIN || 0; if (progress > 100) { progress = 100; } this._renderer.setStyle( - this.progressCircle.nativeElement, + progressCircle.nativeElement, 'stroke-dasharray', `${progress} ,100`, ); } } - @ViewChild('progressCircle', { static: true }) progressCircle?: ElementRef; + readonly progressCircle = viewChild('progressCircle'); constructor(private readonly _renderer: Renderer2) {} }