Angular 15升级至16后编译失败:AppModuleShared识别异常
Angular 15升级至16后编译失败的解决方案
问题背景
将代码库从Angular 15升级到16后,项目无法编译。已尝试以下操作:
- 执行
ng update更新所有依赖,无待更新项 - 删除
node_modules和package_lock.json,重新执行npm install,安装成功
编译时仍在主模块报错:
Error: src/app/tasks/tasks.module.ts:20:5 - error NG6002: 'AppModuleShared' does not appear to be an NgModule class. AppModuleShared
升级后未修改tasks.module.ts与AppModuleShared代码,报错位置代码片段:
@NgModule({ declarations: [ InboxComponent, TaskStatusPipe ], imports: [ CommonModule, RouterModule.forChild([ { path: 'tasks', component: InboxComponent } ]), AppModuleShared // 报错行 ] }) export class TasksModule { }
AppModuleShared完整代码:
import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { MatLegacyAutocompleteModule as MatAutocompleteModule } from '@angular/material/legacy-autocomplete'; import { MatBadgeModule } from '@angular/material/badge'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button'; import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-card'; import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox'; import { MatNativeDateModule } from '@angular/material/core'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog'; import { MatDividerModule } from '@angular/material/divider'; import { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field'; import { MatGridListModule } from '@angular/material/grid-list'; import { MatIconModule } from '@angular/material/icon'; import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input'; import { MatLegacyListModule as MatListModule } from '@angular/material/legacy-list'; import { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu'; import { MatLegacyPaginatorModule as MatPaginatorModule } from '@angular/material/legacy-paginator'; import { MatLegacyProgressBarModule as MatProgressBarModule } from '@angular/material/legacy-progress-bar'; import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner'; import { MatLegacyRadioModule as MatRadioModule } from '@angular/material/legacy-radio'; import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select'; import { MatSidenavModule } from '@angular/material/sidenav'; import { MatLegacySlideToggleModule as MatSlideToggleModule } from '@angular/material/legacy-slide-toggle'; import { MatLegacySnackBarModule as MatSnackBarModule } from '@angular/material/legacy-snack-bar'; import { MatSortModule } from '@angular/material/sort'; import { MatStepperModule } from '@angular/material/stepper'; import { MatLegacyTableModule as MatTableModule } from '@angular/material/legacy-table'; import { MatLegacyTabsModule as MatTabsModule } from '@angular/material/legacy-tabs'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatLegacyTooltipModule as MatTooltipModule } from '@angular/material/legacy-tooltip'; import {MatExpansionModule} from '@angular/material/expansion'; import { MatTreeModule } from '@angular/material/tree'; import { LayoutModule } from '@angular/cdk/layout'; import { FlexLayoutModule } from '@angular/flex-layout'; import { ProductTileComponent } from './shared/product-tile/product-tile.component'; import { ProductAliasTileComponent } from './shared/product-alias-tile/product-alias-tile.component'; import { DragulaModule } from 'ng2-dragula'; import { NgxsModule } from '@ngxs/store'; import { TourMatMenuModule } from 'ngx-tour-md-menu'; import { HttpClient, HttpClientModule } from '@angular/common/http'; import { NguCarouselModule } from '@ngu/carousel'; import { NgSelectModule } from '@ng-select/ng-select'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatDatetimepickerModule, MatNativeDatetimeModule, MAT_DATETIME_FORMATS } from '@mat-datetimepicker/core'; import { MAT_DATE_FORMATS } from '@angular/material/core'; import { CommonModule } from '@angular/common'; import { NgxMaterialTimepickerModule } from 'ngx-material-timepicker'; import { CmpState } from '../states/CmpState'; import { FavouritesState } from '../states/FavouritesState'; import { UserState } from '../states/UserState'; import { PrefsState } from '../states/PrefsState'; import { OrderForState } from '../states/OrderForState'; import { BasketState } from '../states/BasketState'; import { PreferencesComponent } from './shared/preferences/preferences.component'; import { ProfileComponent } from './shared/profile/profile.component'; import { BasketComponent } from './shared/basket/basket.component'; import { BreadcrumbComponent } from './shared/breadcrumb/breadcrumb.component'; import { HomeSwitchAddressComponent } from './home/home-switch-address/home-switch-address.component'; import { HomeSwitchUserComponent } from './home/home-switch-user/home-switch-user.component'; import { OverlayModule } from '@angular/cdk/overlay'; import { ToastrModule } from 'ngx-toastr'; import { ShoppingListComponent } from './shared/shopping-list/shopping-list.component'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { CustomDateAdapter } from './product/questionnaire/form-elements/date-time/custom-date-adapter'; import { DateAdapter } from '@angular/material/core'; import { PreviousRouteService } from './previous-route.service'; import { RulesService } from 'src/services/Questionnaire/RulesService'; import { QuestionnaireService } from 'src/services/Questionnaire/QuestionnaireService'; import { CategoriesState } from '../states/CategoriesState'; import { LookupService } from 'src/services/Questionnaire/LookupService'; import { HelperService } from 'src/services/Helper/HelperService'; import { ExcelOrdersHelperService } from 'src/services/Helper/ExcelOrdersHelperService'; import { DecodeHtml, EncodeUriPipe, SafePipe } from 'src/pipes'; import { OrderForPaneComponent } from './shared/order-for-pane/order-for-pane.component'; import { OrderReviewState } from 'src/states/order-review.state'; import { GetProfilePicPipe } from 'src/pipes/get-profile-pic.pipe'; import { FormatDatePipe } from 'src/pipes/format-date.pipe'; import { configuration } from 'src/domain'; import { FilterPipe } from 'src/pipes/filter.pipe'; import { LuceneExplanationBottomSheetComponent } from './shared/lucene-explanation-bottom-sheet/lucene-explanation-bottom-sheet.component'; import { SupportTabComponent } from './shared/support-tab/support-tab.component'; import { IconsService } from 'src/services/Icons/IconsService'; import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; import { HelpCentreState } from 'src/states/help-centre.state'; import { OrdersService } from 'src/services'; import { ProductTileNarrowComponent } from './shared/product-tile-narrow/product-tile-narrow.component'; import { InfoModalComponent } from './shared/info-modal/info-modal.component'; import { ApiUserProfileComponent } from './shared/api-user-profile/api-user-profile.component'; import { TemplateGroupComponent } from './shared/template-group/template-group.component'; import { TemplateCreatorComponent } from './shared/template-creator/template-creator.component'; import { CreateTemplateDialogComponent } from './shared/create-template-dialog/create-template-dialog.component'; import { TemplatesState } from 'src/states/TemplatesState'; import { TemplateCreatorWrapperComponent } from './shared/template-creator-wrapper/template-creator-wrapper.component'; import { TemplateFromOrderCreatorComponent } from './shared/template-from-order-creator/template-from-order-creator.component'; import { ReorderState } from 'src/states/ReorderState'; import { CopyTemplateWrapperComponent } from './shared/copy-template-wrapper/copy-template-wrapper-dialog.component'; import { SharedTemplateGroupComponent } from './shared/shared-template-group/shared-template-group.component'; const LOCALE_DATETIMEPICKER_FORMATS = { parse: { dateInput: { day: '2-digit', hour: '2-digit', minute: '2-digit', month: '2-digit', year: 'numeric' } }, display: { dateInput: { year: 'numeric', month: '2-digit', day: '2-digit', customDate: 'true' }, monthInput: { month: 'long' }, datetimeInput: 'input', timeInput: { hour: '2-digit', minute: '2-digit' }, monthYearLabel: { year: 'numeric', month: 'short' }, dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' }, monthYearA11yLabel: { year: 'numeric', month: 'long' }, popupHeaderDateLabel: { weekday: 'short', month: 'short', day: '2-digit' } } }; @NgModule({ declarations: [ ProductTileComponent, ProductAliasTileComponent, ProductTileNarrowComponent, PreferencesComponent, ProfileComponent, ApiUserProfileComponent, DecodeHtml, BasketComponent, SafePipe, EncodeUriPipe, OrderForPaneComponent, GetProfilePicPipe, FormatDatePipe, FilterPipe, LuceneExplanationBottomSheetComponent, SupportTabComponent, TemplateGroupComponent, TemplateCreatorComponent, TemplateCreatorWrapperComponent, CreateTemplateDialogComponent, TemplateFromOrderCreatorComponent, CopyTemplateWrapperComponent, SharedTemplateGroupComponent ], imports: [ NgxsModule.forRoot([ CmpState, OrderReviewState, TemplatesState, ReorderState, ], {selectorOptions: { suppressErrors: false }, developmentMode: false}), CommonModule, MatSlideToggleModule, ToastrModule.forRoot(), FormsModule, OverlayModule, ReactiveFormsModule, MatToolbarModule, MatFormFieldModule, MatIconModule, MatInputModule, LayoutModule, DragulaModule.forRoot(), TourMatMenuModule.forRoot(), HttpClientModule, NguCarouselModule, MatMenuModule, MatProgressBarModule, MatAutocompleteModule, MatSortModule, RouterModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: function (httpClient: HttpClient) { return new TranslateHttpLoader(httpClient, '/assets/i18n/', `-lang.json?version=${configuration.version}`); }, deps: [HttpClient] } }) ], exports: [ CommonModule, FormsModule, MatSortModule, TranslateModule, RouterModule, BreadcrumbComponent, SafePipe, EncodeUriPipe, OrderForPaneComponent, GetProfilePicPipe, FormatDatePipe, FilterPipe, SharedTemplateGroupComponent ], providers: [ QuestionnaireState, IconsService, LookupService, MatNativeDateModule, CustomDateAdapter, PreviousRouteService, { provide: DateAdapter, useClass: CustomDateAdapter }, { provide: MAT_DATETIME_FORMATS, useValue: LOCALE_DATETIMEPICKER_FORMATS }, { provide: MAT_DATE_FORMATS, useValue: LOCALE_DATETIMEPICKER_FORMATS } ] }) export class AppModuleShared { }
问题原因
Angular 16对NgModule的导入规则做了严格校验:根模块(包含forRoot()初始化逻辑的模块)不能被特性模块导入。AppModuleShared中包含NgxsModule.forRoot()、TranslateModule.forRoot()等根级配置,属于根模块范畴,被TasksModule导入后触发编译错误。
修复步骤
1. 拆分共享模块
创建独立的SharedModule,提取AppModuleShared中可复用的组件、管道、通用模块导入:
// src/app/shared/shared.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; // 导入需要共享的Material模块 import { MatSortModule } from '@angular/material/sort'; import { MatSlideToggleModule } from '@angular/material/legacy-slide-toggle'; // 导入需要共享的组件、管道 import { BreadcrumbComponent } from '../shared/breadcrumb/breadcrumb.component'; import { SafePipe } from '../../pipes'; import { EncodeUriPipe } from '../../pipes/encode-uri.pipe'; import { OrderForPaneComponent } from '../shared/order-for-pane/order-for-pane.component'; import { GetProfilePicPipe } from '../../pipes/get-profile-pic.pipe'; import { FormatDatePipe } from '../../pipes/format-date.pipe'; import { FilterPipe } from '../../pipes/filter.pipe'; import { SharedTemplateGroupComponent } from '../shared/shared-template-group/shared-template-group.component'; @NgModule({ declarations: [ BreadcrumbComponent, SafePipe, EncodeUriPipe, OrderForPaneComponent, GetProfilePicPipe, FormatDatePipe, FilterPipe, SharedTemplateGroupComponent ], imports: [ CommonModule, FormsModule, ReactiveFormsModule, RouterModule, TranslateModule, MatSortModule, MatSlideToggleModule ], exports: [ CommonModule, FormsModule, ReactiveFormsModule, RouterModule, TranslateModule, MatSortModule, MatSlideToggleModule, BreadcrumbComponent, SafePipe, EncodeUriPipe, OrderForPaneComponent, GetProfilePicPipe, FormatDatePipe, FilterPipe, SharedTemplateGroupComponent ] }) export class SharedModule { }
2. 调整根模块AppModuleShared
保留根级初始化逻辑,导入新的SharedModule,移除已拆分到共享模块的内容:
// src/app/app-module-shared.ts @NgModule({ declarations: [ ProductTileComponent, ProductAliasTileComponent, ProductTileNarrowComponent, PreferencesComponent, ProfileComponent, ApiUserProfileComponent, DecodeHtml, BasketComponent, LuceneExplanationBottomSheetComponent, SupportTabComponent, TemplateGroupComponent, TemplateCreatorComponent, TemplateCreatorWrapperComponent, CreateTemplateDialogComponent, TemplateFromOrderCreatorComponent, CopyTemplateWrapperComponent, HomeSwitchAddressComponent, HomeSwitchUserComponent, ShoppingListComponent, InfoModalComponent ], imports: [ NgxsModule.forRoot([ CmpState, FavouritesState, UserState, PrefsState, OrderForState, BasketState, CategoriesState, OrderReviewState, TemplatesState, ReorderState, HelpCentreState ], {selectorOptions: { suppressErrors: false }, developmentMode: false}), SharedModule, ToastrModule.forRoot(), OverlayModule, MatToolbarModule, MatFormFieldModule, MatIconModule, MatInputModule, LayoutModule, DragulaModule.forRoot(), TourMatMenuModule.forRoot(), HttpClientModule, NguCarouselModule, MatMenuModule, MatProgressBarModule, MatAutocompleteModule, MatBottomSheetModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: function (httpClient: HttpClient) { return new TranslateHttpLoader(httpClient, '/assets/i18n/', `-lang.json?version=${configuration.version}`); }, deps: [HttpClient] } }) ], providers: [ QuestionnaireState, IconsService, LookupService, CustomDateAdapter, PreviousRouteService, RulesService, QuestionnaireService, HelperService, ExcelOrdersHelperService, OrdersService, { provide: DateAdapter, useClass: CustomDateAdapter }, { provide: MAT_DATETIME_FORMATS, useValue: LOCALE_DATETIMEPICKER_FORMATS }, { provide: MAT_DATE_FORMATS, useValue: LOCALE_DATETIMEPICKER
相关产品推荐
相关产品推荐

