5

我正在尝试设置一个 SPA,我可以在其中将数据从我的 appsettings.json 传递到服务器渲染上的客户端。我按照步骤使用新的 SpaServices 模板配置 SSR

https://docs.microsoft.com/en-us/aspnet/core/spa/angular

但是我不明白如何完成此处标记的任务

您可以通过 Angular 支持的任何方式将其传递给应用程序的其他部分

我看到使用的示例是 base_url,但似乎 base 作为 DOM 元素注入页面

 <base href='/'>

但目前尚不清楚如何以这种方式阅读其他项目。我正在测试是否通过应用程序是 Https,我的 Startup.cs 中有这一部分

options.SupplyData = (context, data) =>
{
    data["isHttpsRequest"] = context.Request.IsHttps;
};

在 main.server.ts

 { provide: 'isHttps', useValue: params.data.isHttpsRequest }

但在 main.ts 中我迷路了,我有这样的东西

export function getBaseUrl() {
  return document.getElementsByTagName('base')[0].href;
}

export function getIsHttps() {
  // NO idea what needs to go here
  return "";
}

const providers = [
  { provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] },
  { provide: 'isHttps', useFactory: getIsHttps, deps: [] }
];

我不确定 SpaServices 如何将 Prerender 上的值注入应用程序(我查看了代码,但我不清楚)。价值是放在窗户上的吗?如何读取值以便可以注入组件的构造函数?

4

2 回答 2

2

找到了答案。您可以使用 Angular 的官方TransferState。您需要修改以下文件

app.server.module.ts (添加 ServerTransferStateModule)

import { NgModule } from '@angular/core';
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';

import { AppModule } from './app.module';
import { AppComponent } from './app.component';

@NgModule({
  imports: [
    AppModule,
    ServerModule,
    ServerTransferStateModule
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule { }

app.module.ts (添加 BrowserTransferStateModule)

import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'universal-demo-v5' }),
    HttpClientModule,
    BrowserTransferStateModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

这添加了使用 TransferState 所需的模块。

然后,您需要提供 dotnet 服务器发送的值main.server.ts并添加一个虚拟提供程序,main.ts以免引发冲突。

主服务器.ts

import "zone.js/dist/zone-node";
import "reflect-metadata";
import { renderModule, renderModuleFactory } from "@angular/platform-server";
import { APP_BASE_HREF } from "@angular/common";
import { enableProdMode } from "@angular/core";
import { provideModuleMap } from "@nguniversal/module-map-ngfactory-loader";
import { createServerRenderer } from "aspnet-prerendering";
export { AppServerModule } from "./app/app.server.module";

enableProdMode();

export default createServerRenderer(params => {
  const { AppServerModule, AppServerModuleNgFactory, LAZY_MODULE_MAP } = (module as any).exports;

  const options = {
    document: params.data.originalHtml,
    url: params.url,
    extraProviders: [
      provideModuleMap(LAZY_MODULE_MAP),
      { provide: APP_BASE_HREF, useValue: params.baseUrl },
      { provide: "BASE_URL", useValue: params.origin + params.baseUrl},

      //Added provider, ServerParams is the data sent on Startup.cs
      { provide: "SERVER_PARAMS", useValue: params.data.ServerParams }
    ]
  };

  const renderPromise = AppServerModuleNgFactory
    ? /* AoT */ renderModuleFactory(AppServerModuleNgFactory, options)
    : /* dev */ renderModule(AppServerModule, options);

  return renderPromise.then(html => ({ html }));
});

main.ts (仅在 DOM 完成加载时添加虚拟提供程序和引导应用程序。这将使我们有时间在 TransferState 上完全保存对象)

import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";

import { AppModule } from "./app/app.module";
import { environment } from "./environments/environment";

export function getBaseUrl() {
  return document.getElementsByTagName("base")[0].href;
}

const providers = [
  { provide: "BASE_URL", useFactory: getBaseUrl, deps: [] },
  { provide: "SERVER_PARAMS", useValue: "" }
];

if (environment.production) {
  enableProdMode();
}

document.addEventListener("DOMContentLoaded", () => {
  platformBrowserDynamic(providers)
    .bootstrapModule(AppModule)
    .catch(err => console.log(err));
});

最后,我们将服务器提供的对象保存在app.component.tsTransferState 上

app.component.ts

import { Component, Inject } from "@angular/core";
import { TransferState, makeStateKey } from "@angular/platform-browser";

const SERVER_PARAMS = makeStateKey("serverParams");
@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  serverParams: any;
  // Provider set on main.ts and main.server.ts
  constructor(@Inject("SERVER_PARAMS") private stateInj: string, private state: TransferState) {
    this.serverParams = this.state.get(SERVER_PARAMS, null as any);

    if (!this.serverParams) {
      this.state.set(SERVER_PARAMS, stateInj as any);
    }
  }
}

而已

现在您可以在任何组件上使用 Transfer state 来获取数据

home.component.ts

import { Component, Inject } from "@angular/core";
import { TransferState, makeStateKey } from "@angular/platform-browser";

const SERVER_PARAMS = makeStateKey("serverParams");

@Component({
  selector: "app-home",
  templateUrl: "./home.component.html"
})
export class HomeComponent {
  serverParameter = "PLACEHOLDER";
  constructor(public state: TransferState) {}

  ngOnInit() {
    this.serverParameter = this.state.get(SERVER_PARAMS, "");
  }
}

于 2018-11-02T15:54:06.260 回答
0

所以我深入研究并试图让它工作,但不幸的是它并不像你一样工作,或者我认为它应该工作。“您可以以 Angular 支持的任何方式将其传递给应用程序的其他部分”意味着客户端代码需要通过使用全局命名空间或其他方式(我想不出)来访问它。当应用程序通过 main.server.ts 呈现时,您可以从服务器嵌入变量,但这并不意味着它们可以自动提供给客户端应用程序......在我看来,这是一种半生不熟的解决方案,而您我不是唯一对此感到困惑的人(请参阅https://github.com/aspnet/JavaScriptServices/issues/1444)。

我建议您寻找包含服务器端数据的其他方法,例如应用程序引导程序上的配置服务,该服务在启动时获取配置,或者在由服务器端代码编写的 Angular 托管页面中嵌入 JSON 对象。

于 2018-03-16T23:20:17.010 回答