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
SearchModule
that importsMatAutocompleteModule
is 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
SearchModule
is used when creating theSearchComponent
. To achieve this, you’d currently need to inject theCompiler
and use itscompileModuleSync
method to obtain anNgModuleFactory
for the importedSearchModule
class.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
platformBrowserDynamic
to pull in the whole compiler. This approach is actually exactly what theRouter
uses under the covers for itsloadChildren
support.Given the
NgModuleFactory
, we can use itscomponentFactoryResolver
to be able to create the component factory forSearchComponent
in the context of theSearchModule
instead ofAppModule
. Therefore, the injectedComponentFactoryResolver
is 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
SearchModule
each time, which may not be desirable. Consider caching thengModule
somewhere (or even justfactory
in this case) but keep in mind that this instance is specific to a parentInjector
, which isthis.container.injector
in 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_PROVIDER
andMAT_SELECT_SCROLL_STRATEGY_PROVIDER
in the component level solved it for me.MatTooltipModule
also throwsERROR Error: NullInjectorError: No provider for InjectionToken mat-tooltip-scroll-strategy!
The fix was the same as before, to import
MatTooltipModule
in 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…