71 votes

Comment passer des paramètres rendus du backend à la méthode angular2 bootstrap

Est-il possible de passer des arguments rendu sur le backend de angular2 la méthode du bootstrap? Je tiens à mettre en en-tête http pour toutes les demandes à l'aide de BaseRequestOptions avec la valeur fournie à partir de l'arrière-plan. Mon main.ts le fichier ressemble à ceci:

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

bootstrap(AppComponent);

J'ai trouvé comment passer des arguments à ce composant racine (https://stackoverflow.com/a/35553650/3455681), mais j'en ai besoin quand je suis fireing bootstrap méthode... des idées?

edit:

webpack.config.js contenu:

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'
      }
    ]
  }
};

93voto

Günter Zöchbauer Points 21340

update2

Plunker exemple

mise à jour AoT

Travailler avec AoT la fermeture d'usine doit être déplacé

function loadContext(context: ContextService) {
  return () => context.load();
}

@NgModule({
  ...
  providers: [ ..., ContextService, { provide: APP_INITIALIZER, useFactory: loadContext, deps: [ContextService], multi: true } ],

Voir aussi https://github.com/angular/angular/issues/11262

mise à jour d'un RC.6 et 2.0.0 dernier exemple

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 il n'est pas nécessaire d'attendre l'initialisation, le constructeur de la classe de AppModule {} peut également être utilisé:

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

conseil (dépendance cyclique)

Par exemple, l'injection du routeur peut provoquer des dépendances cycliques. Pour contourner, d'injecter de l' Injector et obtenir de la dépendance par

this.myDep = injector.get(MyDependency);

au lieu d'injecter MyDependency directement avec:

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

mise à jour

Cela devrait fonctionner de la même dans le RC.5 mais au lieu d'ajouter le fournisseur d' providers: [...] de la racine du module à la place de bootstrap(...)

(pas testé moi-même encore).

mise à jour

Une approche intéressante pour le faire entièrement à l'intérieur Angulaire est expliqué ici https://github.com/angular/angular/issues/9047#issuecomment-224075188

Vous pouvez utiliser APP_INITIALIZER qui va exécuter une fonction lorsque la app est initialisé de retard et de ce qu'il prévoit que si la fonction renvoie une promesse. Cela signifie que l'application peut être initialisation sans tout à fait aussi beaucoup de latence et vous pouvez également utiliser les services existants et le cadre les fonctionnalités.

Par exemple, supposons que vous avez un multi-tenanted solution où l' info sur le site s'appuie sur le nom de domaine, il est servi à partir de. Cela peut [nom].letterpress.com ou un domaine personnalisé qui est mentionné sur le nom d'hôte complet. On peut cacher le fait que c'est derrière une promesse faite par la à l'aide de APP_INITIALIZER.

Dans le 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;
  }

REMARQUE: config est juste une configuration personnalisé de classe. rootDomain serait '.letterpress.com' pour cet exemple et permettre des choses comme aptaincodeman.letterpress.com.

Tous les composants et d'autres services peuvent maintenant avoir Site injecté dans et de les utiliser l' .current de la propriété qui sera un béton peuplé d'objets sans avoir à attendre sur toute promesse au sein de l'application.

Cette approche semblait couper le démarrage de la latence qui est par ailleurs tout à fait perceptible si vous avez été en attente pour le grand Angulaire du faisceau à charger, puis une autre requête http avant le bootstrap même a commencé.

d'origine

Vous pouvez passer à l'aide de Angulars de l'injection de dépendances:

var headers = ... // get the headers from the server

bootstrap(AppComponent, [{provide: 'headers', useValue: headers})]);
class SomeComponentOrService {
   constructor(@Inject('headers') private headers) {}
}

ou de fournir préparé BaseRequestOptions directement comme

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

30voto

computeiro Points 374

Dans Angular2 sortie de la version finale, le APP_INITIALIZER fournisseur peut être utilisé pour obtenir ce que vous voulez.

J'ai écrit un Résumé avec un exemple complet: https://gist.github.com/fernandohu/122e88c3bcd210bbe41c608c36306db9

L'essentiel exemple est la lecture de fichiers JSON, mais peut être facilement modifié pour se lire à partir d'un point de terminaison REST.

Ce dont vous avez besoin, c'est en gros:

a) de créer des APP_INITIALIZER dans votre fichier de module existant:

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 }
    ],
    ...
});

Ces lignes vont appeler la méthode load() de BackendRequestClass classe avant que votre application est démarrée.

Assurez-vous de définir "Http" dans "les importations de la section" si vous voulez faire http appels à l'arrière-plan à l'aide de angular2 construit dans la bibliothèque.

b) Créer une classe et le nom du fichier "backend.demande.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) Pour lire le contenu du backend d'appel, vous avez juste besoin d'injecter de l'BackendRequestClass dans l'une des classes de votre choix et appelez getResult(). Exemple:

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

Laissez-moi savoir si cela résout votre problème.

8voto

Werlang Points 37

Au lieu d'avoir votre point d'entrée de l'appel de bootstrap lui-même, vous pouvez créer et exporter une fonction qui fait le travail:

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

Vous pouvez également placer cette fonction sur l'objet global, en fonction de votre configuration (webpack/SystemJS). Il est également AOT-compatible.

Cela a l'avantage de retarder le démarrage, quand elle fait sens. Par exemple, lorsque vous récupérer les données de l'utilisateur comme un appel AJAX après que l'utilisateur remplit un formulaire. Il suffit d'appeler le exporté bootstrap fonction de ces données.

1voto

thierry templier Points 998

La seule façon de le faire est de fournir à ces valeurs lors de la définition de vos fournisseurs:

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

Vous pouvez ensuite utiliser ces paramètres dans votre CustomRequestOptions classe:

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

Si vous obtenir ces paramètres à partir d'une requête AJAX, vous avez besoin pour amorcer de manière asynchrone de cette façon:

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

Voir cette question:

Modifier

Puisque vous avez vos données dans le format HTML, vous pouvez utiliser les éléments suivants.

Vous pouvez importer une fonction et de l'appeler avec des paramètres.

Voici un exemple du module principal qui démarre votre application:

import {bootstrap} from '...';
import {provide} from '...';
import {AppComponent} from '...';

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

Ensuite, vous pouvez l'importer à partir de votre code HTML de la page principale comme ceci:

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

Voir cette question: Transmettre des Valeurs Constantes Angulaire de _layout.cshtml.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X