Use loadComponent for routes and cleanup
Switch route definitions to lazy-load components via loadComponent dynamic imports and remove direct component references from RouterConstants. Remove several components' standalone flags and adjust component metadata (styleUrl vs styleUrls) and imports accordingly. Make AlgorithmsService and AlgorithmsComponent synchronous (getCategories() now returns an array and template iterates categories directly). Replace alert in PathfindingComponent with MatSnackBar and inject it. Simplify LanguageService initialization to use existing translate configuration. Remove unused ReloadService. Make GenericGridComponent.lastCell protected. Miscellaneous tidy-ups across related files.
This commit is contained in:
@@ -1,20 +1,19 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import {AboutComponent} from './pages/about/about.component';
|
|
||||||
import {RouterConstants} from './constants/RouterConstants';
|
import {RouterConstants} from './constants/RouterConstants';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: '', component: AboutComponent },
|
{ path: '', loadComponent: () => import('./pages/about/about.component').then(m => m.AboutComponent) },
|
||||||
{ path: RouterConstants.ABOUT.PATH, component: RouterConstants.ABOUT.COMPONENT},
|
{ path: RouterConstants.ABOUT.PATH, loadComponent: () => import('./pages/about/about.component').then(m => m.AboutComponent) },
|
||||||
{ path: RouterConstants.PROJECTS.PATH, component: RouterConstants.PROJECTS.COMPONENT},
|
{ path: RouterConstants.PROJECTS.PATH, loadComponent: () => import('./pages/projects/projects.component').then(m => m.ProjectsComponent) },
|
||||||
{ path: RouterConstants.ALGORITHMS.PATH, component: RouterConstants.ALGORITHMS.COMPONENT},
|
{ path: RouterConstants.ALGORITHMS.PATH, loadComponent: () => import('./pages/algorithms/algorithms.component').then(m => m.AlgorithmsComponent) },
|
||||||
{ path: RouterConstants.PATHFINDING.PATH, component: RouterConstants.PATHFINDING.COMPONENT},
|
{ path: RouterConstants.PATHFINDING.PATH, loadComponent: () => import('./pages/algorithms/pathfinding/pathfinding.component').then(m => m.PathfindingComponent) },
|
||||||
{ path: RouterConstants.SORTING.PATH, component: RouterConstants.SORTING.COMPONENT},
|
{ path: RouterConstants.SORTING.PATH, loadComponent: () => import('./pages/algorithms/sorting/sorting.component').then(m => m.SortingComponent) },
|
||||||
{ path: RouterConstants.IMPRINT.PATH, component: RouterConstants.IMPRINT.COMPONENT},
|
{ path: RouterConstants.IMPRINT.PATH, loadComponent: () => import('./pages/imprint/imprint.component').then(m => m.ImprintComponent) },
|
||||||
{ path: RouterConstants.GOL.PATH, component: RouterConstants.GOL.COMPONENT},
|
{ path: RouterConstants.GOL.PATH, loadComponent: () => import('./pages/algorithms/conway-gol/conway-gol.component').then(m => m.ConwayGolComponent) },
|
||||||
{ path: RouterConstants.LABYRINTH.PATH, component: RouterConstants.LABYRINTH.COMPONENT},
|
{ path: RouterConstants.LABYRINTH.PATH, loadComponent: () => import('./pages/algorithms/pathfinding/labyrinth/labyrinth.component').then(m => m.LabyrinthComponent) },
|
||||||
{ path: RouterConstants.FRACTAL.PATH, component: RouterConstants.FRACTAL.COMPONENT},
|
{ path: RouterConstants.FRACTAL.PATH, loadComponent: () => import('./pages/algorithms/fractal/fractal.component').then(m => m.FractalComponent) },
|
||||||
{ path: RouterConstants.FRACTAL3d.PATH, component: RouterConstants.FRACTAL3d.COMPONENT},
|
{ path: RouterConstants.FRACTAL3d.PATH, loadComponent: () => import('./pages/algorithms/fractal3d/fractal3d.component').then(m => m.Fractal3dComponent) },
|
||||||
{ path: RouterConstants.PENDULUM.PATH, component: RouterConstants.PENDULUM.COMPONENT},
|
{ path: RouterConstants.PENDULUM.PATH, loadComponent: () => import('./pages/algorithms/pendulum/pendulum.component').then(m => m.default) },
|
||||||
{ path: RouterConstants.CLOTH.PATH, component: RouterConstants.CLOTH.COMPONENT}
|
{ path: RouterConstants.CLOTH.PATH, loadComponent: () => import('./pages/algorithms/cloth/cloth.component').then(m => m.ClothComponent) },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1,88 +1,63 @@
|
|||||||
import {AboutComponent} from '../pages/about/about.component';
|
export class RouterConstants {
|
||||||
import {ProjectsComponent} from '../pages/projects/projects.component';
|
|
||||||
import {ImprintComponent} from '../pages/imprint/imprint.component';
|
|
||||||
import {AlgorithmsComponent} from '../pages/algorithms/algorithms.component';
|
|
||||||
import {PathfindingComponent} from '../pages/algorithms/pathfinding/pathfinding.component';
|
|
||||||
import {SortingComponent} from '../pages/algorithms/sorting/sorting.component';
|
|
||||||
import {ConwayGolComponent} from '../pages/algorithms/conway-gol/conway-gol.component';
|
|
||||||
import {LabyrinthComponent} from '../pages/algorithms/pathfinding/labyrinth/labyrinth.component';
|
|
||||||
import {FractalComponent} from '../pages/algorithms/fractal/fractal.component';
|
|
||||||
import {Fractal3dComponent} from '../pages/algorithms/fractal3d/fractal3d.component';
|
|
||||||
import PendulumComponent from '../pages/algorithms/pendulum/pendulum.component';
|
|
||||||
import {ClothComponent} from '../pages/algorithms/cloth/cloth.component';
|
|
||||||
|
|
||||||
export class RouterConstants {
|
|
||||||
|
|
||||||
static readonly ABOUT = {
|
static readonly ABOUT = {
|
||||||
PATH: 'about',
|
PATH: 'about',
|
||||||
LINK: '/about',
|
LINK: '/about',
|
||||||
COMPONENT: AboutComponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly PROJECTS = {
|
static readonly PROJECTS = {
|
||||||
PATH: 'projects',
|
PATH: 'projects',
|
||||||
LINK: '/projects',
|
LINK: '/projects',
|
||||||
COMPONENT: ProjectsComponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly ALGORITHMS = {
|
static readonly ALGORITHMS = {
|
||||||
PATH: 'algorithms',
|
PATH: 'algorithms',
|
||||||
LINK: '/algorithms',
|
LINK: '/algorithms',
|
||||||
COMPONENT: AlgorithmsComponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly PATHFINDING = {
|
static readonly PATHFINDING = {
|
||||||
PATH: 'algorithms/pathfinding',
|
PATH: 'algorithms/pathfinding',
|
||||||
LINK: '/algorithms/pathfinding',
|
LINK: '/algorithms/pathfinding',
|
||||||
COMPONENT: PathfindingComponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly SORTING = {
|
static readonly SORTING = {
|
||||||
PATH: 'algorithms/sorting',
|
PATH: 'algorithms/sorting',
|
||||||
LINK: '/algorithms/sorting',
|
LINK: '/algorithms/sorting',
|
||||||
COMPONENT: SortingComponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly GOL = {
|
static readonly GOL = {
|
||||||
PATH: 'algorithms/gol',
|
PATH: 'algorithms/gol',
|
||||||
LINK: '/algorithms/gol',
|
LINK: '/algorithms/gol',
|
||||||
COMPONENT: ConwayGolComponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly LABYRINTH = {
|
static readonly LABYRINTH = {
|
||||||
PATH: 'algorithms/labyrinth',
|
PATH: 'algorithms/labyrinth',
|
||||||
LINK: '/algorithms/labyrinth',
|
LINK: '/algorithms/labyrinth',
|
||||||
COMPONENT: LabyrinthComponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly FRACTAL = {
|
static readonly FRACTAL = {
|
||||||
PATH: 'algorithms/fractal',
|
PATH: 'algorithms/fractal',
|
||||||
LINK: '/algorithms/fractal',
|
LINK: '/algorithms/fractal',
|
||||||
COMPONENT: FractalComponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly FRACTAL3d = {
|
static readonly FRACTAL3d = {
|
||||||
PATH: 'algorithms/fractal3d',
|
PATH: 'algorithms/fractal3d',
|
||||||
LINK: '/algorithms/fractal3d',
|
LINK: '/algorithms/fractal3d',
|
||||||
COMPONENT: Fractal3dComponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly PENDULUM = {
|
static readonly PENDULUM = {
|
||||||
PATH: 'algorithms/pendulum',
|
PATH: 'algorithms/pendulum',
|
||||||
LINK: '/algorithms/pendulum',
|
LINK: '/algorithms/pendulum',
|
||||||
COMPONENT: PendulumComponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly CLOTH = {
|
static readonly CLOTH = {
|
||||||
PATH: 'algorithms/cloth',
|
PATH: 'algorithms/cloth',
|
||||||
LINK: '/algorithms/cloth',
|
LINK: '/algorithms/cloth',
|
||||||
COMPONENT: ClothComponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly IMPRINT = {
|
static readonly IMPRINT = {
|
||||||
PATH: 'imprint',
|
PATH: 'imprint',
|
||||||
LINK: '/imprint',
|
LINK: '/imprint',
|
||||||
COMPONENT: ImprintComponent
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {ParticleBackgroundComponent} from '../../shared/components/particles-bac
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
standalone: true,
|
|
||||||
imports: [RouterOutlet, TopbarComponent, TranslatePipe, ParticleBackgroundComponent],
|
imports: [RouterOutlet, TopbarComponent, TranslatePipe, ParticleBackgroundComponent],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.scss'
|
styleUrl: './app.component.scss'
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { MatButtonModule } from '@angular/material/button';
|
|||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
|
||||||
imports: [MatDialogModule, MatButtonModule, MatIconModule],
|
imports: [MatDialogModule, MatButtonModule, MatIconModule],
|
||||||
template: `
|
template: `
|
||||||
<div class="dialog">
|
<div class="dialog">
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import {RouterConstants} from '../../constants/RouterConstants';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-topbar',
|
selector: 'app-topbar',
|
||||||
standalone: true,
|
|
||||||
imports: [
|
imports: [
|
||||||
RouterLink,
|
RouterLink,
|
||||||
MatToolbarModule, MatIconModule, MatButtonModule, MatMenuModule, MatTooltipModule,
|
MatToolbarModule, MatIconModule, MatButtonModule, MatMenuModule, MatTooltipModule,
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import {SharedFunctions} from '../../shared/SharedFunctions';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-about',
|
selector: 'app-about',
|
||||||
standalone: true,
|
|
||||||
imports: [
|
imports: [
|
||||||
NgOptimizedImage,
|
NgOptimizedImage,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<h1>{{ 'ALGORITHM.TITLE' |translate }}</h1>
|
<h1>{{ 'ALGORITHM.TITLE' |translate }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-grid">
|
<div class="card-grid">
|
||||||
@for (category of categories$ | async; track category.id) {
|
@for (category of categories; track category.id) {
|
||||||
<mat-card class="algo-card" [routerLink]="[category.routerLink]">
|
<mat-card class="algo-card" [routerLink]="[category.routerLink]">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{ category.title | translate }}</mat-card-title>
|
<mat-card-title>{{ category.title | translate }}</mat-card-title>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { Component, OnInit, inject } from '@angular/core';
|
import { Component, inject } from '@angular/core';
|
||||||
import { AlgorithmsService } from './algorithms.service';
|
import { AlgorithmsService } from './algorithms.service';
|
||||||
import { AlgorithmCategory } from './algorithm-category';
|
import { AlgorithmCategory } from './algorithm-category';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { RouterLink } from '@angular/router';
|
import { RouterLink } from '@angular/router';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import {TranslatePipe} from '@ngx-translate/core';
|
import {TranslatePipe} from '@ngx-translate/core';
|
||||||
@@ -10,16 +8,11 @@ import {TranslatePipe} from '@ngx-translate/core';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-algorithms',
|
selector: 'app-algorithms',
|
||||||
templateUrl: './algorithms.component.html',
|
templateUrl: './algorithms.component.html',
|
||||||
styleUrls: ['./algorithms.component.scss'],
|
styleUrl: './algorithms.component.scss',
|
||||||
standalone: true,
|
imports: [RouterLink, MatCardModule, TranslatePipe],
|
||||||
imports: [CommonModule, RouterLink, MatCardModule, TranslatePipe],
|
|
||||||
})
|
})
|
||||||
export class AlgorithmsComponent implements OnInit {
|
export class AlgorithmsComponent {
|
||||||
private readonly algorithmsService = inject(AlgorithmsService);
|
private readonly algorithmsService = inject(AlgorithmsService);
|
||||||
|
|
||||||
categories$: Observable<AlgorithmCategory[]> | undefined;
|
readonly categories: AlgorithmCategory[] = this.algorithmsService.getCategories();
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.categories$ = this.algorithmsService.getCategories();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { AlgorithmCategory } from './algorithm-category';
|
import { AlgorithmCategory } from './algorithm-category';
|
||||||
import { Observable, of } from 'rxjs';
|
|
||||||
import {RouterConstants} from '../../constants/RouterConstants';
|
import {RouterConstants} from '../../constants/RouterConstants';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@@ -59,7 +58,7 @@ export class AlgorithmsService {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
getCategories(): Observable<AlgorithmCategory[]> {
|
getCategories(): AlgorithmCategory[] {
|
||||||
return of(this.categories);
|
return this.categories;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {MatButtonModule} from '@angular/material/button';
|
|||||||
import {MatButtonToggleModule} from '@angular/material/button-toggle';
|
import {MatButtonToggleModule} from '@angular/material/button-toggle';
|
||||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||||
import {MatInputModule} from '@angular/material/input';
|
import {MatInputModule} from '@angular/material/input';
|
||||||
|
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||||
|
|
||||||
import {TranslateModule, TranslateService} from '@ngx-translate/core';
|
import {TranslateModule, TranslateService} from '@ngx-translate/core';
|
||||||
|
|
||||||
@@ -27,7 +28,6 @@ enum NodeType {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-pathfinding',
|
selector: 'app-pathfinding',
|
||||||
standalone: true,
|
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
@@ -48,6 +48,7 @@ enum NodeType {
|
|||||||
export class PathfindingComponent implements AfterViewInit {
|
export class PathfindingComponent implements AfterViewInit {
|
||||||
private readonly pathfindingService = inject(PathfindingService);
|
private readonly pathfindingService = inject(PathfindingService);
|
||||||
private readonly translate = inject(TranslateService);
|
private readonly translate = inject(TranslateService);
|
||||||
|
private readonly snackBar = inject(MatSnackBar);
|
||||||
|
|
||||||
readonly NodeType = NodeType;
|
readonly NodeType = NodeType;
|
||||||
readonly MIN_GRID_SIZE = MIN_GRID_SIZE;
|
readonly MIN_GRID_SIZE = MIN_GRID_SIZE;
|
||||||
@@ -483,7 +484,8 @@ export class PathfindingComponent implements AfterViewInit {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
alert(this.translate.instant('PATHFINDING.ALERT.START_END_NODES'));
|
const message = this.translate.instant('PATHFINDING.ALERT.START_END_NODES');
|
||||||
|
this.snackBar.open(message, 'OK', { duration: 5000, horizontalPosition: 'center', verticalPosition: 'top' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,10 +15,9 @@ import {AlgorithmInformation} from '../information/information.models';
|
|||||||
import {Information} from '../information/information';
|
import {Information} from '../information/information';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-sorting',
|
selector: 'app-sorting',
|
||||||
standalone: true,
|
|
||||||
imports: [CommonModule, MatCardModule, MatFormFieldModule, MatSelectModule, MatButtonModule, MatIconModule, TranslateModule, FormsModule, MatInput, Information],
|
imports: [CommonModule, MatCardModule, MatFormFieldModule, MatSelectModule, MatButtonModule, MatIconModule, TranslateModule, FormsModule, MatInput, Information],
|
||||||
templateUrl: './sorting.component.html',
|
templateUrl: './sorting.component.html',
|
||||||
styleUrls: ['./sorting.component.scss']
|
styleUrl: './sorting.component.scss'
|
||||||
})
|
})
|
||||||
export class SortingComponent implements OnInit {
|
export class SortingComponent implements OnInit {
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ import {MatButton} from '@angular/material/button';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-project-dialog',
|
selector: 'app-project-dialog',
|
||||||
templateUrl: './project-dialog.component.html',
|
templateUrl: './project-dialog.component.html',
|
||||||
styleUrls: ['./project-dialog.component.scss'],
|
styleUrl: './project-dialog.component.scss',
|
||||||
standalone: true,
|
|
||||||
imports: [
|
imports: [
|
||||||
MatDialogTitle,
|
MatDialogTitle,
|
||||||
MatDialogContent,
|
MatDialogContent,
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ export interface Projects {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-projects',
|
selector: 'app-projects',
|
||||||
standalone: true,
|
|
||||||
imports: [
|
imports: [
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatChipsModule,
|
MatChipsModule,
|
||||||
|
|||||||
@@ -10,10 +10,9 @@ export class LanguageService {
|
|||||||
readonly lang = signal<Lang>(this.getInitial());
|
readonly lang = signal<Lang>(this.getInitial());
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.translate.addLangs(['de', 'en']);
|
// translate service lang and fallback are already configured via provideTranslateService in app.config
|
||||||
this.translate.setFallbackLang('en');
|
// just ensure the stored preference is active on startup
|
||||||
this.lang.set(this.getInitial());
|
this.translate.use(this.lang());
|
||||||
this.use(this.lang());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
use(l: Lang) {
|
use(l: Lang) {
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import { Injectable, NgZone, signal } from '@angular/core';
|
|
||||||
import {LocalStoreConstants} from '../constants/LocalStoreConstants';
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class ReloadService {
|
|
||||||
private readonly _reloadTick = signal(0);
|
|
||||||
readonly reloadTick = this._reloadTick.asReadonly();
|
|
||||||
|
|
||||||
private readonly _languageChangedTick = signal(0);
|
|
||||||
readonly languageChangedTick = this._languageChangedTick.asReadonly();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private informListeners(e: StorageEvent, zone: NgZone) {
|
|
||||||
if (e.key === LocalStoreConstants.LANGUAGE_KEY) {
|
|
||||||
zone.run(() => this._languageChangedTick.update(v => v + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bumpLanguageChanged(): void {
|
|
||||||
this._reloadTick.update(v => v + 1);
|
|
||||||
localStorage.setItem(LocalStoreConstants.RELOAD_ALL_LANG_LISTENER_KEY, String(Date.now()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,6 @@ export interface GridPos { row: number; col: number }
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-generic-grid',
|
selector: 'app-generic-grid',
|
||||||
standalone: true,
|
|
||||||
imports: [CommonModule],
|
imports: [CommonModule],
|
||||||
templateUrl: './generic-grid.html',
|
templateUrl: './generic-grid.html',
|
||||||
styleUrl: './generic-grid.scss',
|
styleUrl: './generic-grid.scss',
|
||||||
@@ -36,7 +35,7 @@ export class GenericGridComponent implements AfterViewInit {
|
|||||||
grid: any[][] = [];
|
grid: any[][] = [];
|
||||||
|
|
||||||
isDrawing = false;
|
isDrawing = false;
|
||||||
private lastCell: GridPos | null = null;
|
protected lastCell: GridPos | null = null;
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
this.ctx = this.getContextOrThrow();
|
this.ctx = this.getContextOrThrow();
|
||||||
|
|||||||
Reference in New Issue
Block a user