How to pass parameters rendered from backend to angular2 bootstrap method

前端 未结 4 549
感动是毒
感动是毒 2020-11-21 23:58

Is there a way to pass arguments rendered on the backend to angular2 bootstrap method? I want to set http header for all requests using BaseRequestOptions with value provide

4条回答
  •  被撕碎了的回忆
    2020-11-22 00:40

    update2

    Plunker example

    update AoT

    To work with AoT the factory closure needs to be moved out

    function loadContext(context: ContextService) {
      return () => context.load();
    }
    
    @NgModule({
      ...
      providers: [ ..., ContextService, { provide: APP_INITIALIZER, useFactory: loadContext, deps: [ContextService], multi: true } ],
    

    See also https://github.com/angular/angular/issues/11262

    update an RC.6 and 2.0.0 final example

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

    If there is no need to wait for the initialization to complete, the constructor of `class AppModule {} can also be used:

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

    hint (cyclic dependency)

    For example injecting the router can cause cyclic dependencies. To work around, inject the Injector and get the dependency by

    this.myDep = injector.get(MyDependency);
    

    instead of injecting MyDependency directly like:

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

    update

    This should work the same in RC.5 but instead add the provider to providers: [...] of the root module instead of bootstrap(...)

    (not tested myself yet).

    update

    An interesting approach to do it entirely inside Angular is explained here https://github.com/angular/angular/issues/9047#issuecomment-224075188

    You can use APP_INITIALIZER which will execute a function when the app is initialized and delay what it provides if the function returns a promise. This means the app can be initializing without quite so much latency and you can also use the existing services and framework features.

    As an example, suppose you have a multi-tenanted solution where the site info relies on the domain name it's being served from. This can be [name].letterpress.com or a custom domain which is matched on the full hostname. We can hide the fact that this is behind a promise by using APP_INITIALIZER.

    In 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 {
        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;
      }
    

    NOTE: config is just a custom config class. rootDomain would be '.letterpress.com' for this example and would allow things like aptaincodeman.letterpress.com.

    Any components and other services can now have Site injected into them and use the .current property which will be a concrete populated object with no need to wait on any promise within the app.

    This approach seemed to cut the startup latency which was otherwise quite noticeable if you were waiting for the large Angular bundle to load and then another http request before the bootstrap even began.

    original

    You can pass it using Angulars dependency injection:

    var headers = ... // get the headers from the server
    
    bootstrap(AppComponent, [{provide: 'headers', useValue: headers})]);
    
    class SomeComponentOrService {
       constructor(@Inject('headers') private headers) {}
    }
    

    or provide prepared BaseRequestOptions directly like

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

提交回复
热议问题