升级Angular 15后AppComponent不存在selectedIndex属性的问题求助
问题描述
我有一个基于Ionic 6和Capacitor的应用,已将Angular版本升级至15.1.0,相关依赖配置如下:
"dependencies": { "@angular/common": "^15.1.0", "@angular/core": "^15.1.0", "@angular/forms": "^15.1.0", "@angular/platform-browser": "^15.1.0", "@angular/platform-browser-dynamic": "^15.1.0", "@angular/router": "^15.1.0", "@capacitor-community/admob": "^4.0.0", "@capacitor/android": "^4.6.1", "@capacitor/app": "4.1.1", "@capacitor/core": "4.6.1", "@capacitor/haptics": "4.1.0", "@capacitor/keyboard": "4.1.0", "@capacitor/share": "^4.1.0", "@capacitor/status-bar": "4.1.1", "@ionic-native/sqlite": "^5.36.0", "@ionic-native/sqlite-porter": "^5.36.0", "@ionic/angular": "^6.4.2", "@ionic/storage-angular": "^3.0.6", "cordova-sqlite-storage": "^6.1.0", "ionicons": "^6.0.4", "localforage-cordovasqlitedriver": "^1.8.0", "rxjs": "~7.8.0", "tslib": "^2.4.1", "zone.js": "~0.12.0" }, "devDependencies": { "@angular-devkit/build-angular": "^15.1.0", "@angular-eslint/builder": "~15.1.0", "@angular-eslint/eslint-plugin": "~15.1.0", "@angular-eslint/eslint-plugin-template": "~15.1.0", "@angular-eslint/template-parser": "~15.1.0", "@angular/cli": "^15.1.0", "@angular/compiler": "^15.1.0", "@angular/compiler-cli": "^15.1.0", "@angular/language-service": "^15.1.0", "@capacitor/cli": "4.6.1", "@ionic/angular-toolkit": "^7.0.0", "@types/jasmine": "~4.3.1", "@types/jasminewd2": "~2.0.10", "@types/node": "^18.11.18", "@typescript-eslint/eslint-plugin": "5.48.1", "@typescript-eslint/parser": "5.48.1", "eslint": "^8.31.0", "eslint-plugin-import": "2.27.4", "eslint-plugin-jsdoc": "39.6.4", "eslint-plugin-prefer-arrow": "1.2.3", "jasmine-core": "~4.5.0", "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.1", "karma-chrome-launcher": "~3.1.1", "karma-coverage": "~2.2.0", "karma-coverage-istanbul-reporter": "~3.0.3", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "^2.0.0", "protractor": "~7.0.0", "ts-node": "~10.9.1", "typescript": "~4.9.4" }
应用侧边菜单代码如下:
app.component.html
<ion-app> <ion-split-pane contentId="main-content"> <ion-menu contentId="main-content" type="overlay"> <ion-content> <ion-list id="inbox-list"> <ion-menu-toggle auto-hide="false" *ngFor="let p of appPages; let i = index"> <ion-item (click)="selectedIndex = i" routerDirection="root" [routerLink]="[p.url]" lines="none" detail="false" [class.selected]="selectedIndex == i"> <ion-icon slot="start" [ios]="p.icon" [md]="p.icon" color="secondary"></ion-icon> <ion-label>{{ p.title }}</ion-label> </ion-item> </ion-menu-toggle> </ion-list> </ion-content> </ion-menu> <ion-router-outlet id="main-content"></ion-router-outlet> </ion-split-pane> </ion-app>
app.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: 'app.component.html', styleUrls: ['app.component.scss'], }) export class AppComponent { constructor() { } appPages = [ { title: 'Calculate', url: '', icon: 'calculator-sharp' }, { title: 'Favorites', url: '/favorites', icon: 'star-sharp' }, { title: 'Settings', url: '/settings', icon: 'settings-sharp' }, ]; }
升级后侧边菜单的selectedIndex属性失效,控制台报错:
[ng] Error: src/app/app.component.html:7:32 - error TS2339: Property 'selectedIndex' does not exist on type 'AppComponent'. [ng] [ng] 7 <ion-item (click)="selectedIndex = i" routerDirection="root" [routerLink]="[p.url]" lines="none" [ng] ~~~~~~~~~~~~~ [ng] [ng] src/app/app.component.ts:5:16 [ng] 5 templateUrl: 'app.component.html', [ng] ~~~~~~~~~~~~~~~~~~~~ [ng] Error occurs in the template of component AppComponent. [ng] [ng] [ng] Error: src/app/app.component.html:8:48 - error TS2339: Property 'selectedIndex' does not exist on type 'AppComponent'. [ng] [ng] 8 detail="false" [class.selected]="selectedIndex == i"> [ng] ~~~~~~~~~~~~~ [ng] [ng] src/app/app.component.ts:5:16 [ng] 5 templateUrl: 'app.component.html', [ng] ~~~~~~~~~~~~~~~~~~~~ [ng] Error occurs in the template of component AppComponent.
我查阅了Angular 15迁移指南,未找到相关说明,请问为何selectedIndex不再生效?
更新:附上angular.json配置
{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "app": { "root": "", "sourceRoot": "src", "projectType": "application", "prefix": "app", "schematics": {}, "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "www", "index": "src/index.html", "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", "assets": [ { "glob": "**/*", "input": "src/assets", "output": "assets" }, { "glob": "**/*.svg", "input": "node_modules/ionicons/dist/ionicons/svg", "output": "./svg" } ], "styles": ["src/theme/variables.scss", "src/global.scss"], "scripts": [], "aot": false, "vendorChunk": true, "extractLicenses": false, "buildOptimizer": false, "sourceMap": true, "optimization": false, "namedChunks": true }, "configurations": { "production": { "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ], "optimization": true, "outputHashing": "all", "sourceMap": false, "namedChunks": false, "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, "budgets": [ { "type": "initial", "maximumWarning": "2mb", "maximumError": "5mb" } ] }, "ci": { "progress": false } } }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { "browserTarget": "app:build" }, "configurations": { "production": { "browserTarget": "app:build:production" }, "ci": { "progress": false } } }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { "browserTarget": "app:build" } }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { "main": "src/test.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.spec.json", "karmaConfig": "karma.conf.js", "styles": [], "scripts": [], "assets": [ { "glob": "favicon.ico", "input": "src/", "output": "/" }, { "glob": "**/*", "input": "src/assets", "output": "/assets" } ] }, "configurations": { "ci": { "progress": false, "watch": false } } }, "lint": { "builder": "@angular-eslint/builder:lint", "options": { "lintFilePatterns": [ "src/**/*.ts", "src/**/*.html" ] } }, "e2e": { "builder": "@angular-devkit/build-angular:protractor", "options": { "protractorConfig": "e2e/protractor.conf.js", "devServerTarget": "app:serve" }, "configurations": { "production": { "devServerTarget": "app:serve:production" }, "ci": { "devServerTarget": "app:serve:ci" } } } } } }, "cli": { "schematicCollections": [ "@ionic/angular-toolkit" ], "analytics": false }, "schematics": { "@ionic/angular-toolkit:component": { "styleext": "scss" }, "@ionic/angular-toolkit:page": { "styleext": "scss" } } }
解决方案
这不是Angular 15的特性变更问题,而是Angular严格模板检查的要求:
之前的Angular版本中,模板里直接赋值selectedIndex = i时,Angular会隐式在组件类上创建这个属性,但在升级后的环境中(可能是启用了更严格的类型检查,或是Angular 12+默认强化了严格模式),不允许这种隐式属性创建,必须在组件类中显式声明selectedIndex。
修改app.component.ts,添加selectedIndex属性并初始化:
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: 'app.component.html', styleUrls: ['app.component.scss'], }) export class AppComponent { selectedIndex = 0; // 显式声明并初始化,默认选中第一个菜单 constructor() { } appPages = [ { title: 'Calculate', url: '', icon: 'calculator-sharp' }, { title: 'Favorites', url: '/favorites', icon: 'star-sharp' }, { title: 'Settings', url: '/settings', icon: 'settings-sharp' }, ]; }
这样就能解决TS2339错误,同时selectedIndex的选中状态逻辑也能正常工作。
另外,建议检查项目的tsconfig.json中的strictTemplates配置,该选项开启后会强制模板中的所有属性必须在组件类中显式声明,这也是升级后报错的核心原因——之前可能该选项未开启,升级后默认启用或是被配置为开启状态。
内容的提问来源于stack exchange,提问作者Fearcoder
相关产品推荐
相关产品推荐

