I am using a custom Http provider to handle API authentication error. In my CustomHttp, I need to redirect the user to the login page when a 401 status error is emitted by the API. That works fine!
app.module.ts
export function loadCustomHttp(backend: XHRBackend, defaultOptions: AppRequestOptions,
router: Router, dataHelper: DataHelperService) {
return new CustomHttp(backend, defaultOptions, router, dataHelper);
}
@NgModule({
// some declarations, imports, ...
providers: [
// some services ...
{
provide: Http,
useFactory: loadCustomHttp,
deps: [XHRBackend, RequestOptions, Router, DataHelperService]
}
});
custom-http.ts
import { Injectable } from '@angular/core';
import { Http, RequestOptions, RequestOptionsArgs, ConnectionBackend, Request, Response } from '@angular/http';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { DataHelperService } from '../helpers/data-helper.service';
import { AuthStorage } from '../services/auth/auth-storage';
import 'rxjs/add/observable/throw';
import 'rxjs/add/observable/empty';
@Injectable()
export class CustomHttp extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions,
private router: Router, private dataHelper: DataHelperService) {
super(backend, defaultOptions);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
return this.intercept(super.request(url, options));
}
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
return this.intercept(super.get(url, options));
}
post(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
return this.intercept(super.post(url, body, options));
}
put(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
return this.intercept(super.put(url, body, options));
}
delete(url: string, options?: RequestOptionsArgs): Observable<Response> {
return this.intercept(super.delete(url, options));
}
intercept(observable: Observable<Response>): Observable<Response> {
return observable.catch((err, source) => {
let token = AuthStorage.getToken();
if (err.status === 401 && token && AuthStorage.isTokenExpired()) {
// token has expired -> redirecting user to login
AuthStorage.clearAll();
this.router.navigate(['auth/login']);
}
return Observable.throw(err);
});
}
}
Then, I tried to use the APP_INITIALIZER
opaque token to get the required settings to initialize my app.
app.module.ts
@NgModule({
// some declarations, imports, ...
providers: [
// some services ...
ConfigService,
{
provide: APP_INITIALIZER,
useFactory: (config: ConfigService) => () => config.load(),
deps:[ConfigService, Http],
multi: true
}
});
config.service.ts
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { AppSettings } from '../../environments/app-settings';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/toPromise';
@Injectable()
export class ConfigService {
public settings:AppSettings;
constructor(private http:Http) { }
load() : Promise<AppSettings> {
let url = '/settings/';
var observable= this.http.get(url)
.map(res => res.json());
observable.subscribe(config => this.settings = config);
return observable.toPromise();
}
}
This creates an error :
Uncaught Error: Provider parse errors:
Cannot instantiate cyclic dependency! ApplicationRef_: in NgModule AppModuleNgModuleProviderAnalyzer.parse @ provider_analyzer.js:291NgModuleCompiler.compile @ ng_module_compiler.js:54RuntimeCompiler._compileModule @ runtime_compiler.js:102RuntimeCompiler._compileModuleAndComponents @ runtime_compiler.js:65RuntimeCompiler.compileModuleAsync @ runtime_compiler.js:55PlatformRef_._bootstrapModuleWithZone @ application_ref.js:303PlatformRef_.bootstrapModule @ application_ref.js:285(anonymous function) @ main.ts:18__webpack_require__ @ bootstrap 0e2b412…:52(anonymous function) @ main.bundle.js:86665__webpack_require__ @ bootstrap 0e2b412…:52webpackJsonpCallback @ bootstrap 0e2b412…:23(anonymous function) @ main.bundle.js:1
If I comment out the custom Http provider, the error is not shown and the APP_INITIALIZER
works as expected.
If I remove the Router
from the Http provider deps declaration, I don't have the error anymore but the my ConfigService.load()
function is called twice.
Does anyone knows why this router dependency is causing this cyclic dependency error ?
How can I prevent my ConfigService.load()
function to be called twice ?
If needed, I have created a public repository reproducing the error : https://github.com/haia212/AngularErrorTestProject