簡體   English   中英

如何將后端渲染的參數傳遞給angular2 bootstrap方法

[英]How to pass parameters rendered from backend to angular2 bootstrap method

有沒有辦法將后端呈現的參數傳遞給 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'
      }
    ]
  }
};

更新2

Plunker 示例

更新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: [...]添加到providers: [...] of the root module 而不是bootstrap(...)

(尚未測試自己)。

更新

這里解釋了一種完全在 Angular 內部完成的有趣方法https://github.com/angular/angular/issues/9047#issuecomment-224075188

您可以使用APP_INITIALIZER它將在應用程序初始化時執行一個函數,並在該函數返回承諾時延遲它提供的內容。 這意味着應用程序可以在沒有太多延遲的情況下進行初始化,您還可以使用現有的服務和框架功能。

例如,假設您有一個多租戶解決方案,其中站點信息依賴於為其提供服務的域名。 這可以是 [name].letterpress.com 或與完整主機名匹配的自定義域。 我們可以通過使用APP_INITIALIZER來隱藏這是在 promise 背后的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 請求,則啟動延遲非常明顯。

原來的

您可以使用 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})]);

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

這些行將在您的應用程序啟動之前從 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);
            });

        });
    }
}

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

如果這能解決您的問題,請告訴我。

您可以創建並導出一個完成工作的函數,而不是讓您的入口點調用 bootstrap 本身:

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

你也可以把這個函數放在全局對象上,這取決於你的設置(webpack/SystemJS)。 它也與 AOT 兼容。

這有一個額外的好處,可以延遲引導,當它有意義時。 例如,當您在用戶填寫表單后通過 AJAX 調用檢索此用戶數據時。 只需使用此數據調用導出的引導程序函數即可。

唯一的方法是在定義提供者時提供這些值:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM