Como passar parâmetros renderizados do backend para o método de bootstrap angular2

Existe uma maneira de passar argumentos renderizados no backend para o método de bootstrap angular2? Eu quero definir o header http para todas as solicitações usando BaseRequestOptions com valor fornecido a partir do back-end. Meu arquivo main.ts é assim:

 import { bootstrap } from '@angular/platform-browser-dynamic'; import { AppComponent } from "./app.component.ts"; bootstrap(AppComponent); 

Eu encontrei como passar esses argumentos para o componente raiz ( https://stackoverflow.com/a/35553650/3455681 ), mas eu preciso quando estou acionando o método de bootstrap … Alguma idéia?

editar:

conteúdo do webpack.config.js:

 module.exports = { entry: { app: "./Scripts/app/main.ts" }, output: { filename: "./Scripts/build/[name].js" }, resolve: { extensions: ["", ".ts", ".js"] }, module: { loaders: [ { test: /\.ts$/, loader: 'ts-loader' } ] } }; 

update2

Exemplo de Plunker

atualizar o AoT

Para trabalhar com a AoT, o fechamento da fábrica precisa ser removido

 function loadContext(context: ContextService) { return () => context.load(); } @NgModule({ ... providers: [ ..., ContextService, { provide: APP_INITIALIZER, useFactory: loadContext, deps: [ContextService], multi: true } ], 

Veja também https://github.com/angular/angular/issues/11262

atualizar um exemplo final de RC.6 e 2.0.0

 function configServiceFactory (config: ConfigService) { return () => config.load(); } @NgModule({ declarations: [AppComponent], imports: [BrowserModule, routes, FormsModule, HttpModule], providers: [AuthService, Title, appRoutingProviders, ConfigService, { provide: APP_INITIALIZER, useFactory: configServiceFactory deps: [ConfigService], multi: true } ], bootstrap: [AppComponent] }) export class AppModule { } 

Se não houver necessidade de esperar pela boot, o construtor da class AppModule {} também pode ser usado:

 class AppModule { constructor(/*inject required dependencies */) {...} } 

dica (dependência cíclica)

Por exemplo, injetar o roteador pode causar dependencies cíclicas. Para contornar, injete o Injector e obtenha a dependência

 this.myDep = injector.get(MyDependency); 

em vez de injetar MyDependency diretamente como:

 @Injectable() export class ConfigService { private router:Router; constructor(/*private router:Router*/ injector:Injector) { setTimeout(() => this.router = injector.get(Router)); } } 

atualizar

Isso deve funcionar da mesma maneira no RC.5, mas em vez disso, adicionar o provedor aos providers: [...] do módulo raiz ao invés do bootstrap(...)

(não me testado ainda).

atualizar

Uma abordagem interessante para fazê-lo inteiramente dentro do Angular é explicada aqui https://github.com/angular/angular/issues/9047#issuecomment-224075188

Você pode usar o APP_INITIALIZER que executará uma function quando o aplicativo for inicializado e atrasará o que ele fornece se a function retornar uma promise. Isso significa que o aplicativo pode ser inicializado sem muita latência e você também pode usar os serviços e os resources de estrutura existentes.

Por exemplo, suponha que você tenha uma solução de multilocação onde as informações do site dependam do nome de domínio do qual está sendo atendido. Isso pode ser [nome] .letterpress.com ou um domínio personalizado que corresponde ao nome completo do host. Podemos ocultar o fato de que isso está por trás de uma promise usando APP_INITIALIZER .

No bootstrap:

 {provide: APP_INITIALIZER, useFactory: (sites:SitesService) => () => sites.load(), deps:[SitesService, HTTP_PROVIDERS], multi: true}), 

sites.service.ts:

 @Injectable() export class SitesService { public current:Site; constructor(private http:Http, private config:Config) { } load():Promise { var url:string; var pos = location.hostname.lastIndexOf(this.config.rootDomain); var url = (pos === -1) ? this.config.apiEndpoint + '/sites?host=' + location.hostname : this.config.apiEndpoint + '/sites/' + location.hostname.substr(0, pos); var promise = this.http.get(url).map(res => res.json()).toPromise(); promise.then(site => this.current = site); return promise; } 

NOTA: config é apenas uma class de configuração personalizada. rootDomain seria '.letterpress.com' para este exemplo e permitiria coisas como aptaincodeman.letterpress.com .

Quaisquer componentes e outros serviços podem agora ter Site injetado neles e usar a propriedade .current que será um object concreto preenchido, sem necessidade de esperar por qualquer promise dentro do aplicativo.

Essa abordagem pareceu reduzir a latência de boot que, de outra forma, seria bastante perceptível se você estivesse esperando que o grande pacote Angular fosse carregado e, em seguida, outra solicitação de http antes mesmo do bootstrap começar.

original

Você pode passá-lo usando a injeção de dependência Angulars:

 var headers = ... // get the headers from the server bootstrap(AppComponent, [{provide: 'headers', useValue: headers})]); 
 class SomeComponentOrService { constructor(@Inject('headers') private headers) {} } 

ou fornecer BaseRequestOptions preparados diretamente como

 class MyRequestOptions extends BaseRequestOptions { constructor (private headers) { super(); } } var values = ... // get the headers from the server var headers = new MyRequestOptions(values); bootstrap(AppComponent, [{provide: BaseRequestOptions, useValue: headers})]); 

Na versão final do Angular2, o provedor APP_INITIALIZER pode ser usado para obter o que você deseja.

Eu escrevi um Gist com um exemplo completo: https://gist.github.com/fernandohu/122e88c3bcd210bbe41c608c36306db9

O exemplo gist está lendo arquivos JSON, mas pode ser facilmente alterado para ler a partir de um terminal REST.

O que você precisa é basicamente:

a) Configure o APP_INITIALIZER no seu arquivo de módulo existente:

 import { APP_INITIALIZER } from '@angular/core'; import { BackendRequestClass } from './backend.request'; import { HttpModule } from '@angular/http'; ... @NgModule({ imports: [ ... HttpModule ], ... providers: [ ... ... BackendRequestClass, { provide: APP_INITIALIZER, useFactory: (config: BackendRequestClass) => () => config.load(), deps: [BackendRequestClass], multi: true } ], ... }); 

Essas linhas chamarão o método load () da class BackendRequestClass antes que seu aplicativo seja iniciado.

Certifique-se de definir “HttpModule” na seção “imports” se quiser fazer chamadas http para o backend usando a biblioteca interna angular2.

b) Crie uma class e nomeie o arquivo “backend.request.ts”:

 import { Inject, Injectable } from '@angular/core'; import { Http } from '@angular/http'; import { Observable } from 'rxjs/Rx'; @Injectable() export class BackendRequestClass { private result: Object = null; constructor(private http: Http) { } public getResult() { return this.result; } public load() { return new Promise((resolve, reject) => { this.http.get('http://address/of/your/backend/endpoint').map( res => res.json() ).catch((error: any):any => { reject(false); return Observable.throw(error.json().error || 'Server error'); }).subscribe( (callResult) => { this.result = callResult; resolve(true); }); }); } } 

c) Para ler o conteúdo da chamada backend, você só precisa injetar o BackendRequestClass em qualquer class de sua escolha e chamar getResult (). Exemplo:

 import { BackendRequestClass } from './backend.request'; export class AnyClass { constructor(private backendRequest: BackendRequestClass) { // note that BackendRequestClass is injected into a private property of AnyClass } anyMethod() { this.backendRequest.getResult(); // This should return the data you want } } 

Deixe-me saber se isso resolve o seu problema.

Em vez de ter seu próprio ponto de input chamando bootstrap, você poderia criar e exportar uma function que executa o trabalho:

 export function doBootstrap(data: any) { platformBrowserDynamic([{provide: Params, useValue: new Params(data)}]) .bootstrapModule(AppModule) .catch(err => console.error(err)); } 

Você também pode colocar essa function no object global, dependendo da configuração (webpack / SystemJS). Também é compatível com AOT.

Isso tem o benefício adicional de atrasar o bootstrap, quando faz sentido. Por exemplo, quando você recupera esses dados do usuário como uma chamada AJAX depois que o usuário preenche um formulário. Basta chamar a function de autoboot exportada com esses dados.

A única maneira de fazer isso é fornecer esses valores ao definir seus provedores:

 bootstrap(AppComponent, [ provide(RequestOptions, { useFactory: () => { return new CustomRequestOptions(/* parameters here */); }); ]); 

Então você pode usar esses parâmetros em sua class CustomRequestOptions :

 export class AppRequestOptions extends BaseRequestOptions { constructor(parameters) { this.parameters = parameters; } } 

Se você obtiver esses parâmetros a partir de uma solicitação AJAX, será necessário fazer o bootstrap de forma assíncrona desta maneira:

 var appProviders = [ HTTP_PROVIDERS ] var app = platform(BROWSER_PROVIDERS) .application([BROWSER_APP_PROVIDERS, appProviders]); var http = app.injector.get(Http); http.get('http://.../some path').flatMap((parameters) => { return app.bootstrap(appComponentType, [ provide(RequestOptions, { useFactory: () => { return new CustomRequestOptions(/* parameters here */); }}) ]); }).toPromise(); 

Veja esta pergunta:

  • bootstrap angular2 com dados de chamadas ajax (s)

Editar

Como você tem seus dados no HTML, você pode usar o seguinte.

Você pode importar uma function e chamá-la com parâmetros.

Aqui está uma amostra do módulo principal que inicializa seu aplicativo:

 import {bootstrap} from '...'; import {provide} from '...'; import {AppComponent} from '...'; export function main(params) { bootstrap(AppComponent, [ provide(RequestOptions, { useFactory: () => { return new CustomRequestOptions(params); }); ]); } 

Então você pode importá-lo de sua página principal HTML como este:

  

Veja esta pergunta: Passe valores constantes para Angular de _layout.cshtml .