【问题标题】:Initialize other provider after APP_INITIALIZE在 APP_INITIALIZE 之后初始化其他提供者
【发布时间】:2017-06-23 07:30:39
【问题描述】:

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

【问题讨论】:

  • 您是在使用路由还是在自己的模块上实现了forRoot
  • @Maximus 不,forRoot 旨在从该第 3 方模块的模块中调用。 see this source file
  • 你不能那样做。而且根据我对上面代码的理解,你不需要。 configService 没有代码,但你可以在 AppInsightsConfig 中链接一个 Promise 并修补返回的对象。 AppInsightsConfig 最初不会有instrumentationKey,但会在应用程序初始化后拥有它。 deps: [APP_INITIALIZER, 会搞砸 DI 但无济于事。
  • @estus 感谢您的提示。 DI 真的搞砸了,还有很多问题。

标签: angular typescript


【解决方案1】:

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

  • 我将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 ]
        };
    }    
}

【讨论】:

  • 看起来你想破解系统 :) 另外,当我从后端获得配置后,我需要注册另一个提供程序,但在这些困难之后,你分享的内容看起来不是t 角度 DI 的正确方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多