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 -
useDefineForClassFieldsuseDefineForClassFields 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:
trueif target isES2022or higher, including ESNext,falseotherwise.
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.
Mein Name ist Christian Mäder. Jeden Freitag nehme ich mir eine Stunde Zeit, um Fragen rund um die Entwicklung und Pflege von Software zu beantworten.
Solche und weitere deiner Fragen beantworte ich dir gerne. Die Stunde steht exklusiv dir zur Verfügung. Sie ist für dich komplett kostenlos und unverbindlich. Du musst dich lediglich anmelden: