如何将从后端渲染的parameter passing给angular2引导方法
有没有办法将后端呈现的parameter passing给angular2引导方法? 我想为所有使用BaseRequestOptions和后端提供的值的请求设置http头。 我的main.ts
文件如下所示:
import { bootstrap } from '@angular/platform-browser-dynamic'; import { AppComponent } from "./app.component.ts"; bootstrap(AppComponent);
我发现如何将这个parameter passing给根组件( 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' } ] } };
UPDATE2
Plunker例子
更新 AoT
为了与AoT合作,工厂closures需要被移出
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: [...]
添加到提供providers: [...]
根模块而不是bootstrap(...)
(还没有testing过)。
更新
一个有趣的方法完全在Angular内部完成,在这里解释https://github.com/angular/angular/issues/9047#issuecomment-224075188
你可以使用
APP_INITIALIZER
,它会在应用程序初始化时执行一个函数,并在函数返回promise时延迟它提供的内容。 这意味着应用程序可以在没有太多延迟的情况下进行初始化,也可以使用现有的服务和框架function。例如,假设您有一个多租户解决scheme,其中站点信息依赖于它所服务的域名。 这可以是[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
只是一个自定义configuration类。 在这个例子中,rootDomain
将是'.letterpress.com'
,并允许像aptaincodeman.letterpress.com
这样的东西。现在,任何组件和其他服务都可以将
Site
注入到它们中,并使用.current
属性,这是一个具体的填充对象,无需在应用程序中等待任何承诺。这种方法似乎削减了启动延迟,如果您等待大型Angular bundle加载,然后在引导启动之前再发出另一个http请求,则启动延迟会非常明显。
原版的
你可以使用Angularsdependency injection来传递它:
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})]);
在Angular2最终版本中,可以使用APP_INITIALIZER提供程序来实现你想要的。
我用一个完整的例子写了一个Gist: https : //gist.github.com/fernandohu/122e88c3bcd210bbe41c608c36306db9
要点是从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()方法。
确保你在“imports”部分设置了“HttpModule”,如果你想使用内置的angular2库对后端进行http调用的话。
b)创build一个类并命名文件“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)要读取后端调用的内容,只需将BackendRequestClass注入到您select的任何类中,然后调用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 } }
让我知道这是否解决了你的问题。
您可以创build并导出一个执行该工作的函数,而不是让自己的入口点调用引导程序:
export function doBootstrap(data: any) { platformBrowserDynamic([{provide: Params, useValue: new Params(data)}]) .bootstrapModule(AppModule) .catch(err => console.error(err)); }
你也可以把这个函数放在全局对象上,具体取决于你的设置(webpack / SystemJS)。 它也是AOT兼容的。
如果有意义的话,这样可以延长引导时间。 例如,在用户填写表单之后,当您将此用户数据作为AJAX调用检索时。 用这个数据调用导出的bootstrap函数。
唯一的方法是在定义提供者时提供这些值:
bootstrap(AppComponent, [ provide(RequestOptions, { useFactory: () => { return new CustomRequestOptions(/* parameters here */); }); ]);
那么你可以在你的CustomRequestOptions
类中使用这些参数:
export class AppRequestOptions extends BaseRequestOptions { constructor(parameters) { this.parameters = parameters; } }
如果您从AJAX请求中获取这些参数,则需要以这种方式asynchronous引导:
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();
看到这个问题:
- angular2引导来自ajax调用的数据
编辑
既然你有你的数据在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>
看到这个问题: 从_layout.cshtml传递常量值为Angular 。