在APP_INITIALIZE之后初始化其他提供者

时间:2017-06-23 07:30:40

标签: angular typescript

我使用Angular 4有一点引导问题。 我有一个配置文件(json),我在应用程序启动时从服务器加载(另请参阅here)。到目前为止这是有效的。

现在我有一个dependency,需要在forRoot方法中传递配置值。实现如下:

static forRoot(config: AppInsightsConfig): ModuleWithProviders {
    return {
        ngModule: ApplicationInsightsModule,
        providers: [
            { provide: AppInsightsConfig, useValue: config }
        ]
    };
}

我的想法是在没有forRoot()的情况下导入模块,但在加载配置(来自服务器)之后提供AppInsightsConfig。

@NgModule({
    bootstrap: sharedConfig.bootstrap,
    declarations: [...sharedConfig.declarations],
    imports: [
        BrowserModule,
        FormsModule,
        HttpModule,
        ApplicationInsightsModule,
        ...sharedConfig.imports        
    ],
    providers: [
        { provide: 'ORIGIN_URL', useValue: location.origin },
        { provide: 
            APP_INITIALIZER, 
            useFactory: (configService: ConfigService) => () => configService.load(), 
            deps: [ConfigService], multi: true 
        },
        { provide: 
            AppInsightsConfig, 
            useFactory: (configService: ConfigService) => { 
                // outputs undefined, expected it to have the config
                console.log(configService.config); 
                return { instrumentationKey: 'key from config' } 
            },
            // My idea was, if I add APP_INITIALIZER as dependency,
            // the config would have been loaded, but this isn't the case.
            deps: [APP_INITIALIZER, ConfigService] 
        },
        AppInsightsService
    ]
})

如何在我的配置加载后提供AppInsightsConfig或其他服务?

1 个答案:

答案 0 :(得分:2)

最后,我提出了以下解决方案:

  • 我将angular-application-insights包的源代码(不是我的初衷)合并到我的代码库中,以便能够改变使我难以按照自己的意愿进行引导的事情。我也反对cyclic dependencies。感谢MarkPieszak分享您的图书馆。

这就是我的app-module现在的样子:

import { Resolve, Router } from '@angular/router';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { sharedConfig } from './app.module.shared';

import { SiteControlModule } from './site-control/site-control.module';
import { SiteControlComponent } from './site-control/site-control.component';
import { AuthModule } from './auth/auth.module';

import { ConfigService } from './core/config/config.service';

import { ApplicationInsightsModule } from './application-insights/application-insights.module';
import { ApplicationInsightsService } from './application-insights/application-insights.service';

import { CoreModule } from './core/core.module';

let initializeConfig = (aiService: ApplicationInsightsService, configService: ConfigService) => () => { 
    let loadConfigPromise = configService.load(); 

    loadConfigPromise.then(() => {
        aiService.config = configService.config.applicationInsights;
        aiService.init();
    });

    return loadConfigPromise; 
};


@NgModule({
    bootstrap: sharedConfig.bootstrap,
    declarations: [...sharedConfig.declarations],
    imports: [
        BrowserModule,
        FormsModule,
        HttpModule,
        AuthModule,
        ...sharedConfig.imports,
        CoreModule.forRoot(),
        ApplicationInsightsModule.forRoot(),
        SiteControlModule
    ],
    providers: [
        { provide: 'ORIGIN_URL', useValue: location.origin },
        { provide: 
            APP_INITIALIZER, 
            useFactory: initializeConfig, 
            deps: [ ApplicationInsightsService, ConfigService ], 
            multi: true 
        }     
    ]
})
export class AppModule {    
}

config.service.ts

import { Injectable } from '@angular/core';
import { Headers, RequestOptions,  Http, Response} from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/toPromise';

import { Config } from './models/config.model';

@Injectable()
export class ConfigService {

    config : Config;

    constructor(private http: Http) {
        console.log('constructing config service');
    }

    public load(): Promise<void> {
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let url = 'environment.json';

        let promise = this.http.get(url, { headers })
            .toPromise()
            .then(configs => { 
                console.log('environment loaded');
                this.config = configs.json() as Config; 
            })
            .catch(err => { 
                console.log(err); 
            });

        return promise;
    }

}

改变了application-insights.service.ts

的部分内容
import { Injectable, Optional, Injector } from '@angular/core';
import { Router, NavigationStart, NavigationEnd } from '@angular/router';
import { AppInsights } from 'applicationinsights-js';
import 'rxjs/add/operator/filter';
import IAppInsights = Microsoft.ApplicationInsights.IAppInsights;

import { ApplicationInsightsConfig } from '../core/config/models/application-insights.model';

@Injectable()
export class ApplicationInsightsService implements IAppInsights {

    context: Microsoft.ApplicationInsights.ITelemetryContext;
    queue: Array<() => void>;
    config: Microsoft.ApplicationInsights.IConfig;

    constructor(@Optional() _config: ApplicationInsightsConfig, private injector: Injector) {
        this.config = _config;
    }

    private get router(): Router {
        return this.injector.get(Router);
    }

    ...

应用insights.module.ts

import { NgModule, ModuleWithProviders, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';

import { ApplicationInsightsService} from './application-insights.service';

export * from './application-insights.service';

@NgModule({
    imports: [ CommonModule ],
    declarations: [],
    exports: [],
    providers: []
})

export class ApplicationInsightsModule {

    constructor(@Optional() @SkipSelf() parentModule: ApplicationInsightsModule) {
        if (parentModule) {
            throw new Error(
                'ApplicationInsightsModule is already loaded. Import it in the AppModule only');
        }
    }

    static forRoot(): ModuleWithProviders {
        return {
            ngModule: ApplicationInsightsModule,
            providers: [ ApplicationInsightsService ]
        };
    }    
}