簡體   English   中英

Angular Universal 在呈現之前不等待 api/http 請求

[英]Angular Universal does not wait for api/http request before render

我有一個全新的 angular 通用項目,它似乎偽裝了所有 HTML(這很好)。 但是,我正在嘗試對我的 .NET 服務器進行 API 調用,這是一個帶有天氣預報 API 的標准 API 構建。

API 調用並且它工作得很好,但它只發生在我的 web 應用程序從預渲染切換到 csr 之后。 參見示例 1。

示例 1

在此處輸入圖像描述

如果我在頁面上禁用 javascript 這就是我得到的

在此處輸入圖像描述

這是 HTML 代碼

  <div style="padding: 5rem">
    <h1>TEST</h1>
    <h2>{{ this.SampleMessage }}</h2>
    <div *ngFor="let product of weather$ | async">
      <p>{{ this.product.summary }}</p>
    </div>
  </div>

應用程序組件.ts

import { AppService } from './app.service';
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  title = 'ModernaMediaAngular';
  text:string = "test"

  weather$: Observable<any>;
  SampleMessage="Example of Angular Async Pipe";    
  
  constructor(private as: AppService, ) {}

  async ngOnInit() {
    await this.getWeatherAsyncPipe();
    //non async
    this.as.getWeather().subscribe( res =>
      {
        this.text = res[0].date;
        console.log("got resolution");
        console.log(res);
        console.log(this.text);
      }
    );


  }
  public async getWeatherAsyncPipe() {    
        this.SampleMessage="Example of Angular Async Pipe";    
        this.weather$ = await this.as.getWeatherAsync();    
        console.log(this.weather$);
      }    
}

應用程序服務.ts

import { environment } from './../environments/environment';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AppService {
  public waeatherUrl = environment.url + '/api/weatherForecast/get'
  constructor(private http: HttpClient) { }

  getWeather() : Observable<object> {
    const headers = new HttpHeaders(
      {'Content-Type': 'application/json',
      'Access-Control-Allow-Origin' : 'http://localhost:4200'
    }
      );
    var x = this.http.get(this.waeatherUrl, {headers: headers}).pipe();
    console.log(x);
    return x;
  }

  public getWeatherAsync():Observable<any> {
    return this.http.get<any[]>(this.waeatherUrl);    
  }
}

應用程序模塊.ts

import { BrowserStateInterceptor } from './interceptors/browserstate.interceptor';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';

import { TransferHttpCacheModule } from '@nguniversal/common'

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'serverApp' }),
    TransferHttpCacheModule,
    AppRoutingModule,
    HttpClientModule,
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

應用程序服務器模塊

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

主.ts

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

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

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

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

每當預渲染嘗試從 API 獲取時,我也會得到我認為是 cors 的錯誤。

chunk {main} main.js, main.js.map (main) 66.3 kB [initial] [rendered]
chunk {polyfills} polyfills.js, polyfills.js.map (polyfills) 141 kB [initial] [rendered]
chunk {runtime} runtime.js, runtime.js.map (runtime) 6.15 kB [entry] [rendered]
chunk {styles} styles.css, styles.css.map (styles) 118 bytes [initial] [rendered]
chunk {vendor} vendor.js, vendor.js.map (vendor) 2.8 MB [initial] [rendered]
Date: 2021-03-15T14:26:38.903Z - Hash: a20337e49cf8941fcbf6 - Time: 12492ms
Hash: af451acd21594faf190e
Time: 19059ms
Built at: 2021-03-15 15:26:41
      Asset      Size  Chunks                          Chunk Names
    main.js  6.44 MiB    main  [emitted]        [big]  main
main.js.map  6.99 MiB    main  [emitted] [dev]         main
Entrypoint main [big] = main.js main.js.map
chunk {main} main.js, main.js.map (main) 6.08 MiB [entry] [rendered]

Compiled successfully.
** Angular Universal Live Development Server is listening on http://localhost:4200, open your browser on http://localhost:4200 **
Observable {
  _isScalar: false,
  source: Observable {
    _isScalar: false,
    source: Observable {
      _isScalar: false,
      source: [Observable],
      operator: [MergeMapOperator]
    },
    operator: FilterOperator { predicate: [Function], thisArg: undefined }
  },
  operator: MapOperator { project: [Function], thisArg: undefined }
}

Observable {
  _isScalar: false,
  source: Observable {
    _isScalar: false,
    source: Observable {
      _isScalar: false,
      source: [Observable],
      operator: [MergeMapOperator]
    },
    operator: FilterOperator { predicate: [Function], thisArg: undefined }
  },
  operator: MapOperator { project: [Function], thisArg: undefined }
}

