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

Bod*_*zio 71 javascript angular

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

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

bootstrap(AppComponent);
Run Code Online (Sandbox Code Playgroud)

我发现如何将这个参数传递给root组件(/sf/answers/2488755531/),但是当我开火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'
      }
    ]
  }
};
Run Code Online (Sandbox Code Playgroud)

Gün*_*uer 93

UPDATE2

Plunker的例子

更新 AoT

要与AoT合作,需要将工厂关闭搬出

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

@NgModule({
  ...
  providers: [ ..., ContextService, { provide: APP_INITIALIZER, useFactory: loadContext, deps: [ContextService], multi: true } ],
Run Code Online (Sandbox Code Playgroud)

另见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 { }
Run Code Online (Sandbox Code Playgroud)

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

class AppModule {
  constructor(/*inject required dependencies */) {...} 
}
Run Code Online (Sandbox Code Playgroud)

提示(循环依赖)

例如,注入路由器可能会导致循环依赖.要解决此问题,请注入Injector并获取依赖关系

this.myDep = injector.get(MyDependency);
Run Code Online (Sandbox Code Playgroud)

而不是MyDependency直接注入:

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

更新

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

(尚未自我测试).

更新

完全在Angular中完成这个有趣的方法在这里解释https://github.com/angular/angular/issues/9047#issuecomment-224075188

您可以使用APP_INITIALIZER哪个将在应用程序初始化时执行函数,并在函数返回promise时延迟它提供的功能.这意味着应用程序可以在没有太多延迟的情况下进行初始化,您还可以使用现有的服务和框架功能.

例如,假设您有一个多租户解决方案,其中站点信息依赖于它所服务的域名.这可以是[name] .letterpress.com或在完整主机名上匹配的自定义域.我们可以通过使用隐藏这是一个承诺的事实APP_INITIALIZER.

在引导程序中:

{provide: APP_INITIALIZER, useFactory: (sites:SitesService) => () => sites.load(), deps:[SitesService, HTTP_PROVIDERS], multi: true}),
Run Code Online (Sandbox Code Playgroud)

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;
  }
Run Code Online (Sandbox Code Playgroud)

注意:config只是一个自定义配置类.rootDomain将是 '.letterpress.com'这个例子,并允许像这样的事情 aptaincodeman.letterpress.com.

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

这种方法似乎削减了启动延迟,如果你等待加载大型Angular bundle然后在bootstrap开始之前再发送另一个http请求,那么这种启动延迟非常明显.

原版的

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

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

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

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})]);
Run Code Online (Sandbox Code Playgroud)

  • 优秀的解决方案,对于像我这样的未来google的一些注意事项:1)注意`load()`必须返回一个`Promise`,而不是一个`Observable`.如果你像我一样在你的服务中使用Observables,请在这里使用`.toPromise()`函数.2)您可能想知道如何将`sites.load()`值检索到您的服务,组件等中.注意`SitesService`将它分配给`this.current`.所以你只需要将`SitesService`注入你的组件并检索它的`current`属性 (4认同)
  • 因此,您想从HTML阅读。您可以在服务器上添加一个脚本标签,将它们分配给一些全局变量`&lt;script&gt; function(){window.headers = someJson;。}()&lt;/ script&gt;`。不确定语法,我自己不太使用JS。这样,您根本就不必解析。 (2认同)
  • 但是,当我重新构建项目时,一个好的解决方案是出现以下错误:“错误中的错误遇到了静态解析符号值的问题。不支持函数调用。请考虑使用对已导出函数的引用替换函数或lambda(在位置24:46中原始.ts文件),在... / src / app / app.module.ts中解析符号AppModule”。我相信它指向useFactory中的lambda表达式。您如何将上述lambda转换为导出的函数?函数只是充当包装器吗? (2认同)
  • 使用AoT,您需要将(()=&gt; sites.load()`移到一个函数中(在类和装饰器的外部),然后在提供程序中将其替换为该函数名 (2认同)
  • @GünterZöchbauer谢谢,我按照您的建议尝试了,但是遇到了同样的错误。但是也许我也没有关注。您能否看一下我的问题:http://stackoverflow.com/questions/42998892/function-calls-are-not-supported-consider-replacing-the-function-or-lambda-with (2认同)

小智 30

在Angular2最终版本中,APP_INITIALIZER提供程序可用于实现您的需求.

我写了一个完整的例子: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 }
    ],
    ...
});
Run Code Online (Sandbox Code Playgroud)

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

如果要使用内置于库中的angular2对后端进行http调用,请确保在"导入"部分中设置"HttpModule".

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

        });
    }
}
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 在Angular 2.3.0中,出现错误:“未处理的承诺拒绝:appInits [i]不是函数;区域:&lt;root&gt;;任务:Promise.then;值:TypeError:appInits [i]不是function(…)TypeError:appInits [i]在新的ApplicationInitStatus上不是函数(在&lt;anonymous&gt;(https:// localhost:8080 / js / vendor.js:89:2),&lt;anonymous&gt;:3751:49处评估)的MyModuleInjector.createInternal(/MyModule/module.ngfactory.js:454:36)-看来加载返回的承诺也不能由useFactory函数返回。 (2认同)

And*_*ang 8

您可以创建并导出执行工作的函数,而不是让您的入口点本身调用bootstrap:

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

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

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