我在使用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或其他服务?

最佳答案

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

  • 我将angular-application-insights软件包的源(不是我的初衷)合并到了我的代码库中,以便能够更改使我难以按自己的意愿进行引导的内容。我还与cyclic dependencies进行了对抗。感谢MarkPieszak分享您的图书馆。

  • 这是我的应用程序模块现在的样子:
    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);
        }
    
        ...
    

    application-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 ]
            };
        }
    }
    

    关于angular - 在APP_INITIALIZE之后初始化其他提供程序,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44715578/

    10-11 13:27