ERROR HttpErrorResponse {
  headers: HttpHeaders {
    normalizedNames: Map {},
    lazyUpdate: null,
    headers: Map {}
  },
  status: 0,
  statusText: 'Unknown Error',
  url: 'https://localhost:5001/api/weatherForecast/get',
  ok: false,
  name: 'HttpErrorResponse',
  message: 'Http failure response for https://localhost:5001/api/weatherForecast/get: 0 Unknown Error',
  error: ProgressEvent {
    type: 'error',
    target: XMLHttpRequest {
      onloadstart: null,
      onprogress: null,
      onabort: null,
      onerror: null,
      onload: null,
      ontimeout: null,
      onloadend: null,
      _listeners: [Object],
      onreadystatechange: null,
      _anonymous: undefined,
      readyState: 4,
      response: null,
      responseText: '',
      responseType: 'text',
      responseURL: '',
      status: 0,
      statusText: '',
      timeout: 0,
      upload: [XMLHttpRequestUpload],
      _method: 'GET',
      _url: [Url],
      _sync: false,
      _headers: [Object],
      _loweredHeaders: [Object],
      _mimeOverride: null,
      _request: null,
      _response: null,
      _responseParts: null,
      _responseHeaders: null,
      _aborting: null,
      _error: null,
      _loadedBytes: 0,
      _totalBytes: 0,
      _lengthComputable: false
    },
    currentTarget: XMLHttpRequest {
      onloadstart: null,
      onprogress: null,
      onabort: null,
      onerror: null,
      onload: null,
      ontimeout: null,
      onloadend: null,
      _listeners: [Object],
      onreadystatechange: null,
      _anonymous: undefined,
      readyState: 4,
      response: null,
      responseText: '',
      responseType: 'text',
      responseURL: '',
      status: 0,
      statusText: '',
      timeout: 0,
      upload: [XMLHttpRequestUpload],
      _method: 'GET',
      _url: [Url],
      _sync: false,
      _headers: [Object],
      _loweredHeaders: [Object],
      _mimeOverride: null,
      _request: null,
      _response: null,
      _responseParts: null,
      _responseHeaders: null,
      _aborting: null,
      _error: null,
      _loadedBytes: 0,
      _totalBytes: 0,
      _lengthComputable: false
    },
    lengthComputable: false,
    loaded: 0,
    total: 0
  }
}

ERROR HttpErrorResponse {
  headers: HttpHeaders {
    normalizedNames: Map {},
    lazyUpdate: null,
    headers: Map {}
  },
  status: 0,
  statusText: 'Unknown Error',
  url: 'https://localhost:5001/api/weatherForecast/get',
  ok: false,
  name: 'HttpErrorResponse',
  message: 'Http failure response for https://localhost:5001/api/weatherForecast/get: 0 Unknown Error',
  error: ProgressEvent {
    type: 'error',
    target: XMLHttpRequest {
      onloadstart: null,
      onprogress: null,
      onabort: null,
      onerror: null,
      onload: null,
      ontimeout: null,
      onloadend: null,
      _listeners: [Object],
      onreadystatechange: null,
      _anonymous: undefined,
      readyState: 4,
      response: null,
      responseText: '',
      responseType: 'text',
      responseURL: '',
      status: 0,
      statusText: '',
      timeout: 0,
      upload: [XMLHttpRequestUpload],
      _method: 'GET',
      _url: [Url],
      _sync: false,
      _headers: [Object],
      _loweredHeaders: [Object],
      _mimeOverride: null,
      _request: null,
      _response: null,
      _responseParts: null,
      _responseHeaders: null,
      _aborting: null,
      _error: null,
      _loadedBytes: 0,
      _totalBytes: 0,
      _lengthComputable: false
    },
    currentTarget: XMLHttpRequest {
      onloadstart: null,
      onprogress: null,
      onabort: null,
      onerror: null,
      onload: null,
      ontimeout: null,
      onloadend: null,
      _listeners: [Object],
      onreadystatechange: null,
      _anonymous: undefined,
      readyState: 4,
      response: null,
      responseText: '',
      responseType: 'text',
      responseURL: '',
      status: 0,
      statusText: '',
      timeout: 0,
      upload: [XMLHttpRequestUpload],
      _method: 'GET',
      _url: [Url],
      _sync: false,
      _headers: [Object],
      _loweredHeaders: [Object],
      _mimeOverride: null,
      _request: null,
      _response: null,
      _responseParts: null,
      _responseHeaders: null,
      _aborting: null,
      _error: null,
      _loadedBytes: 0,
      _totalBytes: 0,
      _lengthComputable: false
    },
    lengthComputable: false,
    loaded: 0,
    total: 0
  }
}

但控制台中沒有錯誤:

在此處輸入圖像描述

在我的 .NET 5 項目的startup.cs中,我配置了 cors:

public void ConfigureServices(IServiceCollection services) {
    services.AddControllers();
    services.AddSwaggerGen(c => {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "ModernaMediaDotNet", Version = "v1" });
        });
    services.AddCors();
}

並在 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

app.UseRouting();
app.UseCors(x => x
            .AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader()
            .SetIsOriginAllowed(origin => true) // allow any origin
            );

我曾經在我們的測試環境中遇到過同樣的問題,這是由於我們使用的是自簽名證書,這是節點不喜歡的。

我們最終在server.ts中添加了以下行,以便節點禁用 TLS 驗證。

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

注意這是不安全的( 文檔),你不應該在產品中使用它(你應該有有效的證書)。

另一種選擇是僅在服務器端進行 http 調用,您可以使用HttpInterceptor來實現

您的應用程序可能需要將 API 調用包裝在區域宏任務中。

  1. npm 安裝@bespunky/angular-zen
  2. 導入RouterXModule.forRoot().forChild()根據您的模塊)。
  3. 讓您的組件/服務擴展RouteAware class。
  4. 在調用this.resolveInMacroTask()中包裝你的 observable。

您可以手動完成,但RouteAware會處理所有事情為您提供額外的好處。

更多的

文檔(參見Angular 通用部分)

使用RouteAware實時示例

我遇到了同樣的問題,在你的路由模塊中使用一個解析器,它會解決它,祝你編碼愉快!

  constructor(
    private activatedRoute: ActivatedRoute,
  ) {
    this.activatedRoute.data.subscribe(({ animes }) => {
      this.animeList = animes;
    });
  }

暫無
暫無

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

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