Angular 13出现NullInjectorError:无ModuleSkillCollectionComponent提供者求助
在Angular 13中生成新组件后出现如下错误,删除该组件后错误仍触发:
Uncaught (in promise): NullInjectorError:
R3InjectorError(AppModule)[HttpService ->
ModuleSkillCollectionComponent -> ModuleSkillCollectionComponent ->
ModuleSkillCollectionComponent]: NullInjectorError: No provider for
ModuleSkillCollectionComponent!
控制台提示指向导航组件代码:
<nav class="navbar max-w-full w-full mx-auto"> <div class="flex-1"> <div class="w-full top-0 border-b"> <ul class="menu menu-horizontal w-screen"> <li class="left-0 top-0 font-bold"><a href="">CompetentieApp</a></li> <li class="mr-10"> <a routerLinkActive="tab-active" href="/faq">Eindniveaus</a> </li> <li class="mr-10"><a href="/faq">Overzicht Alle Modules</a></li> <li class="mr-10"><a href="/faq">Beheer</a></li> <li class="mr-10"> <a routerLinkActive="tab-active" routerLink="moduleCompetenties">Logout</a> </li> <img class="absolute w-14 md:w-36 mr-10 right-0 top-0" src="assets/logo.png" alt="logo" /> </ul> </div> </div> </nav> <router-outlet></router-outlet>
相关配置与代码文件
1. app-routing.ts
const routes: Routes = [ { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: 'moduleCompetenties', component: ModuleSkillCollectionComponent }, { path: '**', redirectTo: 'home', pathMatch: 'full' }, ]; @NgModule({ imports: [ CommonModule, RouterModule.forRoot(routes) ], exports: [RouterModule], }) export class AppRoutingModule {}
2. http.service.ts
@Injectable({ providedIn: 'root' }) export class HttpService { // moduleSkillJson: ModuleSkillJSON[] = []; // private isGetting = false; constructor(private http: HttpClient, private moduleSKillClass: ModuleSkillCollectionComponent) {} post() { this.http.post('http://localhost:8080/add', this.postBody).subscribe(() => { console.log(this.postBody); }); } getSkill(skillJson: Skill[]) { this.http.get<{[key: string] : Skill}>(environment.apiUrl + '/skillCollection') .pipe( map((res) => { // console.log(res); const skillDetailsArray: Skill[] = []; for (const key in res) { if (res.hasOwnProperty(key)) { skillDetailsArray.push({...res[key], id: key}) } } // console.log(skillDetailsArray); return skillDetailsArray; } ) ).subscribe(skills => { // console.log(skills); skillJson = skills; // console.log("SkillDetails has " + this.skillJson.length + " entries."); } ); } getModuleSkill(moduleSkillJson: ModuleSkillJSON[]) { this.http .get<{[key: string] : ModuleSkillJSON}>(environment.apiUrl + "/moduleSkill") .pipe( map((res) => { // console.log(res); const moduleSkill: ModuleSkillJSON[] = []; for (const key in res) { if (res.hasOwnProperty(key)) { moduleSkill.push({...res[key], id: key}) } } console.log(moduleSkill); return moduleSkill; }) ).subscribe(moduleSkills => { moduleSkillJson = moduleSkills; this.moduleSKillClass.entriesLooper(moduleSkillJson); }); // console.log("SkillDetails has " + this.moduleSkillJson.length + " entries."); } }
3. App.module.ts
@NgModule({ declarations: [AppComponent, ModuleSkillCollectionComponent], imports: [ BrowserModule, AppRoutingModule, ReactiveFormsModule, HttpClientModule, ], providers: [HttpService], bootstrap: [AppComponent], }) export class AppModule {}
4. module-skill-collection.component.ts
export class ModuleSkillCollectionComponent implements OnInit { showTable: Boolean = false; loop: boolean = true; cohort: string = '22-23'; moduleSkillJson: ModuleSkillJSON[]; skillJson: Skill[] = []; entries: number[] = []; i: number = 1; constructor(private service: HttpService) {} ngOnInit(): void { this.getModuleSkills(); this.getSkill(); } isActive() { this.showTable = !this.showTable; } counter(i: number) { return new Array(i); } getSkill() { this.service.getSkill(this.skillJson); } getModuleSkills() { this.service.getModuleSkill(this.moduleSkillJson); this.entriesLooper(this.moduleSkillJson); } entriesLooper(moduleSKills: ModuleSkillJSON[]): number[] { for (let modulesSKill of moduleSKills) { this.entries.push(modulesSKill.entry); } return this.entries; } } export interface Skill { id?: string; skillGroupName: string; skillGroupType: string; skillDetailName: string; skillDetailType: string; } export interface ModuleSkillJSON { moduleCode: string; moduleName: string; skill: Array<Skill>[]; entry: number; exist: number; id?: string; }
问题根源
错误核心是**HttpService构造函数中注入了ModuleSkillCollectionComponent组件**。Angular组件不能作为依赖注入到服务中:组件是由Angular在路由加载、模板渲染等场景下创建的实例,并非全局可提供的依赖项,这种组件与服务的循环依赖直接触发了NullInjectorError。
修复步骤
移除服务中对组件的依赖
修改http.service.ts的构造函数,删除组件注入:constructor(private http: HttpClient) {} // 移除private moduleSKillClass: ModuleSkillCollectionComponent重构服务方法,返回Observable而非直接处理
服务只负责数据请求,将数据处理逻辑交回组件,修改getModuleSkill方法:getModuleSkill(): Observable<ModuleSkillJSON[]> { return this.http .get<{[key: string] : ModuleSkillJSON}>(environment.apiUrl + "/moduleSkill") .pipe( map((res) => { const moduleSkill: ModuleSkillJSON[] = []; for (const key in res) { if (res.hasOwnProperty(key)) { moduleSkill.push({...res[key], id: key}) } } console.log(moduleSkill); return moduleSkill; }) ); }调整组件调用逻辑
在组件中订阅服务返回的Observable,自行处理数据:getModuleSkills() { this.service.getModuleSkill().subscribe(moduleSkills => { this.moduleSkillJson = moduleSkills; this.entriesLooper(this.moduleSkillJson); }); }修复
getSkill方法的传值问题
原方法中直接赋值skillJson = skills无法修改组件变量,同样改为返回Observable:// 服务中修改getSkill getSkill(): Observable<Skill[]> { return this.http.get<{[key: string] : Skill}>(environment.apiUrl + '/skillCollection') .pipe( map((res) => { const skillDetailsArray: Skill[] = []; for (const key in res) { if (res.hasOwnProperty(key)) { skillDetailsArray.push({...res[key], id: key}) } } return skillDetailsArray; } ) ); } // 组件中修改调用 getSkill() { this.service.getSkill().subscribe(skills => { this.skillJson = skills; }); }确认配置正确性
确保AppModule的providers数组中没有添加组件(当前代码已符合要求)。
内容的提问来源于stack exchange,提问作者H. Motsu

