Javascript 如何将后端渲染的参数传递给angular2 bootstrap方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37611549/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to pass parameters rendered from backend to angular2 bootstrap method
提问by Bodzio
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 BaseRequestOptionswith value provided from the backend. My main.ts
file looks like this:
有没有办法将后端呈现的参数传递给 angular2 bootstrap 方法?我想使用BaseRequestOptions和后端提供的值为所有请求设置 http 标头。我的main.ts
文件看起来像这样:
import { bootstrap } from '@angular/platform-browser-dynamic';
import { AppComponent } from "./app.component.ts";
bootstrap(AppComponent);
I found how to pass this arguments to root component (https://stackoverflow.com/a/35553650/3455681), but i need it when I'm fireing bootstrap
method... Any ideas?
我找到了如何将此参数传递给根组件(https://stackoverflow.com/a/35553650/3455681),但是当我触发bootstrap
方法时我需要它......有任何想法吗?
edit:
编辑:
webpack.config.js content:
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'
}
]
}
};
回答by Günter Z?chbauer
update2
更新2
updateAoT
更新AoT
To work with AoT the factory closure needs to be moved out
为了与 AoT 合作,工厂关闭需要移出
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
另见https://github.com/angular/angular/issues/11262
updatean RC.6 and 2.0.0 final example
更新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 { }
If there is no need to wait for the initialization to complete, the constructor of `class AppModule {} can also be used:
如果不需要等待初始化完成,也可以使用`class AppModule {}的构造函数:
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
例如,注入路由器会导致循环依赖。要解决此问题,请注入Injector
并获取依赖项
this.myDep = injector.get(MyDependency);
instead of injecting MyDependency
directly like:
而不是MyDependency
直接注入,如:
@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(...)
这应该在 RC.5 中工作相同,但将提供者添加到providers: [...]
根模块而不是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
这里解释了一种完全在 Angular 内部完成的有趣方法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<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; }
NOTE:
config
is just a custom config class.rootDomain
would be'.letterpress.com'
for this example and would allow things likeaptaincodeman.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.
您可以使用
APP_INITIALIZER
which 将在应用程序初始化时执行函数,并在函数返回承诺时延迟它提供的内容。这意味着应用程序可以在没有太多延迟的情况下进行初始化,您还可以使用现有的服务和框架功能。例如,假设您有一个多租户解决方案,其中站点信息依赖于为其提供服务的域名。这可以是 [name].letterpress.com 或与完整主机名匹配的自定义域。我们可以使用
APP_INITIALIZER
.在引导程序中:
{provide: APP_INITIALIZER, useFactory: (sites:SitesService) => () => sites.load(), deps:[SitesService, HTTP_PROVIDERS], multi: true}),
站点.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 请求,则启动延迟非常明显。
original
原来的
You can pass it using Angulars dependency injection:
您可以使用 Angulars 依赖注入传递它:
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
或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})]);
回答by computeiro
In Angular2 final release, the APP_INITIALIZER provider can be used to achieve what you want.
在 Angular2 最终版本中,可以使用 APP_INITIALIZER 提供程序来实现您想要的。
I wrote a Gist with a complete example: https://gist.github.com/fernandohu/122e88c3bcd210bbe41c608c36306db9
我写了一个完整的例子:https: //gist.github.com/fernandohu/122e88c3bcd210bbe41c608c36306db9
The gist example is reading from JSON files but can be easily changed to read from a REST endpoint.
要点示例是从 JSON 文件读取,但可以轻松更改为从 REST 端点读取。
What you need, is basically:
你需要的,基本上是:
a) Set up APP_INITIALIZER in your existent module file:
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 }
],
...
});
These lines will call the load() method from BackendRequestClass class before your application is started.
这些行将在您的应用程序启动之前从 BackendRequestClass 类调用 load() 方法。
Make sure you set "HttpModule" in "imports" section if you want to make http calls to the backend using angular2 built in library.
如果要使用 angular2 内置库对后端进行 http 调用,请确保在“导入”部分设置“HttpModule”。
b) Create a class and name the file "backend.request.ts":
b) 创建一个类并将文件命名为“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) To read the contents of the backend call, you just need to inject the BackendRequestClass into any class of you choice and call getResult(). Example:
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
}
}
Let me know if this solves your problem.
如果这能解决您的问题,请告诉我。
回答by André Werlang
Instead of having your entry point calling bootstrap itself, you could create and export a function that does the work:
您可以创建并导出一个完成工作的函数,而不是让您的入口点调用 bootstrap 本身:
export function doBootstrap(data: any) {
platformBrowserDynamic([{provide: Params, useValue: new Params(data)}])
.bootstrapModule(AppModule)
.catch(err => console.error(err));
}
You could also place this function on the global object, depending on your setup (webpack/SystemJS). It also is AOT-compatible.
你也可以把这个函数放在全局对象上,这取决于你的设置(webpack/SystemJS)。它也与 AOT 兼容。
This has the added benefit to delay the bootstrap, whenit makes sense. For instance, when you retrieve this user data as an AJAX call after the user fills out a form. Just call the exported bootstrap function with this data.
这有一个额外的好处,可以延迟引导,当它有意义时。例如,当您在用户填写表单后通过 AJAX 调用检索此用户数据时。只需使用此数据调用导出的引导程序函数即可。
回答by Thierry Templier
The only way to do that is to provide these values when defining your providers:
唯一的方法是在定义提供者时提供这些值:
bootstrap(AppComponent, [
provide(RequestOptions, { useFactory: () => {
return new CustomRequestOptions(/* parameters here */);
});
]);
Then you can use these parameters in your CustomRequestOptions
class:
然后你可以在你的CustomRequestOptions
类中使用这些参数:
export class AppRequestOptions extends BaseRequestOptions {
constructor(parameters) {
this.parameters = parameters;
}
}
If you get these parameters from an AJAX request, you need to bootstrap asynchronously this way:
如果从 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();
See this question:
看到这个问题:
Edit
编辑
Since you have your data in the HTML you could use the following.
由于您的数据在 HTML 中,因此您可以使用以下内容。
You can import a function and call it with parameters.
您可以导入一个函数并使用参数调用它。
Here is a sample of the main module that bootstraps your application:
这是引导您的应用程序的主模块的示例:
import {bootstrap} from '...';
import {provide} from '...';
import {AppComponent} from '...';
export function main(params) {
bootstrap(AppComponent, [
provide(RequestOptions, { useFactory: () => {
return new CustomRequestOptions(params);
});
]);
}
Then you can import it from your HTML main page like this:
然后你可以像这样从你的 HTML 主页导入它:
<script>
var params = {"token": "@User.Token", "xxx": "@User.Yyy"};
System.import('app/main').then((module) => {
module.main(params);
});
</script>
See this question: Pass Constant Values to Angular from _layout.cshtml.