angular: Eager providers and Injector.get() can cause errors

I’m submitting a … (check one with “x”)

[x] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

After updating from 4.0.0-rc.2 to 4.0.0 Router not injected as expected

Expected behavior

Should be injected as expected

Minimal reproduction of the problem with instructions

Plunker: http://plnkr.co/edit/ogCHjeYuCDM8vNLdwAFx?p=preview

Update from 4.0.0-rc.2 to 4.0.0

ConfigService provided in AppModule as APP_INITIALIZER

  providers: [
    ConfigService,
    {
      provide: APP_INITIALIZER,
      useFactory: configServiceFactory,
      deps: [ConfigService],
      multi: true
    }
  ],

ApiService injected manually from ConfigService

@Injectable()
export class ConfigService {

  private api: ApiService;

  public constructor(
    private injector: Injector
  ) {

    // Avoid cyclid dependencies, inject manually:
    this.api = injector.get(ApiService);
  }

router is undefined when injected in ApiService

import { Http, Headers, RequestOptionsArgs, Response } from '@angular/http';
import { Router } from '@angular/router';

@Injectable()
export class ApiService {

  constructor(
    private router: Router,
    private http: Http
  ) {

    console.log(router, 'router'); // undefined
    debugger;

What is the motivation / use case for changing the behavior?

Please tell us about your environment:

@angular/cli: 1.0.0 node: 7.4.0 os: win32 x64 @angular/animations: 4.0.0 @angular/common: 4.0.0 @angular/compiler: 4.0.0 @angular/compiler-cli: 4.0.0 @angular/core: 4.0.0 @angular/forms: 4.0.0 @angular/http: 4.0.0 @angular/platform-browser: 4.0.0 @angular/platform-browser-dynamic: 4.0.0 @angular/platform-server: 4.0.0 @angular/router: 4.0.0 @angular/material: 2.0.0-beta.2 @angular/flex-layout: 2.0.0-rc.1 @angular/cli: 1.0.0 @ngtools/webpack: 1.3.0

  • Angular version: 4.0.0
  • Browser: all

  • Language: TypeScript 2.2.1

  • Node (for AoT issues): node --version = 7.4.0

The log given by the failure.

core.es5.js:1085 ERROR TypeError: Cannot read property 'url' of undefined
    at CatchSubscriber.selector (api.service.ts:127)
    at CatchSubscriber.error (catch.js:104)
    at MapSubscriber.Subscriber._error (Subscriber.js:128)
    at MapSubscriber.Subscriber.error (Subscriber.js:102)
    at XMLHttpRequest.onLoad (http.es5.js:1210)
    at ZoneDelegate.invokeTask (zone.js:363)
    at Object.onInvokeTask (core.es5.js:4136)
    at ZoneDelegate.invokeTask (zone.js:362)
    at Zone.runTask (zone.js:166)
    at XMLHttpRequest.ZoneTask.invoke (zone.js:416)
defaultErrorLogger @ core.es5.js:1085
ErrorHandler.handleError @ core.es5.js:1145
next @ core.es5.js:4774
schedulerFn @ core.es5.js:3848
SafeSubscriber.__tryOrUnsub @ Subscriber.js:234
SafeSubscriber.next @ Subscriber.js:183
Subscriber._next @ Subscriber.js:125
Subscriber.next @ Subscriber.js:89
Subject.next @ Subject.js:55
EventEmitter.emit @ core.es5.js:3834
NgZone.triggerError @ core.es5.js:4205
onHandleError @ core.es5.js:4166
ZoneDelegate.handleError @ zone.js:334
Zone.runTask @ zone.js:169
ZoneTask.invoke @ zone.js:416
Navigated to http://localhost:4200/
core.es5.js:3053Template parse warnings:
The <template> element is deprecated. Use <ng-template> instead ("[WARNING ->]<template><div class="mat-autocomplete-panel" role="listbox" [id]="id" [ngClass]="_getClassList()" #p"): ng:///MdAutocompleteModule/MdAutocomplete.html@0:0
Console.warn @ core.es5.js:3053
TemplateParser.parse @ compiler.es5.js:11508
JitCompiler._compileTemplate @ compiler.es5.js:25195
(anonymous) @ compiler.es5.js:25119
JitCompiler._compileComponents @ compiler.es5.js:25119
createResult @ compiler.es5.js:25004
ZoneDelegate.invoke @ zone.js:330
Zone.run @ zone.js:126
(anonymous) @ zone.js:679
ZoneDelegate.invokeTask @ zone.js:363
Zone.runTask @ zone.js:166
drainMicroTaskQueue @ zone.js:529
api.service.ts:31 undefined "router"

2017-03-26 15_39_04-mtcrm

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 7
  • Comments: 18 (3 by maintainers)

Commits related to this issue

Most upvoted comments

Any update ???

When would this bug be fixed? I’m still facing the issue. Please notify

I use this :

private _userService: UserService;
private _router: Router;

constructor(private injector: Injector) {
    //Work
    setTimeout(() => this._userService = injector.get(UserService));
    setTimeout(() => this._router = injector.get(Router));
    //Don't work
    //this._userService = this.injector.get(UserService);
    // this._router = this.injector.get(Router);

I was blocked also but was able to work around this issue with AOT by following https://github.com/angular/angular-cli/issues/5762#issuecomment-295670906. Injecting the injector into my service used with APP_INITIALIZER and waiting for it to be initialized.

import { LOCATION_INITIALIZED } from ‘@angular/common’; import { Injectable, Injector } from ‘@angular/core’; import { Http } from ‘@angular/http’;

// Services import { AnalyticsService } from ‘./services/analytics.service’; import { SettingsService } from ‘./services/settings.service’;

@Injectable() export class AppConfigService { private isInit = false;

/**
 * Creates an instance of AppConfigService.
 * @param {Injector} injector - angular injector
 * @param {SettingsService} settingsService - service used to load config from server
 * @param {Http} http - angular http
 * @param {AnalyticsService} analyticsService - Injected here to instantiate only
 *
 * @memberOf AppConfigService
 */
constructor(
	private injector: Injector,
	private settingsService: SettingsService,
	private http: Http,
	private analyticsService: AnalyticsService) {}

// Initializes the settings service with data from server as app starts
load(): boolean|Promise<boolean> {
	const p: Promise<any> = this.injector.get(LOCATION_INITIALIZED, Promise.resolve(null));
	return p.then(() => {
		if (!this.isInit) {
			return this.settingsService.init();
		} else {
			return true;
		}
	});
}

}