Injection par inject() plutôt que par paramètres de constructeur
Corrige les 14 erreurs @angular-eslint/prefer-inject (EngineService, BodyDetailSceneComponent, InfoPanelComponent, GalaxySystemSceneComponent, SearchComponent). Chaque paramètre devient un champ `inject()` de même nom, même visibilité (navigationStore reste public : le template le lit) et dans le même ordre, placé avant les autres champs : les dépendances restent résolues avant tout autre initialiseur, comme l'étaient les paramètres. Les corps de constructeur (effects, buildIndex) ne changent pas. engine.service.spec.ts construisait EngineService avec `new` et un faux NgZone : il le fait maintenant dans runInInjectionContext, avec le même faux NgZone fourni par un Injector. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { NgZone } from '@angular/core';
|
import { Injector, NgZone, runInInjectionContext } from '@angular/core';
|
||||||
import * as THREE from 'three/webgpu';
|
import * as THREE from 'three/webgpu';
|
||||||
import { beforeEach, describe, expect, it } from 'vitest';
|
import { beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@@ -10,7 +10,8 @@ import { EngineService } from './engine.service';
|
|||||||
* arithmetic that keeps them showing the same thing.
|
* arithmetic that keeps them showing the same thing.
|
||||||
*/
|
*/
|
||||||
function engineWithCameras(): { engine: EngineService; perspective: THREE.PerspectiveCamera; orthographic: THREE.OrthographicCamera } {
|
function engineWithCameras(): { engine: EngineService; perspective: THREE.PerspectiveCamera; orthographic: THREE.OrthographicCamera } {
|
||||||
const engine = new EngineService({ runOutsideAngular: (fn: () => unknown) => fn() } as unknown as NgZone);
|
const zone = { runOutsideAngular: (fn: () => unknown) => fn() } as unknown as NgZone;
|
||||||
|
const engine = runInInjectionContext(Injector.create({ providers: [{ provide: NgZone, useValue: zone }] }), () => new EngineService());
|
||||||
const perspective = new THREE.PerspectiveCamera(50, 16 / 9, 0.1, 1000);
|
const perspective = new THREE.PerspectiveCamera(50, 16 / 9, 0.1, 1000);
|
||||||
const orthographic = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 1000);
|
const orthographic = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 1000);
|
||||||
// The two cameras are private, because nothing outside should choose between them by hand.
|
// The two cameras are private, because nothing outside should choose between them by hand.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable, NgZone } from '@angular/core';
|
import { inject, Injectable, NgZone } from '@angular/core';
|
||||||
import * as THREE from 'three/webgpu';
|
import * as THREE from 'three/webgpu';
|
||||||
|
|
||||||
export type EngineTickCallback = (deltaSeconds: number, elapsedSeconds: number) => void;
|
export type EngineTickCallback = (deltaSeconds: number, elapsedSeconds: number) => void;
|
||||||
@@ -20,6 +20,7 @@ export type SceneCamera = THREE.PerspectiveCamera | THREE.OrthographicCamera;
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EngineService {
|
export class EngineService {
|
||||||
|
private readonly ngZone = inject(NgZone);
|
||||||
private readonly clock = new THREE.Clock(false);
|
private readonly clock = new THREE.Clock(false);
|
||||||
private readonly tickCallbacks = new Set<EngineTickCallback>();
|
private readonly tickCallbacks = new Set<EngineTickCallback>();
|
||||||
|
|
||||||
@@ -36,8 +37,6 @@ export class EngineService {
|
|||||||
private projection: Projection = 'perspective';
|
private projection: Projection = 'perspective';
|
||||||
private running = false;
|
private running = false;
|
||||||
|
|
||||||
constructor(private readonly ngZone: NgZone) {}
|
|
||||||
|
|
||||||
get isInitialized(): boolean {
|
get isInitialized(): boolean {
|
||||||
return !!this.renderer;
|
return !!this.renderer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AfterViewInit, Component, ElementRef, OnDestroy, signal, viewChild } from '@angular/core';
|
import { AfterViewInit, Component, ElementRef, inject, OnDestroy, signal, viewChild } from '@angular/core';
|
||||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import * as THREE from 'three/webgpu';
|
import * as THREE from 'three/webgpu';
|
||||||
@@ -65,6 +65,12 @@ const GLOW_SCALE = 2.6;
|
|||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class BodyDetailSceneComponent implements AfterViewInit, OnDestroy {
|
export class BodyDetailSceneComponent implements AfterViewInit, OnDestroy {
|
||||||
|
private readonly engine = inject(EngineService);
|
||||||
|
private readonly dataLoader = inject(DataLoaderService);
|
||||||
|
private readonly route = inject(ActivatedRoute);
|
||||||
|
private readonly router = inject(Router);
|
||||||
|
private readonly navigationStore = inject(NavigationStore);
|
||||||
|
|
||||||
/** A kept place, revisited from this page: a star means leaving it for the map. */
|
/** A kept place, revisited from this page: a star means leaving it for the map. */
|
||||||
goToBookmark(bookmark: Bookmark): void {
|
goToBookmark(bookmark: Bookmark): void {
|
||||||
if (bookmark.kind === 'star') {
|
if (bookmark.kind === 'star') {
|
||||||
@@ -95,14 +101,6 @@ export class BodyDetailSceneComponent implements AfterViewInit, OnDestroy {
|
|||||||
readonly viewModel = signal<BodyDetailViewModel | undefined>(undefined);
|
readonly viewModel = signal<BodyDetailViewModel | undefined>(undefined);
|
||||||
readonly notFound = signal(false);
|
readonly notFound = signal(false);
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly engine: EngineService,
|
|
||||||
private readonly dataLoader: DataLoaderService,
|
|
||||||
private readonly route: ActivatedRoute,
|
|
||||||
private readonly router: Router,
|
|
||||||
private readonly navigationStore: NavigationStore
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
void this.bootstrap();
|
void this.bootstrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ import { ReadoutSectionsComponent } from './readout-sections.component';
|
|||||||
export class InfoPanelComponent {
|
export class InfoPanelComponent {
|
||||||
readonly body = input.required<BodyDetailViewModel>();
|
readonly body = input.required<BodyDetailViewModel>();
|
||||||
|
|
||||||
|
private readonly router = inject(Router);
|
||||||
|
|
||||||
readonly bookmarks = inject(BookmarksStore);
|
readonly bookmarks = inject(BookmarksStore);
|
||||||
|
|
||||||
private readonly articles = inject(ArticleService);
|
private readonly articles = inject(ArticleService);
|
||||||
@@ -105,7 +107,7 @@ export class InfoPanelComponent {
|
|||||||
readonly article = signal<Article | null>(null);
|
readonly article = signal<Article | null>(null);
|
||||||
readonly aboutState = signal<'idle' | 'loading' | 'none' | 'unavailable'>('idle');
|
readonly aboutState = signal<'idle' | 'loading' | 'none' | 'unavailable'>('idle');
|
||||||
|
|
||||||
constructor(private readonly router: Router) {
|
constructor() {
|
||||||
// The panel is reused as the route's parameter changes, so what was asked about one body
|
// The panel is reused as the route's parameter changes, so what was asked about one body
|
||||||
// must not still be showing under the next one's name.
|
// must not still be showing under the next one's name.
|
||||||
effect(() => {
|
effect(() => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AfterViewInit, Component, computed, effect, ElementRef, OnDestroy, signal, viewChild } from '@angular/core';
|
import { AfterViewInit, Component, computed, effect, ElementRef, inject, OnDestroy, signal, viewChild } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import * as THREE from 'three/webgpu';
|
import * as THREE from 'three/webgpu';
|
||||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||||||
@@ -323,6 +323,11 @@ function galacticOverviewPose(): { position: THREE.Vector3; target: THREE.Vector
|
|||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
||||||
|
private readonly engine = inject(EngineService);
|
||||||
|
private readonly dataLoader = inject(DataLoaderService);
|
||||||
|
private readonly router = inject(Router);
|
||||||
|
readonly navigationStore = inject(NavigationStore);
|
||||||
|
|
||||||
private readonly canvasRef = viewChild.required<ElementRef<HTMLCanvasElement>>('canvas');
|
private readonly canvasRef = viewChild.required<ElementRef<HTMLCanvasElement>>('canvas');
|
||||||
private readonly labelHostRef = viewChild.required<ElementRef<HTMLDivElement>>('labelHost');
|
private readonly labelHostRef = viewChild.required<ElementRef<HTMLDivElement>>('labelHost');
|
||||||
private readonly leaderRef = viewChild.required<ElementRef<SVGLineElement>>('leader');
|
private readonly leaderRef = viewChild.required<ElementRef<SVGLineElement>>('leader');
|
||||||
@@ -467,12 +472,7 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
|||||||
private starMarker?: THREE.Mesh;
|
private starMarker?: THREE.Mesh;
|
||||||
private starGlow?: THREE.Sprite;
|
private starGlow?: THREE.Sprite;
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
private readonly engine: EngineService,
|
|
||||||
private readonly dataLoader: DataLoaderService,
|
|
||||||
private readonly router: Router,
|
|
||||||
readonly navigationStore: NavigationStore
|
|
||||||
) {
|
|
||||||
effect(() => {
|
effect(() => {
|
||||||
const selectedStarId = this.navigationStore.selectedStarId();
|
const selectedStarId = this.navigationStore.selectedStarId();
|
||||||
if (this.ready) {
|
if (this.ready) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, computed, ElementRef, output, signal, viewChild } from '@angular/core';
|
import { Component, computed, ElementRef, inject, output, signal, viewChild } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { DataLoaderService } from '../../core/data/data-loader.service';
|
import { DataLoaderService } from '../../core/data/data-loader.service';
|
||||||
@@ -80,6 +80,10 @@ const KIND_LABELS: Record<SearchResultKind, string> = {
|
|||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class SearchComponent {
|
export class SearchComponent {
|
||||||
|
private readonly dataLoader = inject(DataLoaderService);
|
||||||
|
private readonly navigationStore = inject(NavigationStore);
|
||||||
|
private readonly router = inject(Router);
|
||||||
|
|
||||||
readonly query = signal('');
|
readonly query = signal('');
|
||||||
/** Fires once a result has been chosen and navigation kicked off — the dock uses it to hand
|
/** Fires once a result has been chosen and navigation kicked off — the dock uses it to hand
|
||||||
* the view back to the readout, since the thing to look at is now the scene, not the box. */
|
* the view back to the readout, since the thing to look at is now the scene, not the box. */
|
||||||
@@ -109,11 +113,7 @@ export class SearchComponent {
|
|||||||
readonly matchTotal = computed(() => this.matches().length);
|
readonly matchTotal = computed(() => this.matches().length);
|
||||||
readonly results = computed(() => this.matches().slice(0, MAX_RESULTS));
|
readonly results = computed(() => this.matches().slice(0, MAX_RESULTS));
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
private readonly dataLoader: DataLoaderService,
|
|
||||||
private readonly navigationStore: NavigationStore,
|
|
||||||
private readonly router: Router
|
|
||||||
) {
|
|
||||||
void this.buildIndex();
|
void this.buildIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user