angular: Error: No provider for InjectionToken mat-autocomplete-scroll-strategy
I upgraded my project from Angular 8 to 9 and refactored my code to take advantage of the ability to lazy load components. For most of my components this works great, but I have one that is throwing an error and I need help resolving the issue.
Here is my component code followed by the error. The module is defined in the same file as the component.
import { Component, OnInit, OnDestroy, NgModule } from '@angular/core';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { switchMap, debounceTime, tap, filter } from 'rxjs/operators';
import { MatFormFieldModule, MatInputModule, MatAutocompleteModule, MatButtonModule, MatProgressSpinnerModule, MatIconModule, MatDialogModule } from '@angular/material';
import { SearchService } from './search.service';
import { SuggestResult } from './suggest-result';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { SharedModule } from 'src/app/shared/shared.module';
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss'],
providers: [ FormBuilder]
})
export class SearchComponent implements OnInit, OnDestroy {
//code removed
}
@NgModule({
imports: [
CommonModule,
HttpClientModule,
FormsModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatAutocompleteModule,
MatButtonModule,
MatProgressSpinnerModule,
MatIconModule,
SharedModule,
MatDialogModule
],
declarations: [SearchComponent]
})
export class SearchModule { }
<div>
<div class="form-container">
<form [formGroup]='resultsForm'>
<mat-form-field [class.mat-form-field-invalid]="!formFieldValid">
<input matInput placeholder="Search term" type="search" [matAutocomplete]="SearchAuto" formControlName='SearchUserInput'>
</mat-form-field>
<div *ngIf="!formFieldValid" class="error-msg"> {{ errorMessage }} </div>
<mat-autocomplete #SearchAuto="matAutocomplete" [displayWith]="getSelectedText"
(optionSelected)='onItemSelected($event.option.value)'>
<mat-option *ngIf="isLoading" class="is-loading">
<mat-spinner mode="indeterminate" diameter="25"></mat-spinner>
</mat-option>
<ng-container *ngIf="!isLoading">
<mat-option *ngFor="let result of filteredResults" [value]="result">
<span>{{ result.name }}</span>
</mat-option>
</ng-container>
</mat-autocomplete>
</form>
<mat-icon>search</mat-icon>
<button mat-button *ngIf="isNotEmpty()" matSuffix mat-icon-button aria-label="Clear" (click)="clearSearch()">
<mat-icon>close</mat-icon>
</button>
</div>
</div>
The error:
null: Error: Uncaught (in promise): NullInjectorError: R3InjectorError(AppModule)[InjectionToken mat-autocomplete-scroll-strategy -> InjectionToken mat-autocomplete-scroll-strategy -> InjectionToken mat-autocomplete-scroll-strategy]: NullInjectorError: No provider for InjectionToken mat-autocomplete-scroll-strategy! ./node_modules/@angular/core/ivy_ngcc/fesm5/core.js/NullInjector.prototype.get@http://localhost:4200/vendor.js:37066:25 ./node_modules/@angular/core/ivy_ngcc/fesm5/core.js/R3Injector.prototype.get@http://localhost:4200/vendor.js:47422:33 ./node_modules/@angular/core/ivy_ngcc/fesm5/core.js/R3Injector.prototype.get@http://localhost:4200/vendor.js:47422:33 ./node_modules/@angular/core/ivy_ngcc/fesm5/core.js/R3Injector.prototype.get@http://localhost:4200/vendor.js:47422:33 ./node_modules/@angular/core/ivy_ngcc/fesm5/core.js/NgModuleRef$1.prototype.get@http://localhost:4200/vendor.js:60382:33 get@http://localhost:4200/vendor.js:58703:35 getOrCreateInjectable@http://localhost:4200/vendor.js:39763:39 ɵɵdirectiveInject@http://localhost:4200/vendor.js:50290:12 MatAutocompleteTrigger_Factory@http://localhost:4200/vendor.js:76965:788 getNodeInjectable@http://localhost:4200/vendor.js:39871:44 searchTokensOnInjector@http://localhost:4200/vendor.js:39807:16 getOrCreateInjectable@http://localhost:4200/vendor.js:39729:58 ɵɵdirectiveInject@http://localhost:4200/vendor.js:50290:12 ɵɵinject@http://localhost:4200/vendor.js:36956:57 factory@http://localhost:4200/vendor.js:47681:44 multiResolve@http://localhost:4200/vendor.js:55407:21 multiProvidersFactoryResolver@http://localhost:4200/vendor.js:55372:12 getNodeInjectable@http://localhost:4200/vendor.js:39871:44 searchTokensOnInjector@http://localhost:4200/vendor.js:39807:16 getOrCreateInjectable@http://localhost:4200/vendor.js:39729:58 ɵɵdirectiveInject@http://localhost:4200/vendor.js:50290:12 FormControlName_Factory@http://localhost:4200/vendor.js:74579:400 getNodeInjectable@http://localhost:4200/vendor.js:39871:44 searchTokensOnInjector@http://localhost:4200/vendor.js:39807:16 getOrCreateInjectable@http://localhost:4200/vendor.js:39729:58 ɵɵdirectiveInject@http://localhost:4200/vendor.js:50290:12 ɵɵinject@http://localhost:4200/vendor.js:36956:57 factory@http://localhost:4200/vendor.js:47681:44 getNodeInjectable@http://localhost:4200/vendor.js:39871:44 searchTokensOnInjector@http://localhost:4200/vendor.js:39807:16 getOrCreateInjectable@http://localhost:4200/vendor.js:39729:58 ɵɵdirectiveInject@http://localhost:4200/vendor.js:50290:12 MatInput_Factory@http://localhost:4200/vendor.js:99837:370 getNodeInjectable@http://localhost:4200/vendor.js:39871:44 instantiateAllDirectives@http://localhost:4200/vendor.js:44362:42 createDirectivesInstances@http://localhost:4200/vendor.js:43753:29 ɵɵelementStart@http://localhost:4200/vendor.js:50436:34 ɵɵelement@http://localhost:4200/vendor.js:50487:19 SearchComponent_Template@http://localhost:4200/search-search-search-component.js:190:56 executeTemplate@http://localhost:4200/vendor.js:43726:19 renderView@http://localhost:4200/vendor.js:43551:28 renderComponent@http://localhost:4200/vendor.js:44741:15 renderChildComponents@http://localhost:4200/vendor.js:43412:24 renderView@http://localhost:4200/vendor.js:43576:34 ./node_modules/@angular/core/ivy_ngcc/fesm5/core.js/ComponentFactory.prototype.create@http://localhost:4200/vendor.js:58804:23 createContainerRef/R3ViewContainerRef</ViewContainerRef.prototype.createComponent@http://localhost:4200/vendor.js:46481:53 ./src/app/dynamic-loader/dynamic-loader.service.ts/DynamicLoaderService.prototype.addComponentToContainer/obs<@http://localhost:4200/main.js:583:23 ./node_modules/zone.js/dist/zone.js/</Zone$1</ZoneDelegate</ZoneDelegate.prototype.invoke@http://localhost:4200/polyfills.js:2740:30 onInvoke@http://localhost:4200/vendor.js:63629:33 ./node_modules/zone.js/dist/zone.js/</Zone$1</ZoneDelegate</ZoneDelegate.prototype.invoke@http://localhost:4200/polyfills.js:2739:36 ./node_modules/zone.js/dist/zone.js/</Zone$1</Zone</Zone.prototype.run@http://localhost:4200/polyfills.js:2497:47 scheduleResolveOrReject/<@http://localhost:4200/polyfills.js:3238:38 ./node_modules/zone.js/dist/zone.js/</Zone$1</ZoneDelegate</ZoneDelegate.prototype.invokeTask@http://localhost:4200/polyfills.js:2775:35 onInvokeTask@http://localhost:4200/vendor.js:63617:33 ./node_modules/zone.js/dist/zone.js/</Zone$1</ZoneDelegate</ZoneDelegate.prototype.invokeTask@http://localhost:4200/polyfills.js:2774:40 ./node_modules/zone.js/dist/zone.js/</Zone$1</Zone</Zone.prototype.runTask@http://localhost:4200/polyfills.js:2542:51 drainMicroTaskQueue@http://localhost:4200/polyfills.js:2955:39
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 17
- Comments: 25 (6 by maintainers)
It does not look like there’s a bug here, but there is a catch and ergonomic issue to this situation.
The root of the problem is that the
SearchModulethat importsMatAutocompleteModuleis never instantiated, as it’s not imported fromAppModule. This results in none of the (transitively) provided providers being available.So as a solution, we need to ensure that the
SearchModuleis used when creating theSearchComponent. To achieve this, you’d currently need to inject theCompilerand use itscompileModuleSyncmethod to obtain anNgModuleFactoryfor the importedSearchModuleclass.Importing the compiler seems counter-intuitive, as we don’t need JIT compilation here. Fortunately in Ivy, this comes at no additional costs: there is no need to use
platformBrowserDynamicto pull in the whole compiler. This approach is actually exactly what theRouteruses under the covers for itsloadChildrensupport.Given the
NgModuleFactory, we can use itscomponentFactoryResolverto be able to create the component factory forSearchComponentin the context of theSearchModuleinstead ofAppModule. Therefore, the injectedComponentFactoryResolveris no longer needed, as that is bound toAppModule.The ergonomics are not ideal here, as all APIs have been designed around the “factory” concept that was needed with ViewEngine (the renderer before Ivy).
I’m keeping this issue open for tracking this use-case, but I’m adjusting the severity label as you mentioned you started to take advantage of lazy loading which introduced the issue.
EDIT: Please bear in mind that the example above will create a new instance of
SearchModuleeach time, which may not be desirable. Consider caching thengModulesomewhere (or even justfactoryin this case) but keep in mind that this instance is specific to a parentInjector, which isthis.container.injectorin this case.I am also having this exact issue when having a lazy loaded module with MatAutocomplete
The fix was the same as before, to import MatTooltipModule in the App-level module, and not inside a sub-component.Is there a way to avoid importing it into the AppModule but to the lazy loaded modules where needed?I was struggling with the same issue. providing
MAT_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY_PROVIDERandMAT_SELECT_SCROLL_STRATEGY_PROVIDERin the component level solved it for me.MatTooltipModulealso throwsERROR Error: NullInjectorError: No provider for InjectionToken mat-tooltip-scroll-strategy!The fix was the same as before, to import
MatTooltipModulein the App-level module, and not inside a sub-component. The isolated case in StackBlitz works fine though https://stackblitz.com/edit/angular-nw6rvl@schinks if you’ve got troubles using stackblitz, you could try https://ng-run.com/. GitHub repo is more problematic (takes us more time to understand what is going on) but would be OK if you can’t reproduce it otherwise.
In any case please try to keep the reproduce scenario to the absolute minimum…