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


¿Hay alguna forma de pasar argumentos renderizados en el backend al método angular2 bootstrap? Quiero establecer el encabezado http para todas las solicitudes usando BaseRequestOptions con el valor proporcionado desde el backend. Mi archivo main.ts 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 disparando bootstrap método... Alguna idea?

Editar:

Webpack.config.js contenido:

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'
      }
    ]
  }
};
Author: Community, 2016-06-03

4 answers

Update2

Ejemplo de émbolo

Update AoT

Para trabajar con AoT el cierre de la fábrica debe ser trasladado

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

Update an RC.6 y 2.0.0 ejemplo final

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, el constructor de ' class AppModule {} también puede ser usado:

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

Hint (dependencia cíclica)

Por ejemplo inyectar el router puede causar dependencias cíclicas. Para solucionar el problema, inyecte el Injector y obtenga la dependencia de

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

Update

Esto debería funcionar igual en RC.5 pero en lugar de agregar el proveedor a providers: [...] del módulo raíz en lugar de bootstrap(...)

(no probado a mí mismo aun).

Update

Un enfoque interesante para hacerlo completamente dentro de Angular se explica aquí https://github.com/angular/angular/issues/9047#issuecomment-224075188

Puede usar APP_INITIALIZER que ejecutará una función cuando el la aplicación se inicializa y retrasa lo que proporciona si la función devuelve promesa. Esto significa que la aplicación se puede inicializar sin tan latencia y también puede utilizar los servicios existentes y marco función.

Como ejemplo, supongamos que usted tiene una solución multi-tenanted donde el la información del sitio se basa en el nombre de dominio desde el que se sirve. Esto puede be [nombre]. letterpress.com o un dominio personalizado que coincide en el nombre de host completo. Podemos ocultar el hecho de que esto está detrás de una promesa por usando APP_INITIALIZER.

En bootstrap:

{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 como aptaincodeman.letterpress.com.

Cualquier componente y otros servicios ahora pueden tener Site inyectado en y usar la propiedad .current que será un concreto objeto rellenado sin necesidad de esperar ninguna promesa dentro de la aplicación.

Este enfoque parecía cortar la latencia de inicio que era de lo contrario bastante notable si usted estaba esperando el paquete Angular grande a cargar y luego otra solicitud http antes de que el bootstrap incluso comenzar.

Original

Se puede pasar usando Angulares inyección de dependencia:

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})]);
 76
Author: Günter Zöchbauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-02 15:16:08

En la versión final de Angular2, el proveedor APP_INITIALIZER se puede usar para lograr lo que desea.

Escribí una idea con un ejemplo completo: https://gist.github.com/fernandohu/122e88c3bcd210bbe41c608c36306db9

El ejemplo de gist 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 a la carga() método de la clase BackendRequestClass antes de que se inicie su aplicación.

Asegúrese de establecer "HttpModule" en la sección "imports" si desea hacer llamadas http al backend usando angular2 built in library.

B) Cree una clase y nombre el archivo "backend.solicitud.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 backend, solo necesita inyectar la 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
    }
}

Hágame saber si esto resuelve tu problema.

 23
Author: computeiro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-02 12:19:27

En lugar de tener su punto de entrada llamando a bootstrap, podría 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 añadido de retrasar el bootstrap, cuando tiene sentido. Por ejemplo, cuando recupera estos datos de usuario como una llamada AJAX después de que el usuario rellene un formulario. Simplemente llame a la función bootstrap exportada con estos datos.

 6
Author: André Werlang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-21 22:46:17

La única manera 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 clase CustomRequestOptions:

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

Si obtiene estos parámetros de una solicitud AJAX, necesita arrancar asíncronamente 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

Dado que tiene sus datos en el HTML, podría usar el siguiente.

Puede importar una función y llamarla con parámetros.

Aquí hay una muestra del módulo principal que arranca 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);
    });
  ]);
}

Entonces puedes importarlo desde tu página principal HTML así:

<script>
  var params = {"token": "@User.Token", "xxx": "@User.Yyy"};
  System.import('app/main').then((module) => {
    module.main(params);
  });
</script>

Vea esta pregunta: Pase Valores Constantes a Angular desde _layout.cshtml.

 0
Author: Thierry Templier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:17