Auch schon einmal in ein ähnliches Problem gelaufen?
Eigentlich ist nach der Vorbereitung laut offiziellem Update Manual alles wie erwartet verlaufen,
der Befehl ng update @angular/core@latest @angular/cli@latest
führt auch wie erwartet aus
und dann passiert das:
Fehlermeldung:
✖ Migration failed: Path “src/test.ts” does not exist.
Ein Blick in die package.json
Datei hat keine offensichtlichen Fehler aufgezeigt.
Alle Dependencies wurden wie erwartet installiert.
Ein kurzer Check mit ng serve
zeigt auch keine Auffälligkeiten -
die Applikation kompiliert.
Der Lauf des Scripts wurde jedoch unterbrochen,
also dürfte es Stellen gegeben haben,
an denen das Update nicht vollständig abgeschlossen wurde.
Beim Testen der Applikation stellte sich Folgendes heraus:
Beim Aufruf von einigen Komponenten tauchte die folgende Fehlermeldung auf:
ERROR Error: Uncaught (in promise): TypeError: Cannot read properties of undefined
(reading 'getUserPreference')
TypeError: Cannot read properties of undefined (reading 'getUserPreference')
at new ViewComponent (view.component.ts:410:31)
at NodeInjectorFactory.ViewComponent_Factory [as factory] (view.component.ts:375:28)
at getNodeInjectable (core.mjs:3485:44)
at createRootComponent (core.mjs:12391:35)
at ComponentFactory.create (core.mjs:12272:25)
at ViewContainerRef.createComponent (core.mjs:21416:47)
at RouterOutlet.activateWith (router.mjs:2611:39)
at ActivateRoutes.activateRoutes (router.mjs:3023:40)
at router.mjs:2972:18
at Array.forEach (<anonymous>)
at resolvePromise (zone.js:1214:31)
at resolvePromise (zone.js:1168:17)
at zone.js:1281:17
at _ZoneDelegate.invokeTask (zone.js:409:31)
at AsyncStackTaggingZoneSpec.onInvokeTask (core.mjs:24002:28)
at _ZoneDelegate.invokeTask (zone.js:408:60)
at Object.onInvokeTask (core.mjs:24300:33)
at _ZoneDelegate.invokeTask (zone.js:408:60)
at Zone.runTask (zone.js:178:47)
at drainMicroTaskQueue (zone.js:588:35)
Nach näherer Betrachtung der im StackTrace
gelisteten ViewComponent
und der Methode getUserPreference
stellte sich heraus,
dass Injected Services
,
welche direkt bei der Konstruktion einer Klasse von Class Fields
aufgerufen wurden,
diesen Fehler hervorgerufen haben.
export class ViewComponent extends AbstractComponentDirective implements OnInit {
state: ViewState = {
theme: this.themeService.getUserPreference()
}
constructor(private readonly themeService: ThemeService) {
super();
}
}
Bei Betrachtung des Problems im Debugger
haben wir festgestellt,
dass die Klasse ThemeService
als Injected Service
gar nicht geladen wurde.
@Injectable({
providedIn: 'root'
})
export class ThemeService {
getUserPreference(): Theme {
return (localStorage.getItem(localStorageThemeKey) ?? 'light') as Theme;
}
setUserPreference(theme: Theme): void {
localStorage.setItem(localStorageThemeKey, theme);
}
}
Dies liess uns darauf schliessen,
dass es sich eventuell um einen Fehler beim Kompilieren von
TypeScript handelt.
Wir haben anschliessend einen Abgleich des tsconfig.json
mit einem zweiten
Angular Projekt vorgenommen,
das wir bereits erfolgreich auf
Angular 15 aktualisiert hatten.
Der wesentlichste Unterschied war die Konfiguration "useDefineForClassFields": false
im tsconfig.json
.
{
"compilerOptions": {
"useDefineForClassFields": false
}
}
In der Dokumentation von TypeScript haben wir folgende Information dazu gefunden:
# Use Define For Class Fields -
useDefineForClassFields
useDefineForClassFields This flag is used as part of migrating to the upcoming standard version of class fields. TypeScript introduced class fields many years before it was ratified in TC39. The latest version of the upcoming specification has a different runtime behavior to TypeScript’s implementation but the same syntax.This flag switches to the upcoming ECMA runtime behavior.
Mit dem wichtigen Zusatz:
Default:
true
if target isES2022
or higher, including ESNext,false
otherwise.
Nach dem Update auf
Angular 15 wurde der target
Wert im tsconfig.json
durch das
Angular-CLI automatisch auf ES2022
gesetzt.
Wegen des veränderten Runtime Behaviors für Class Fields
hatten wir somit die Probleme mit Injected Services
,
welche von Class Fields
aufgerufen wurden.
Bei einem vollständigen Lauf von ng update @angular/core@latest @angular/cli@latest
sollte diese Einstellung automatisch hinzugefügt werden.
Mit der von uns manuell angepassten Konfiguration im tsconfig.json
hat nach dem Update auf
Angular 15 in unserer Applikation wieder alles so funktioniert,
wie erwartet.