angular: defer loading a component with NgModule with providers throws error

Which @angular/* package(s) are the source of the bug?

core

Is this a regression?

Yes

Description

When defer loading a component with NgModule with providers throws error:

dashboard-stats.component.ts:76 ERROR NullInjectorError: R3InjectorError[_InjectionService -> _InjectionService]: 
  NullInjectorError: No provider for _InjectionService!
    at NullInjector.get (core.mjs:5601:27)
    at R3Injector.get (core.mjs:6044:33)
    at R3Injector.get (core.mjs:6044:33)
    at ChainedInjector.get (core.mjs:15407:36)
    at lookupTokenUsingModuleInjector (core.mjs:4112:39)
    at getOrCreateInjectable (core.mjs:4160:12)
    at ɵɵdirectiveInject (core.mjs:11970:19)
    at ɵɵinject (core.mjs:917:42)
    at NodeInjectorFactory.TooltipService_Factory [as factory] (swimlane-ngx-charts.mjs:4152:39)
    at getNodeInjectable (core.mjs:4366:44)

Whereas lazyloading same component with import works just fine.

Parent component:

import { Component, OnInit } from '@angular/core';
import { MediaService } from "./../../shared/services/media.service";
import { EmployerDashboardStatsComponent } from './dashboard-stats.component';

@Component({
  templateUrl: './account.component.html',
  standalone: true,
  imports: [
    DashboardStatsComponent
  ]
})
export default class AccountComponent implements OnInit {

  constructor(
    public media: MediaService
  ) { }

  ngOnInit() {

  }


}

Parent component template

@defer (when media.isGTXS) {
<dashboard-stats></dashboard-stats>
}

Child Component


import { HttpClient } from '@angular/common/http';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { LegendPosition, NgxChartsModule, ScaleType } from '@swimlane/ngx-charts';
import * as shape from 'd3-shape';

@Component({
  selector: 'dashboard-stats',
  templateUrl: './dashboard-stats.component.html',
  standalone: true,
  imports: [
    NgxChartsModule
  ],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class DashboardStatsComponent implements OnInit {

  //charts data
  public jobResponses: any[] = [];
  public experienceLevels: any[] = [];
  public recruitmentActivity = [];

  // options
  public view: [number, number] = [360, 220];
  public showXAxis = true;
  public showYAxis = true;
  public gradient = false;
  public showLegend = false;
  public legendPosition = LegendPosition.Below;
  public showXAxisLabel = true;
  public showYAxisLabel = true;
  public colorScheme = "vivid";

  // pie
  public showLabels = false;
  public explodeSlices = false;
  public doughnut = false;
  public pieChartColorScheme = {
    name: 'vivid',
    selectable: true,
    group: ScaleType.Ordinal,
    domain: ['#e1f5fe', '#b3e5fc', '#81d4fa', '#4fc3f7', '#29b6f6', '#03a9f4', '#039be5', '#0288d1', '#0277bd', '#01579b']
  };

  //line
  public lineChartLineInterpolation = shape.curveBasis;

  constructor(
    private http: HttpClient,
    private cdr: ChangeDetectorRef
  ) {
  }

  ngOnInit() {

  }

  axisFormat(val) {
    if (val % 1 === 0) {
      return val.toLocaleString();
    } else {
      return '';
    }
  }
}

Dynamically loading component works perfectly:

 //lazylod dashboard-stats component
 import('./dashboard-stats.component').then((c) => {
   const refStats = this.viewContainerStatsContainer.createComponent(c.EmployerDashboardStatsComponent);
 });

Please provide a link to a minimal reproduction of the bug

No response

Please provide the exception or error you saw

dashboard-stats.component.ts:76 ERROR NullInjectorError: R3InjectorError[_InjectionService -> _InjectionService]: 
  NullInjectorError: No provider for _InjectionService!
    at NullInjector.get (core.mjs:5601:27)
    at R3Injector.get (core.mjs:6044:33)
    at R3Injector.get (core.mjs:6044:33)
    at ChainedInjector.get (core.mjs:15407:36)
    at lookupTokenUsingModuleInjector (core.mjs:4112:39)
    at getOrCreateInjectable (core.mjs:4160:12)
    at ɵɵdirectiveInject (core.mjs:11970:19)
    at ɵɵinject (core.mjs:917:42)
    at NodeInjectorFactory.TooltipService_Factory [as factory] (swimlane-ngx-charts.mjs:4152:39)
    at getNodeInjectable (core.mjs:4366:44)


### Please provide the environment you discovered this bug in (run `ng version`)

```true
Angular CLI: 17.0.0
Node: 18.18.2
Package Manager: npm 9.8.1
OS: win32 x64

Angular: 17.0.2
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, platform-server
... router, service-worker

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1700.0
@angular-devkit/build-angular   17.0.0
@angular-devkit/core            17.0.0
@angular-devkit/schematics      17.0.0
@angular/cdk                    17.0.0
@angular/cli                    17.0.0
@angular/fire                   16.0.0
@angular/google-maps            17.0.0
@angular/material               17.0.0
@angular/pwa                    17.0.0
@schematics/angular             17.0.0
rxjs                            7.8.1
typescript                      5.2.2
zone.js                         0.14.0

Anything else?

No response

About this issue

  • Original URL
  • State: closed
  • Created 8 months ago
  • Reactions: 3
  • Comments: 19 (14 by maintainers)

Commits related to this issue

Most upvoted comments

@naveedahmed1 thanks for additional information. I’ve reproduced the problem and will work on a fix. Will keep this thread updated.

Thank you so much @AndrewKushnir 😃

FYI, this issue should be fixed by https://github.com/angular/angular/commit/57123524a2e1481987eaf239d2ae7f1216291864, the fix will be released as a part of v17.2.3 next week. Please reopen (or create a new ticket) if the problem still exists after updating to v17.2.3.

@kkachniarz220 Please have a look at this comment:

One thing that should be noted is that providing a service on a NgModule level is roughly equivalent of providing a service via @Injectable(providedIn: 'root'). In other words - providers are not scoped to a NgModule (this is a common misconception I see) but rather all providers are folded into the root injector (!). So you could just register the desired pipe on the root / when bootstrapping an application - this would be equivalent to registering it on a NgModule, really.

Then, pipes are meant to be used as a formatting facility in a template, not as generic providers. We never intended pipes to act as providers. In the particular case of the DatePipe you would be better off reusing the formatDate function directly.

I’m going to close this one as I don’t think we want to introduce providers on a pipe level. The NgModule equivalent behaviour can be achieved today without NgModules and an alternative (arguably better) solution exists for the DatePipe

will this fix be a part of v17.0.4?

@naveedahmed1 no, the fix will be released later, since it needs a bit more research. As a possible temporary solution, you can try using the importProvidersFrom([NgxChartsModule]) in your application providers at bootstrap time or at a particular route level.

I’ve got a smaller repro : https://stackblitz.com/edit/angular-or9ksy

Here the injection is broken when the MyModule import is in the defered component, but it works fine if it is in the AppComponent