如何将从后端渲染的参数传递给angular2 bootstrap方法

时间:2016-06-03 10:10:00

标签: javascript angular

有没有办法将后端呈现的参数传递给angular2 bootstrap方法?我想使用BaseRequestOptions为所有请求设置http标头,并使用后端提供的值。我的main.ts文件如下所示:

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

bootstrap(AppComponent);

我找到了如何将这些参数传递给根组件(https://stackoverflow.com/a/35553650/3455681),但是当我触发bootstrap方法时,我需要它...任何想法?

修改

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

4 个答案:

答案 0 :(得分:87)

<强> UPDATE2

Plunker example

更新 AoT

要使用AoT,需要将工厂关闭移出

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

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

另见https://github.com/angular/angular/issues/11262

更新 RC.6和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 { }

如果不需要等待初始化完成,也可以使用`class AppModule {}的构造函数:

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

提示(循环依赖)

例如,注入路由器会导致循环依赖。 要解决此问题,请注入Injector并通过

获取依赖关系
this.myDep = injector.get(MyDependency);

而不是直接注入MyDependency,如:

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

<强>更新

这在RC.5中应该相同,但是将提供程序添加到根模块的providers: [...]而不是bootstrap(...)

(尚未自我测试)。

<强>更新

https://github.com/angular/angular/issues/9047#issuecomment-224075188

中解释了一种完全在Angular内部完成的有趣方法
  

你可以使用APP_INITIALIZER来执行一个函数   应用程序已初始化并延迟它在函数返回时提供的内容   一个承诺。这意味着应用程序可以正常初始化   延迟很多,您也可以使用现有的服务和框架   特征

     

举个例子,假设你有一个多租户的解决方案   网站信息依赖于它所提供的域名。这个可以   是[name] .letterpress.com或在。上匹配的自定义域名   完整的主机名。我们可以隐瞒事实,这是一个承诺的背后   使用APP_INITIALIZER

     

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

注意:config只是一个自定义配置类。 rootDomain会   '.letterpress.com'这个例子可以允许这样的事情   aptaincodeman.letterpress.com

     

任何组件和其他服务现在都可以注入Site   他们并使用.current属性,这将是具体的   填充对象,无需等待应用程序中的任何承诺。

     

这种方法似乎减少了原本的启动延迟   如果你在等待大的Angular束,那就太明显了   在引导程序开始之前加载然后另一个http请求。

<强>原始

您可以使用Angulars依赖注入传递它:

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

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

或直接提供准备好的BaseRequestOptions

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

答案 1 :(得分:27)

在Angular2最终版本中,APP_INITIALIZER提供程序可用于实现您的目标。

我写了一个完整的例子:https://gist.github.com/fernandohu/122e88c3bcd210bbe41c608c36306db9

gist示例是从JSON文件读取,但可以轻松更改为从REST端点读取。

你需要的,基本上是:

a)在现有模块文件中设置APP_INITIALIZER:

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

这些行将在应用程序启动之前从BackendRequestClass类调用load()方法。

确保你设置&#34; HttpModule&#34; in&#34;进口&#34;如果你想使用内置于库中的angular2对后端进行http调用。

b)创建一个类并命名文件&#34; backend.request.ts&#34;:

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)要读取后端调用的内容,您只需要将BackendRequestClass注入您选择的任何类中并调用getResult()。例如:

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

如果这可以解决您的问题,请告诉我。

答案 2 :(得分:7)

您可以创建并导出一个完成工作的函数,而不是让您的入口点调用bootstrap:

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

您也可以将此功能放在全局对象上,具体取决于您的设置(webpack / SystemJS)。它也是AOT兼容的。

当有意义时,这有助于延迟引导程序。例如,在用户填写表单后,将此用户数据作为AJAX调用检索。只需使用此数据调用导出的引导函数。

答案 3 :(得分:1)

唯一的方法是在定义提供者时提供这些值:

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

然后您可以在CustomRequestOptions类中使用这些参数:

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

如果从AJAX请求中获取这些参数,则需要以这种方式异步引导:

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

看到这个问题:

修改

由于您在HTML中拥有数据,因此可以使用以下内容。

您可以导入一个函数并使用参数调用它。

以下是引导您的应用程序的主要模块示例:

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

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

然后您可以从HTML主页面导入它,如下所示:

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

请参阅此问题:Pass Constant Values to Angular from _layout.cshtml