Cómo pasar parámetros renderizados desde el backend al método de arranque angular2

Resuelto Bodzio asked hace 8 años • 4 respuestas

¿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.tsarchivo 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 bootstrapel 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'
      }
    ]
  }
};
Bodzio avatar Jun 03 '16 17:06 Bodzio
Aceptado

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 Injectory obtenga la dependencia mediante

this.myDep = injector.get(MyDependency);

en lugar de inyectar MyDependencydirectamente 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 usarAPP_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: configes solo una clase de configuración personalizada. rootDomainsería '.letterpress.com'para este ejemplo y permitiría cosas como aptaincodeman.letterpress.com.

Ahora se pueden Siteinyectar todos los componentes y otros servicios y usar la .currentpropiedad 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 BaseRequestOptionsdirectamente 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})]);
Günter Zöchbauer avatar Jun 03 '2016 10:06 Günter Zöchbauer

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.

computeiro avatar Nov 02 '2016 12:11 computeiro

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.

André Werlang avatar Dec 21 '2016 22:12 André Werlang

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 CustomRequestOptionsclase:

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 .

Thierry Templier avatar Jun 03 '2016 10:06 Thierry Templier