4

在加载我的 Angular 2 应用程序之前,我试图从数据库中获取一些配置选项。Angular 应用程序需要等待数据加载后才能启动,因为应根据这些配置选项限制对某些组件的访问。

所以我创建了一个 ConfigurationService 来从后端获取数据,并且我试图在启动应用程序之前使用根模块中的 app_initializer 来调用 configurationservice。我已经设法在几个月前创建的 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 编译器有关。我已经用工厂替换了 lambda 表达式。

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";
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 { }

配置.service.factory.ts

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

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

这实际上是使用配置服务从后端获取数据,但在启动应用程序之前不会等待承诺解决。它在用户从主页开始(每个人都可以访问)然后导航到非管理员用户限制的组件时工作。但是当用户刷新受限制的页面时,在加载配置之前会触发 OnInit(我确定用户是否有权访问)。就我而言,这导致用户被拒绝访问他们应该有权访问的页面。

配置.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");
        }
    }
}

我现在完全没有想法,我一直在寻找解决方案,但只设法找到推荐 lambda 选项的人,这对我不起作用。

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 以避免命名冲突

主要的.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 身份验证,当您没有明确告诉 fetch 提供凭据时,它会给出 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 中遇到了打字稿问题,我收到以下错误:

error TS2304: Cannot find name 'fetch'.

我通过手动下载 whatwg-fetch 打字稿定义文件解决了这个问题

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

@DzmitryShylovich,感谢您的帮助!

于 2016-12-14T13:48:07.087 回答