2016-12-08 4 views
4

Ich versuche, einige Konfigurationsoptionen aus der Datenbank zu bekommen, bevor ich meine angular 2 App lade. Die eckige App muss vor dem Start auf das Laden der Daten warten, da der Zugriff auf bestimmte Komponenten basierend auf diesen Konfigurationsoptionen eingeschränkt sein sollte.Laden der Konfiguration aus der Datenbank, bevor Angular 2 App mit AoT startet

Also habe ich einen ConfigurationService erstellt, um die Daten aus dem Backend zu bekommen und ich versuche mit dem app_initializer im root-Modul den Konfigurationsservice vor dem Starten der App aufzurufen. Ich habe es geschafft, dies in einer eckigen App zu tun, die ich vor ein paar Monaten erstellt habe (eckige RC4), und habe versucht, es dieses Mal auf die gleiche Weise zu tun.

app.module.ts - Erster Versuch

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

Dies führte zu dem folgenden Fehler:

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

Ich denke, das etwas mit den AOT-Compiler zu tun hat, die ich m verwenden. Ich habe die Lambda-Ausdrücke durch eine Fabrik ersetzt.

app.module.ts - Zweiter Versuch

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;     
      } 
     );  
} 

Dies wird tatsächlich die Daten aus dem Backend des configurationservice verwenden, aber Warten Sie nicht auf das Versprechen zu lösen, bevor Sie die App starten. Es funktioniert, wenn der Benutzer auf der Homepage startet (die für jeden zugänglich ist) und dann zu einer Komponente navigiert, die für Benutzer ohne Administratorrechte gesperrt ist. Wenn der Benutzer jedoch die eingeschränkte Seite aktualisiert, wird das OnInit (wo ich feststellen kann, ob ein Benutzer Zugriff hat) ausgelöst, bevor die Konfiguration geladen werden kann. Dies führt in meinem Fall dazu, dass Benutzern der Zugriff auf Seiten verweigert wird, auf die sie zugreifen dürfen.

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); 
    }  
} 

Component Implementierung

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

Ich bin ganz aus Ideen zu diesem Zeitpunkt habe ich nach einer Lösung gesucht, aber nur geschafft Leute finden, die die Lambda-Option empfehlen, was bei mir nicht funktioniert.

Antwort

3

Mit Hilfe von @DzmitryShylovich auf dem angular gitter channel, der das aus dem Inneren der app.module.ts den Dienst darauf hingewiesen Aufruf spät war und ich sollte den Dienst von innerhalb der main.ts nennen Datei I statt, hat es geschafft, mein Problem zu lösen. Er stellte mir folgendes zur Verfügung: plnkr.

Dies ist, was ich in meiner eigenen Lösung getan habe, um es zum Laufen zu bringen.

Konstanten.ts

import { OpaqueToken } from '@angular/core' 

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

Ich habe eine OpaqueToken to avoid naming collisions

main.ts erstellen

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); 
} 

In meiner main.ts Datei ich den Webservice aufrufen meine Konfigurationsdaten zu erhalten, analysieren die Daten und erstellen Sie einen Anbieter, der in meine App übertragen wird. Ich setze die Zugangsdaten ein, weil ich die Windows-Authentifizierung für den Webservice verwende, die eine 401 liefert, wenn Sie Fetch nicht explizit anweisen, Anmeldeinformationen anzugeben.

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

In meinem app.component kann ich meine erstellt Provider injiziert das Konfigurationsobjekt zu verwenden, die ich von dem Webservice bekam.

Typoskript Ausgabe

ich in ein Typoskript Problem in Visual Studio lief bei der Entwicklung dieser Lösung, ich folgende Fehlermeldung bekam:

error TS2304: Cannot find name 'fetch'. 

ich dies manuell gelöst, indem das Herunterladen von WHATWG-Fetch Typoskript Definitionsdatei

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

@DzmitryShylovich, danke für Ihre Hilfe!

+0

Wie umgehen Sie doppelte Bezeichnerfehler beim Einziehen von @ types/whatwg-fetch? Es scheint, als ob lib.d.ts jetzt Fetch-Typings enthält, so dass es beim manuellen Hinzufügen der Typings zu Kollisionen kommt. –

Verwandte Themen