Naveed Ahmed
Naveed Ahmed

Reputation: 10386

Angular 2 Lazy Loading and Application-wide singletons - Custom Http Class Not working

I am trying to use the Angular 2 Router Lazy Loading to reduce initial load time.

In my app I have customized the original Http service of the Angular to display a loader whenever any http request is initiated which I hide on completion.

Currently, I have a GlobalService (which is a application wide singleton) which hold the current status of the Loading bar and HttpLoading class which extends Angular original Http class.

In AppModule I have added both to the providers so that these are available application wide:

export function httpFactory(backend: XHRBackend, defaultOptions: RequestOptions, globalService: GlobalService) {
        return new HttpLoading(backend, defaultOptions, globalService);
    }

and

providers: [
            GlobalService,
            {
                provide: Http,
                useFactory: httpFactory,
                deps: [XHRBackend, RequestOptions, GlobalService]
            }
        ],

I have the below AppModule:

//angular
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { Http, XHRBackend, HttpModule, RequestOptions, JsonpModule } from '@angular/http';
import { NgModule } from '@angular/core';

//material
import { MdProgressBarModule } from '@angular2-material/progress-bar';

import { HomeModule } from './home/home.module';

import { AppComponent } from './app.component';

//services
import { GlobalService } from './shared/services/global.service';
import { HttpLoading } from './shared/services/http-loading';

//routing
import { routing } from './app.routing';

export function httpFactory(backend: XHRBackend, defaultOptions: RequestOptions, globalService: GlobalService) {
    return new HttpLoading(backend, defaultOptions, globalService);
}

@NgModule({
    imports: [
        //angular
        BrowserModule,
        FormsModule,
        HttpModule,
        JsonpModule,
        ReactiveFormsModule,
        routing,

        //material
        MdProgressBarModule.forRoot(),
        HomeModule,
    ],
    declarations: [
        AppComponent,
    ],
    providers: [
        GlobalService,
        {
            provide: Http,
            useFactory: httpFactory,
            deps: [XHRBackend, RequestOptions, GlobalService]
        }
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

Below is my HttpLoading Class:

import { Http, ConnectionBackend, RequestOptions, RequestOptionsArgs, Response, Headers } from '@angular/http';
import { Injectable } from '@angular/core';
import { GlobalService } from './global.service';

import { Observable } from 'rxjs/Observable';
import "rxjs/add/operator/catch";

export class HttpLoading extends Http {

constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, private _gs: GlobalService) {
        super(backend, defaultOptions);
    }

get(url: string, options?: RequestOptionsArgs): Observable<any> {
        this._gs.isLoading = true;

        return super.get(url, options)
            .map(res => {
                this._gs.isLoading = false;
                return res.json();
            });
    }

}

Below is my GlobalService

import { Injectable, Inject } from '@angular/core';

@Injectable() export class GlobalService { public isLoading: boolean = false; }

I tried to create below SharedModule which I thought I would inject in the AppModule and it will solve the problem:

import { NgModule, ModuleWithProviders } from "@angular/core";
import { Http, XHRBackend, HttpModule, RequestOptions, JsonpModule } from '@angular/http';
import { GlobalService } from './shared/services/global.service';
import { HttpLoading } from './shared/services/http-loading';

export function httpFactory(backend: XHRBackend, defaultOptions: RequestOptions, globalService: GlobalService) {
    return new HttpLoading(backend, defaultOptions, globalService);
}

@NgModule({
    imports: [
        //angular
        HttpModule,
        JsonpModule
    ]
})
export class SharedModule {
    static forRoot(): ModuleWithProviders {
        return {
            ngModule: SharedModule,
            providers: [GlobalService,
                {
                    provide: Http,
                    useFactory: httpFactory,
                    deps: [XHRBackend, RequestOptions, GlobalService]
                }]
        };
    }
}

And updated my AppModule to the below:

//angular
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { Http, XHRBackend, HttpModule, RequestOptions, JsonpModule } from '@angular/http';
import { NgModule } from '@angular/core';

//material
import { MdProgressBarModule } from '@angular2-material/progress-bar';

import { HomeModule } from './home/home.module';
import { SharedModule } from './../shared.module';

import { AppComponent } from './app.component';


//routing
import { routing } from './app.routing';


@NgModule({
    imports: [
        //angular
        BrowserModule,
        FormsModule,
        HttpModule,
        JsonpModule,
        ReactiveFormsModule,
        routing,

        //material
        MdProgressBarModule.forRoot(),
        HomeModule,
        SharedModule
    ],
    declarations: [
        AppComponent,
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

But it didn't work. Whenever I visit any route which has http call, it throws error on console.

For example, in HomeCompnent in ngOnInit I make an http call to fetch some data. But when I move to this component, I displays below error in console.

EXCEPTION: _super.prototype.get.call(...).map is not a function


core.umd.js:3468 TypeError: _super.prototype.get.call(...).map is not a function
    at HttpLoading.get (http-loading.js:50)
    at HomeComponent.ngOnInit

Can anyone please guide what am I doing wrong?

Upvotes: 0

Views: 504

Answers (1)

John Siu
John Siu

Reputation: 5092

Method 1:

In HttpLoading class file, add

import 'rxjs/add/operator/map';

Method 2(for systemjs):

You don't need method 1 if you do this.

In main.ts

import 'rxjs';

In systemjs.config.js;

map: {
  'rxjs': 'node_modules/rxjs',
},
packages: {
  'rxjs': { main: 'bundles/Rx.min.js', defaultExtension: 'js' },

Upvotes: 1

Related Questions