从Angular 13升级到16后管道及属性绑定报错求助
问题描述
将Angular项目从13版本逐步升级至16版本后,启动项目时出现两个核心错误:
- 找不到名为
translate的自定义管道 - 无法绑定到
ngClass(提示其不是button的已知属性)
已尝试删除并重新安装node_modules,问题未解决。
具体错误示例
找不到名为'translate'的管道。
模板中管道使用代码:
{{'_AVERAGE_HANDLING_TIME_' | translate}}
现有模块配置
已将translate管道导入PipesModule,PipesModule导入至SharedModule,SharedModule又导入至AppModule,相关模块代码如下:
pipes.module.ts
import { NgModule } from '@angular/core'; import { TranslatePipe } from './translate.pipe'; import { SafePipe } from './safe.pipe'; import { DashboardSearchPipe, GenderTextPipe, SuggestionSearchPipe, TaskTemplateSearchPipe, TemplateTypePipe, WallboardSearchPipe } from './custome.pipe'; @NgModule({ exports: [ TranslatePipe, SafePipe, TemplateTypePipe, SuggestionSearchPipe, TaskTemplateSearchPipe, DashboardSearchPipe, GenderTextPipe, WallboardSearchPipe ], declarations: [ TranslatePipe, SafePipe, TemplateTypePipe, SuggestionSearchPipe, TaskTemplateSearchPipe, DashboardSearchPipe, GenderTextPipe, WallboardSearchPipe ], providers: [ TranslatePipe, SafePipe, TemplateTypePipe, SuggestionSearchPipe, TaskTemplateSearchPipe, DashboardSearchPipe, GenderTextPipe, WallboardSearchPipe ] }) export class PipesModule { }
shared.module.ts
import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { PipesModule } from '../pipes/pipes.module'; import { AppRoutingModule } from '../app.routing.module'; import { EditorModule } from '@tinymce/tinymce-angular'; import { TagInputModule } from 'ngx-chips'; import { AngularMaterialCommonModule } from '../angular-material-common.module'; import { DirectivesModule } from '../directives/directive.module'; @NgModule({ imports: [ CommonModule, AppRoutingModule, EditorModule, TagInputModule, FormsModule, ReactiveFormsModule, AngularMaterialCommonModule, PipesModule, DirectivesModule ], declarations: [], exports: [ CommonModule, AppRoutingModule, EditorModule, TagInputModule, FormsModule, ReactiveFormsModule, AngularMaterialCommonModule, PipesModule, DirectivesModule ], }) export class SharedModule { }
app.module.ts
import { APP_INITIALIZER, NgModule } from '@angular/core'; import { TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular'; import { defineLocale } from 'ngx-bootstrap/chronos'; import { BsDatepickerModule } from 'ngx-bootstrap/datepicker'; import { BsDropdownModule } from 'ngx-bootstrap/dropdown'; import { nlLocale } from 'ngx-bootstrap/locale'; import { ModalModule } from 'ngx-bootstrap/modal'; import { AppComponent } from './app.component'; import { AuditLogComponent } from './auditlog/auditlog.component'; import { CaseSearchResultComponent } from './case/casesearchresult.component'; import { AboutUsComponent } from './common/aboutus.component'; import { AppLoaderComponent } from './common/apploader.component'; import { AuthGuard } from './common/auth.guard'; import { HeaderComponent } from './common/header.component'; import { LinkUnlinkEntityComponent } from './common/linkunlinkentity.component'; import { LoginComponent } from './common/login.component'; import { MessageBoxComponent } from './common/messagebox.component'; import { ProgressBarComponent } from './common/progress-bar.component'; import { SearchPopupComponent } from './common/searchpopup.component'; import { DashboardComponent } from './dashboard/dashboard.component'; import { DashboardConfigComponent } from './dashboardconfig/dashboardconfig.component'; import { DashboardWebpartComponent } from './dashboardconfig/dashboardwebpart.component'; import { DocumentSearchResultComponent } from './document/documentsearchresult.component'; import { EmployeeSearchResultComponent } from './employee/employeesearchresult.component'; import { FileSearchResultComponent } from './fileentity/fileentitysearchresult.component'; import { LinkSearchResultComponent } from './link/linksearchresult.component'; import { NewsSearchResultComponent } from './news/newssearchresult.component'; import { NoteEntitySearchResultComponent } from './noteentity/noteentitysearchresult.component'; import { OrganizationSearchResultComponent } from './organization/organizationsearchresult.component'; import { PersonSearchResultComponent } from './person/personsearchresult.component'; import { ProductSearchResultComponent } from './product/productsearchresult.component'; import { QuestionAnswerSearchResultComponent } from './questionanswer/questionanswersearchresult.component'; import { ReportComponent } from './report/report.component'; import { LocalizeService } from './services/localize.service'; import { SharedModule } from './shared/shared.module'; import { SuggestionPanelComponent } from './suggestionpanel/suggestionpanel.component'; import { EmailTemplateComponent } from './template/emailtemplate.component'; import { TaskTemplateComponent } from './template/tasktemplate.component'; import { TemplateComponent } from './template/template.component'; import { ThemeSearchResultComponent } from './theme/themesearchresult.component'; import { TicketSearchResultComponent } from './ticket/ticketsearchresult.component'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { HttpClientModule } from '@angular/common/http'; import { WallboardModule } from './wallboard/wallboard.module'; import { CommonModule } from '@angular/common'; @NgModule({ declarations: [ AppComponent, LoginComponent, HeaderComponent, DashboardComponent, AppLoaderComponent, ReportComponent, EmailTemplateComponent, AuditLogComponent, SuggestionPanelComponent, DashboardConfigComponent, DashboardWebpartComponent, AboutUsComponent, LinkUnlinkEntityComponent, SearchPopupComponent, FileSearchResultComponent, EmployeeSearchResultComponent, ThemeSearchResultComponent, NoteEntitySearchResultComponent, ProductSearchResultComponent, QuestionAnswerSearchResultComponent, LinkSearchResultComponent, NewsSearchResultComponent, PersonSearchResultComponent, OrganizationSearchResultComponent, DocumentSearchResultComponent, CaseSearchResultComponent, TicketSearchResultComponent, TemplateComponent, TaskTemplateComponent, MessageBoxComponent, ProgressBarComponent ], imports: [ CommonModule, SharedModule, BrowserModule, BrowserAnimationsModule, HttpClientModule, WallboardModule, BsDropdownModule.forRoot(), ModalModule.forRoot(), BsDatepickerModule.forRoot(), ], providers: [ AuthGuard, LocalizeService, { provide: APP_INITIALIZER, useFactory: setupTranslateFactory, deps: [LocalizeService], multi: true }, { provide: TINYMCE_SCRIPT_SRC, useValue: 'tinymce/tinymce.min.js' } ], bootstrap: [AppComponent] }) export class AppModule { } export function setupTranslateFactory( localizeService: LocalizeService): Function { return () => { localizeService.setLanguage().then((language) => { localizeService.useLanguage(language == 'nl-NL' ? 'NL' : 'EN').then(() => { console.log("language set as: " + language); }); if (language == 'nl-NL') defineLocale('nl', nlLocale); }); }; }
package.json
{ "name": "decos-kcs-web", "version": "0.0.0", "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, "private": true, "dependencies": { "@angular/animations": "^16.2.12", "@angular/cdk": "^16.2.14", "@angular/common": "^16.2.12", "@angular/compiler": "^16.2.12", "@angular/core": "^16.2.12", "@angular/forms": "^16.2.12", "@angular/localize": "^16.2.12", "@angular/material": "^16.2.14", "@angular/platform-browser": "^16.2.12", "@angular/platform-browser-dynamic": "^16.2.12", "@angular/router": "^16.2.12", "@tinymce/tinymce-angular": "^6.0.1", "angular-moment": "^1.3.0", "chart.js": "^4.4.3", "jlinq": "^1.0.0", "lz-string": "^1.5.0", "moment": "^2.29.4", "ngx-bootstrap": "^5.1.2", "ngx-chips": "^3.0.0", "rxjs": "^7.8.1", "spin.js": "^4.0.0", "tinymce": "^7.3.0", "tslib": "^2.0.0", "x2js": "^3.3.0", "zone.js": "^0.14.10" }, "devDependencies": { "@angular-devkit/build-angular": "^16.2.14", "@angular/cli": "^16.2.14", "@angular/compiler-cli": "^16.2.12", "@angular/language-service": "^16.2.12", "@types/node": "~20.8.2", "typescript": "4.9.5" } }
问题根源及解决方案
1. 自定义管道注册冗余导致冲突
Angular 16中,自定义管道不需要再添加到providers数组中,仅需在declarations声明并通过exports导出即可。当前PipesModule的providers数组包含所有管道,这在新版本中属于冗余配置,可能引发管道识别异常。
修改方案:移除PipesModule中的providers数组,修改后的代码如下:
import { NgModule } from '@angular/core'; import { TranslatePipe } from './translate.pipe'; import { SafePipe } from './safe.pipe'; import { DashboardSearchPipe, GenderTextPipe, SuggestionSearchPipe, TaskTemplateSearchPipe, TemplateTypePipe, WallboardSearchPipe } from './custome.pipe'; @NgModule({ exports: [ TranslatePipe, SafePipe, TemplateTypePipe, SuggestionSearchPipe, TaskTemplateSearchPipe, DashboardSearchPipe, GenderTextPipe, WallboardSearchPipe ], declarations: [ TranslatePipe, SafePipe, TemplateTypePipe, SuggestionSearchPipe, TaskTemplateSearchPipe, DashboardSearchPipe, GenderTextPipe, WallboardSearchPipe ] }) export class PipesModule { }
2. 特性模块未导入SharedModule
如果使用translate管道的组件属于特性模块(比如WallboardModule),该模块必须单独导入SharedModule才能获取管道的访问权限。检查所有用到管道的组件所在模块,确认是否导入了SharedModule。
3. ngClass绑定缺失CommonModule
ngClass属于Angular通用指令,需要所在模块导入CommonModule才能使用。虽然AppModule导入了CommonModule,但特性模块需单独导入。检查所有使用ngClass的组件所在模块,确认是否添加了CommonModule到imports数组中。
4. 依赖版本不兼容
当前使用的ngx-bootstrap@5.1.2仅支持Angular 12-14,与Angular 16不兼容,这会导致一系列指令(包括ngClass相关的绑定)异常。
升级方案:安装适配Angular 16的ngx-bootstrap版本:
npm install ngx-bootstrap@12.0.0 --save
同时检查ngx-chips@3.0.0是否兼容Angular 16,若不兼容需寻找替代方案或升级到适配版本。
内容的提问来源于stack exchange,提问作者Anonymous

