Chorehancement: update to Angular v22, 'zoneless' / 'reactive' (#13114)

This commit is contained in:
shamoon
2026-07-10 00:42:16 -07:00
committed by GitHub
parent f244442c65
commit 106b41a15c
213 changed files with 5363 additions and 5842 deletions
@@ -11,30 +11,30 @@
type="number"
min="100"
step="100"
[(ngModel)]="limit"
(ngModelChange)="onLimitChange($event)"
[ngModel]="limit()"
(ngModelChange)="limit.set($event); onLimitChange($event)"
style="width: 100px;">
<span class="input-group-text text-muted" i18n>lines</span>
</div>
<div class="form-check form-switch mt-1">
<input class="form-check-input" type="checkbox" role="switch" [(ngModel)]="autoRefreshEnabled">
<input class="form-check-input" type="checkbox" role="switch" [ngModel]="autoRefreshEnabled()" (ngModelChange)="autoRefreshEnabled.set($event)">
<label class="form-check-label" for="autoRefreshSwitch" i18n>Auto refresh</label>
</div>
</div>
</pngx-page-header>
<ul ngbNav #nav="ngbNav" [(activeId)]="activeLog" (activeIdChange)="reloadLogs()" class="nav-tabs">
@for (logFile of logFiles; track logFile) {
<ul ngbNav #nav="ngbNav" [activeId]="activeLog()" (activeIdChange)="activeLog.set($event); reloadLogs()" class="nav-tabs">
@for (logFile of logFiles(); track logFile) {
<li [ngbNavItem]="logFile">
<a ngbNavLink>
{{logFile}}.log
</a>
</li>
}
@if (loading || !logFiles.length) {
@if (loading() || !logFiles().length) {
<div class="ps-2 d-flex align-items-center">
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
@if (!logFiles.length) {
@if (!logFiles().length) {
<ng-container i18n>Loading...</ng-container>
}
</div>
@@ -42,13 +42,13 @@
</ul>
<div #logContainer class="bg-dark text-light font-monospace log-container p-3" (scroll)="onScroll()">
@if (loading && !logFiles.length) {
@if (loading() && !logFiles().length) {
<div>
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
<ng-container i18n>Loading...</ng-container>
</div>
} @else {
@for (log of logs; track log) {
@for (log of logs(); track log) {
<p class="m-0 p-0" [ngClass]="'log-entry-' + log.level">{{log.message}}</p>
}
}
@@ -56,7 +56,7 @@
<button
type="button"
class="btn btn-sm btn-secondary jump-to-bottom position-fixed bottom-0 end-0 m-5"
[class.visible]="showJumpToBottom"
[class.visible]="showJumpToBottom()"
(click)="scrollToBottom()"
>
<span i18n>Jump to bottom</span>
@@ -86,7 +86,7 @@ describe('LogsComponent', () => {
throwError(() => new Error('error getting logs'))
)
component.reloadLogs()
expect(component.logs).toHaveLength(0)
expect(component.logs()).toHaveLength(0)
})
it('should auto refresh, allow toggle', () => {
@@ -97,7 +97,7 @@ describe('LogsComponent', () => {
jest.advanceTimersByTime(6000)
expect(reloadSpy).toHaveBeenCalledTimes(2)
component.autoRefreshEnabled = false
component.autoRefreshEnabled.set(false)
jest.advanceTimersByTime(6000)
expect(reloadSpy).toHaveBeenCalledTimes(2)
})
@@ -112,9 +112,9 @@ describe('LogsComponent', () => {
})
it('should update jump to bottom visibility on scroll', () => {
component.showJumpToBottom = false
component.showJumpToBottom.set(false)
jest.spyOn(component as any, 'isNearBottom').mockReturnValue(false)
component.onScroll()
expect(component.showJumpToBottom).toBe(true)
expect(component.showJumpToBottom()).toBe(true)
})
})
@@ -7,6 +7,7 @@ import {
OnInit,
ViewChild,
inject,
signal,
} from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'
@@ -34,17 +35,17 @@ export class LogsComponent
private logService = inject(LogService)
private changedetectorRef = inject(ChangeDetectorRef)
public logs: Array<{ message: string; level: number }> = []
readonly logs = signal<Array<{ message: string; level: number }>>([])
public logFiles: string[] = []
readonly logFiles = signal<string[]>([])
public activeLog: string
readonly activeLog = signal<string>(undefined)
public autoRefreshEnabled: boolean = true
readonly autoRefreshEnabled = signal<boolean>(true)
public limit: number = 5000
readonly limit = signal<number>(5000)
public showJumpToBottom = false
readonly showJumpToBottom = signal<boolean>(false)
private readonly limitChange$ = new Subject<number>()
@@ -59,15 +60,15 @@ export class LogsComponent
.list()
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((result) => {
this.logFiles = result
this.loading = false
if (this.logFiles.length > 0) {
this.activeLog = this.logFiles[0]
this.logFiles.set(result)
this.loading.set(false)
if (this.logFiles().length > 0) {
this.activeLog.set(this.logFiles()[0])
this.reloadLogs()
}
timer(5000, 5000)
.pipe(
filter(() => this.autoRefreshEnabled),
filter(() => this.autoRefreshEnabled()),
takeUntil(this.unsubscribeNotifier)
)
.subscribe(() => {
@@ -85,19 +86,19 @@ export class LogsComponent
}
reloadLogs() {
this.loading = true
this.loading.set(true)
const shouldStickToBottom = this.isNearBottom()
this.logService
.get(this.activeLog, this.limit)
.get(this.activeLog(), this.limit())
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe({
next: (result) => {
this.loading = false
this.loading.set(false)
const parsed = this.parseLogsWithLevel(result)
const hasChanges =
parsed.length !== this.logs.length ||
parsed.length !== this.logs().length ||
parsed.some((log, idx) => {
const current = this.logs[idx]
const current = this.logs()[idx]
return (
!current ||
current.message !== log.message ||
@@ -105,16 +106,16 @@ export class LogsComponent
)
})
if (hasChanges) {
this.logs = parsed
this.logs.set(parsed)
if (shouldStickToBottom) {
this.scrollToBottom()
}
this.showJumpToBottom = !shouldStickToBottom
this.showJumpToBottom.set(!shouldStickToBottom)
}
},
error: () => {
this.logs = []
this.loading = false
this.logs.set([])
this.loading.set(false)
},
})
}
@@ -149,7 +150,7 @@ export class LogsComponent
}
this.changedetectorRef.detectChanges()
viewport.scrollTop = viewport.scrollHeight
this.showJumpToBottom = false
this.showJumpToBottom.set(false)
}
private isNearBottom(): boolean {
@@ -162,6 +163,6 @@ export class LogsComponent
}
onScroll(): void {
this.showJumpToBottom = !this.isNearBottom()
this.showJumpToBottom.set(!this.isNearBottom())
}
}