4

angular 2 アプリをロードする前に、データベースからいくつかの構成オプションを取得しようとしています。これらの構成オプションに基づいて特定のコンポーネントへのアクセスを制限する必要があるため、Angular アプリは開始する前にデータが読み込まれるのを待つ必要があります。

したがって、バックエンドからデータを取得するための ConfigurationService を作成しました。ルート モジュールで app_initializer を使用して、アプリを起動する前に構成サービスを呼び出そうとしています。数か月前に作成したAngularアプリ(Angular RC4)でこれを行うことができ、今回も最初は同じようにしようとしました。

app.module.ts - 最初の試行

import { NgModule, APP_INITIALIZER } from '@angular/core';
import { AppComponent } from './app.component';
import { RouterModule } from '@angular/router';
import { HttpModule } from '@angular/http';   

import { ConfigurationService } from "./shared/services/configuration.service";

@NgModule({
    imports: [
        ...
    ],
    declarations: [
        ...   
    ],    
    providers: [
        {
            provide: APP_INITIALIZER,
            useFactory: (config: ConfigurationService) => () => config.loadConfiguration(),
            deps: [ConfigurationService]
        },                 
        ConfigurationService        
    ],
    bootstrap: [AppComponent]
})

export class AppModule { }

これにより、次のエラーが発生しました。

Error encountered resolving symbol values statically. Function calls are not supported. Consider replacing the function or lambda with a reference to an exported function

これは、私が使用している AoT コンパイラと関係があると思います。ラムダ式をファクトリに置き換えました。

app.module.ts - 2 回目の試行

import { NgModule, APP_INITIALIZER } from '@angular/core';
import { AppComponent } from './app.component';
import { RouterModule } from '@angular/router';
import { HttpModule } from '@angular/http';   

import { ConfigurationService } from "./shared/services/configuration.service";
import { ConfigurationServiceFactory } from "./shared/services/configuration.service.factory";

@NgModule({
    imports: [
        ...
    ],
    declarations: [
        ...   
    ],    
    providers: [
        {
            provide: APP_INITIALIZER,
            useFactory: ConfigurationServiceFactory,
            deps: [ConfigurationService]
        },                 
        ConfigurationService        
    ],
    bootstrap: [AppComponent]
})

export class AppModule { }

configuration.service.factory.ts

import { ConfigurationService } from "./configuration.service";

export function ConfigurationServiceFactory(configurationService: ConfigurationService) {  
    return configurationService.loadConfiguration()
        .then(currentUser => {
                configurationService.currentUser = currentUser;                
            }
        );    
}

これは実際には構成サービスを使用してバックエンドからデータを取得しますが、アプリを開始する前に promise が解決されるのを待ちません。これは、ユーザーがホームページ (誰でもアクセスできる) から開始してから、管理者以外のユーザーに制限されているコンポーネントに移動するときに機能します。しかし、ユーザーが制限されているページを更新すると、構成が読み込まれる前に OnInit (ユーザーがアクセスできるかどうかを判断する場所) が起動します。私の場合、これにより、ユーザーはアクセスできるはずのページへのアクセスが拒否されます。

configuration.service.ts

import { Injectable } from "@angular/core";
import { Http, Response } from '@angular/http';

import { CurrentUser } from "../models/currentuser.model";

@Injectable()
export class ConfigurationService {
    public apiUrl: string = "api/";
    public currentUser: CurrentUser;

    constructor(private http:Http) {}

    /**
    * Get configuration from database
    */
    public loadConfiguration(): Promise<CurrentUser> {
        return this.http.get(this.apiUrl + "application/GetDataForCurrentUser")
            .toPromise()
            .then(this.extractData)
            .catch(this.handleError);
    }       
}

コンポーネントの実装

import { Component, OnInit } from '@angular/core';
import { MessageService } from '../shared/message/message.service'
import { ConfigurationService } from "../shared/services/configuration.service";

@Component({    
    selector: 'simple',
    templateUrl: 'simple.component.html'
})
export class SimpleComponent implements OnInit{ 
    constructor(private messageService: MessageService, private configurationService: ConfigurationService) {

    }
    ngOnInit() {                      
        if (!this.configurationService.isAdmin()) {            
            this.messageService.addMessage("Access denied", "danger");
        }
    }
}

現時点ではアイデアがありません。解決策を探しましたが、ラムダオプションを推奨している人々を見つけることができましたが、これは私にとってはうまくいきません。

4

1 に答える 1

5

angular gitter channelの @DzmitryShylovich の助けを借りて、app.module.ts内からサービスを呼び出すのが遅すぎたため、代わりに main.ts ファイル内からサービスを呼び出す必要があると指摘しました。問題。彼は私に次のplnkrを提供してくれました。

これは、それを機能させるために私自身のソリューションで行ったことです。

定数.ts

import { OpaqueToken } from '@angular/core'

export const CONFIG_TOKEN = new OpaqueToken('config');

名前の衝突を避けるために OpaqueToken を作成しました

main.ts

import { platformBrowser } from '@angular/platform-browser';
import { AppModuleNgFactory } from '../aot/app/app.module.ngfactory';
import { CONFIG_TOKEN } from './shared/constants/constants'

const configApi = "yoururlhere";
fetch(configApi, {
    credentials: 'include'
}).then(parseResponse);

function parseResponse(res: any) {    
    return res.json().then(parseData);
}

function parseData(data: any) {    
    return platformBrowser([{ provide: CONFIG_TOKEN, useValue: data }]).bootstrapModuleFactory(AppModuleNgFactory);
}

main.ts ファイルで、Web サービスを呼び出して構成データを取得し、データを解析して、アプリに渡すプロバイダーを作成します。資格情報を含めるように設定しました。Web サービスで Windows 認証を使用しているため、フェッチに資格情報を提供するように明示的に指示しないと 401 が返されます。

app.component.ts

import { Component, OnInit, Inject } from "@angular/core";
import { CONFIG_TOKEN } from './shared/constants/constants'

@Component({
    selector: "app",
    templateUrl: "app.component.html"
})

export class AppComponent implements OnInit {
    public configuration: any;
    public isAdmin: boolean;
    constructor(@Inject(CONFIG_TOKEN) config: any) {        
        this.configuration = config;       
    }

    ngOnInit() {     
        this.isAdmin = this.configuration.currentUser_IsAdmin;
    }    
}

私の app.component では、作成したプロバイダーを挿入して、Web サービスから取得した構成オブジェクトを使用できます。

タイプスクリプトの問題

このソリューションを開発しているときに、Visual Studio で typescript の問題が発生しました。次のエラーが発生しました。

error TS2304: Cannot find name 'fetch'.

whatwg-fetch typescript定義ファイルを手動でダウンロードすることでこれを解決しました

npm install --save-dev @types/whatwg-fetch

@DzmitryShylovich、助けてくれてありがとう!

于 2016-12-14T13:48:07.087 に答える