Cómo pasar parámetros renderizados desde el backend al método de arranque angular2
¿Hay alguna manera de pasar los argumentos representados en el backend al método de arranque angular2? Quiero configurar el encabezado http para todas las solicitudes usando BaseRequestOptions con el valor proporcionado desde el backend. Mi main.ts
archivo se ve así:
import { bootstrap } from '@angular/platform-browser-dynamic';
import { AppComponent } from "./app.component.ts";
bootstrap(AppComponent);
Encontré cómo pasar estos argumentos al componente raíz ( https://stackoverflow.com/a/35553650/3455681 ), pero lo necesito cuando estoy activando bootstrap
el método... ¿Alguna idea?
editar:
Contenido de 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'
}
]
}
};
actualización2
Ejemplo de plomero
actualizar AoT
Para trabajar con AoT es necesario eliminar el cierre de la fábrica
function loadContext(context: ContextService) {
return () => context.load();
}
@NgModule({
...
providers: [ ..., ContextService, { provide: APP_INITIALIZER, useFactory: loadContext, deps: [ContextService], multi: true } ],
Véase también https://github.com/angular/angular/issues/11262
actualizar un ejemplo final de RC.6 y 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 { }
Si no es necesario esperar a que se complete la inicialización, también se puede utilizar el constructor de `class AppModule {}:
class AppModule {
constructor(/*inject required dependencies */) {...}
}
pista (dependencia cíclica)
Por ejemplo, inyectar el enrutador puede provocar dependencias cíclicas. Para solucionarlo, inyecte Injector
y obtenga la dependencia mediante
this.myDep = injector.get(MyDependency);
en lugar de inyectar MyDependency
directamente como:
@Injectable()
export class ConfigService {
private router:Router;
constructor(/*private router:Router*/ injector:Injector) {
setTimeout(() => this.router = injector.get(Router));
}
}
actualizar
Esto debería funcionar igual en RC.5 pero en su lugar agregar el proveedor del providers: [...]
módulo raíz en lugar debootstrap(...)
(Aún no me he probado).
actualizar
Aquí se explica un enfoque interesante para hacerlo completamente dentro de Angular https://github.com/angular/angular/issues/9047#issuecomment -224075188
Puedes usar
APP_INITIALIZER
which ejecutará una función cuando se inicialice la aplicación y retrasará lo que proporciona si la función devuelve una promesa. Esto significa que la aplicación puede inicializarse sin tanta latencia y también puede utilizar los servicios y funciones del marco existentes.Como ejemplo, supongamos que tiene una solución multiinquilino donde la información del sitio depende del nombre de dominio desde el que se sirve. Puede ser [nombre].letterpress.com o un dominio personalizado que coincida con el nombre de host completo. Podemos ocultar el hecho de que esto está detrás de una promesa usando
APP_INITIALIZER
.En arranque:
{provide: APP_INITIALIZER, useFactory: (sites:SitesService) => () => sites.load(), deps:[SitesService, HTTP_PROVIDERS], multi: true}),
sitios.servicio.ts:
@Injectable() export class SitesService { public current:Site; constructor(private http:Http, private config:Config) { } load():Promise<Site> { 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
es solo una clase de configuración personalizada.rootDomain
sería'.letterpress.com'
para este ejemplo y permitiría cosas comoaptaincodeman.letterpress.com
.Ahora se pueden
Site
inyectar todos los componentes y otros servicios y usar la.current
propiedad que será un objeto poblado concreto sin necesidad de esperar ninguna promesa dentro de la aplicación.Este enfoque pareció reducir la latencia de inicio, que de otro modo era bastante notable si estaba esperando a que se cargara el paquete Angular grande y luego otra solicitud http antes de que comenzara el arranque.
original
Puedes pasarlo usando la inyección de dependencia de Angulars:
var headers = ... // get the headers from the server
bootstrap(AppComponent, [{provide: 'headers', useValue: headers})]);
class SomeComponentOrService {
constructor(@Inject('headers') private headers) {}
}
o proporcionar preparado BaseRequestOptions
directamente 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})]);
En la versión final de Angular2, el proveedor APP_INITIALIZER se puede utilizar para lograr lo que desea.
Escribí un Gist con un ejemplo completo: https://gist.github.com/fernandohu/122e88c3bcd210bbe41c608c36306db9
El ejemplo esencial es leer desde archivos JSON, pero se puede cambiar fácilmente para leer desde un punto final REST.
Lo que necesitas es básicamente:
a) Configure APP_INITIALIZER en su archivo 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 }
],
...
});
Estas líneas llamarán al método load() de la clase BackendRequestClass antes de que se inicie su aplicación.
Asegúrese de configurar "HttpModule" en la sección "importaciones" si desea realizar llamadas http al backend utilizando la biblioteca integrada angular2.
b) Cree una clase y nombre el archivo "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 leer el contenido de la llamada de backend, solo necesita inyectar BackendRequestClass en cualquier clase de su elección y llamar a getResult(). Ejemplo:
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
}
}
Déjame saber si esto resuelve tu problema.
En lugar de que su punto de entrada llame al propio bootstrap, puede crear y exportar una función que haga el trabajo:
export function doBootstrap(data: any) {
platformBrowserDynamic([{provide: Params, useValue: new Params(data)}])
.bootstrapModule(AppModule)
.catch(err => console.error(err));
}
También puede colocar esta función en el objeto global, dependiendo de su configuración (webpack/SystemJS). También es compatible con AOT.
Esto tiene el beneficio adicional de retrasar el arranque, cuando tiene sentido. Por ejemplo, cuando recupera estos datos de usuario como una llamada AJAX después de que el usuario completa un formulario. Simplemente llame a la función de arranque exportada con estos datos.
La única forma de hacerlo es proporcionar estos valores al definir sus proveedores:
bootstrap(AppComponent, [
provide(RequestOptions, { useFactory: () => {
return new CustomRequestOptions(/* parameters here */);
});
]);
Entonces puedes usar estos parámetros en tu CustomRequestOptions
clase:
export class AppRequestOptions extends BaseRequestOptions {
constructor(parameters) {
this.parameters = parameters;
}
}
Si obtiene estos parámetros de una solicitud AJAX, debe iniciar de forma asincrónica de esta manera:
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();
Vea esta pregunta:
- arranque angular2 con datos de llamadas ajax
Editar
Como tiene sus datos en el HTML, puede usar lo siguiente.
Puede importar una función y llamarla con parámetros.
A continuación se muestra un ejemplo del módulo principal que inicia su aplicación:
import {bootstrap} from '...';
import {provide} from '...';
import {AppComponent} from '...';
export function main(params) {
bootstrap(AppComponent, [
provide(RequestOptions, { useFactory: () => {
return new CustomRequestOptions(params);
});
]);
}
Luego puedes importarlo desde tu página principal HTML de esta manera:
<script>
var params = {"token": "@User.Token", "xxx": "@User.Yyy"};
System.import('app/main').then((module) => {
module.main(params);
});
</script>
Vea esta pregunta: Pasar valores constantes a Angular desde _layout.cshtml .