Передача данных из ASP.NET Core в Typescript в приложении на основе Angular Cli

Я пытаюсь настроить SPA, где я могу передать данные из моего appsettings.json моей клиентской стороне на серверной визуализации. Я следовал инструкциям по настройке SSR с использованием новых шаблонов SpaServices.

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

Однако я не понимаю, как выполнить задачу, обозначенную здесь

Вы можете передать это другим частям вашего приложения любым способом, поддерживаемым Angular.

Я вижу пример, который используется base_url, но кажется, что база вводится в страницу как элемент 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 в приложение (я просматривал код, но он мне не понятен). Значение помещено в окно? Как мне прочитать значение, чтобы я мог добавить его в конструкторы компонентов?

2 ответа

Нашел ответ. Вы можете использовать официальный TransferState от Angular. Вам нужно будет изменить следующие файлы

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 чтобы не поднимать конфликт.

main.server.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.ts на TransferState

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

ВОТ И ВСЕ

Теперь вы можете использовать состояние передачи на любом компоненте, чтобы получить данные как

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

Поэтому я углубился в это и попытался заставить его работать, но, к сожалению, он не предназначен для работы, как ты, или я думал, что это должно работать. "Вы можете передать это другим частям вашего приложения любым способом, поддерживаемым Angular", что означает, что оно должно быть доступно для кода на стороне клиента либо с помощью глобального пространства имен, либо с помощью других средств (о которых я не могу думать). Когда приложение отображается через main.server.ts, вы можете встраивать переменные с сервера, но это не значит, что они доступны клиентскому приложению автоматически... на мой взгляд, это наполовину испеченное решение, и вы и я не единственный, кого это смущает (см. https://github.com/aspnet/JavaScriptServices/issues/1444).

Я рекомендую вам поискать другие способы включения данных на стороне сервера, например, службу конфигурации в загрузчике вашего приложения, которая получает конфигурацию при запуске, или встраивание объекта JSON в страницу хостинга Angular, написанную кодом на стороне сервера.

Другие вопросы по тегам