javascript - parametros - Cómo pasar parámetros procesados desde el backend al método bootstrap angular2
pasar parametros entre componentes angular 2 (4)
¿Hay alguna manera de pasar los argumentos representados en el backend al método angular2 bootstrap?
Quiero establecer el encabezado http para todas las solicitudes utilizando
BaseRequestOptions
con el valor proporcionado desde el back-end.
Mi archivo
main.ts
tiene este aspecto:
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 el método
bootstrap
... ¿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''
}
]
}
};
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 establecer "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 al backend, solo necesita inyectar el 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
}
}
Avísame si esto resuelve tu problema.
En lugar de tener su punto de entrada llamando a 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 complete un formulario. Simplemente llame a la función bootstrap 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 */);
});
]);
Luego puede usar estos parámetros en su clase
CustomRequestOptions
:
export class AppRequestOptions extends BaseRequestOptions {
constructor(parameters) {
this.parameters = parameters;
}
}
Si obtiene estos parámetros de una solicitud AJAX, debe arrancar de forma asíncrona 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();
Ver esta pregunta:
Editar
Como tiene sus datos en el HTML, puede usar lo siguiente.
Puede importar una función y llamarla con parámetros.
Aquí hay una muestra 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 puede importarlo desde su 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>
Consulte esta pregunta: Pase valores constantes a angular desde _layout.cshtml .
actualización2
actualizar AoT
Para trabajar con AoT, el cierre de la fábrica debe retirarse
function loadContext(context: ContextService) {
return () => context.load();
}
@NgModule({
...
providers: [ ..., ContextService, { provide: APP_INITIALIZER, useFactory: loadContext, deps: [ContextService], multi: true } ],
Ver también https://github.com/angular/angular/issues/11262
actualizar un ejemplo final 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 hay necesidad de esperar a que se complete la inicialización, también se puede usar el constructor de `class AppModule {}:
class AppModule {
constructor(/*inject required dependencies */) {...}
}
pista (dependencia cíclica)
Por ejemplo, inyectar el enrutador puede causar dependencias cíclicas.
Para evitarlo, inyecte el
Injector
y obtenga la dependencia por
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 agrega el proveedor a los
providers: [...]
del módulo raíz en lugar de
bootstrap(...)
(No me he probado todavía).
actualizar
Aquí se explica un enfoque interesante para hacerlo completamente dentro de Angular https://github.com/angular/angular/issues/9047#issuecomment-224075188
Puede usar
APP_INITIALIZER
que ejecutará una función cuando se inicializa la aplicación y retrasará lo que proporciona si la función devuelve una promesa. Esto significa que la aplicación se puede inicializar sin tanta latencia y también puede usar los servicios existentes y las características del marco.Como ejemplo, suponga que tiene una solución multi-tenanted donde la información del sitio se basa en el 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 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<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
.Cualquier componente y otros servicios ahora pueden inyectar el
Site
en ellos y usar la propiedad.current
que será un objeto poblado concreto sin necesidad de esperar ninguna promesa dentro de la aplicación.Este enfoque parecía reducir la latencia de inicio, que de otra manera era bastante notable si estaba esperando que se cargara el paquete angular grande y luego otra solicitud http antes de que comenzara el arranque.
original
Puede pasarlo usando la inyección de dependencia de Angulares:
var headers = ... // get the headers from the server
bootstrap(AppComponent, [{provide: ''headers'', useValue: headers})]);
class SomeComponentOrService {
constructor(@Inject(''headers'') private headers) {}
}
o proporcionar
BaseRequestOptions
preparadas 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})]);