When importing Module to standalone: NullInjectorError: No provider for InjectionToken Icons Token!
Good evening,
I get an NullInjectorError: No provider for InjectionToken Icons Token!when I try to use a custom ShellModule that uses NgIconModule. ShellModule is module-based, whereas the consuming AppComponent is standalone. Icons Token is normally provided in NgIconsModule. However, because the consuming component is standalone, it can neither import nor provide NgIconsModule. "Importing just NgIcon" does not help either.
To Reproduce Steps to reproduce the behavior:
- Create a custom Angular 17 library
my-ui-libthat usesNgIconsModuleand that is module-based
import { NgIconsModule } from '@ng-icons/core';
@NgModule({
imports: [
...
NgIconsModule,
ShellComponent
],
exports: [ShellComponent]
})
export class ShellModule {}
- create a standalone app with Angular 17 as standalone and import the
ShellModule
import { Component, NgModule } from '@angular/core';
import { ShellModule } from '@my-ui-lib/ui-controls';
import { NgIcon, NgIconComponent, NgIconsModule, provideIcons } from '@ng-icons/core';
@Component({
standalone: true,
imports: [ButtonModule, ShellModule],
template: '<my-shell></my-shell>'
})
Expected behavior Run as is, or at least run without errors after importing NgIconsComponent which is the new standard for ngIcons in standalone components
Browser (please complete the following information):
- macOS: 13.6.6
- Chrome: 123.0.6312.107
Packages (please complete the following information):
- @ng-icons/core: 27.2.0 (also tried v25)
- @nx/angular: 18.2.2
Workaround (rather unacceptable)
To import the NgIconsModule despite using a standalone parent, one can define an NgModule that imports NgIconsModule and then import it in the standalone parent:
import { Component, NgModule } from '@angular/core';
import { ShellModule } from '@my-ui-lib/ui-controls';
import { NgIcon, NgIconComponent, NgIconsModule, provideIcons } from '@ng-icons/core';
@NgModule({
imports: [ CommonModule, NgIconsModule.withIcons({tablerCompass, tablerIndentDecrease, tablerIndentIncrease})], exports: [NgIconsModule]
})
class UglyWrapperModule{
}
@Component({
standalone: true,
imports: [ButtonModule, ShellModule, UglyWrapperModule],
template: '<my-shell></my-shell>'
})
⚠️ Please don't use this workaround as it goes completely against the standalone concept. However, this example demonstrates that the root cause of the issue is that
NgIconsMouduleis incompatible withNgIconsComponentas soon as it is wrapped in an old fashion custom NgModule.
Any help is much appreciated. Let me know if you need more details.
Hi, thanks for the report!
Yes, so this is a bit of a quirk with the injector that is particularly noticeable when mixing the two approaches as the hierarchy works a bit differently in MgModule components and standalone components - I should perhaps add some documentation detailing this.
The best option is just use provideIcons in any components that needs icons and import the NgIcon component there. This also has the nice benefit of being more efficient when lazy loading components as the icons get loaded with the component.
@Component({
standalone: true,
imports: [ButtonModule, ShellModule, NgIcon],
template: '<ng-icon name="tablerCompass" />',
viewProviders: [provideIcons({ tablerCompass })]
})
export class ShellComponent {}
If you still have issues let me know and I can look into it further - and if so if you are able to provide a StackBlitz/CodeSandbox reproducing the issue that would be very helpful. Thanks
I'm going to close this issue for now - if you have any further queries or issues let me know and I'll be happy to help. Thanks
@ashley-hunter , thanks for your fast response.
I already tried to import NgIcon and then using viewProviders. Sadly, this did not change anything. I ended up converting the custom ui-Library to standalone. This was a lot of work but made everything work. However, this solution is not feasible for developers handling large libraries or where they do not have control over them.
I'm sorry that I cannot provide a StackBlitz example since there is no sharable version of my code and constructing the example would take more time than I could justify to invest.
Thanks for your insights though.