javascript - Angular 传输状态不会阻止重复的 http 调用

标签 javascript angular angular-universal server-side-rendering

我将 http 请求作为一项服务注入(inject)到我的组件中并从那里订阅。由于我在我的应用程序中引入了具有 Angular 通用的服务器端渲染,页面上的结果至少重复了两次。

我有一个在点击时调用的方法,它向 facebook 的 api 执行 http 请求

getAlbum(albumId: number) {

    this.albumPhotos = this.state.get(ALBUM_PHOTOS_KEY, null as any);

    if (!this.albumPhotos) {
      this.facebookService.getBachadiffAlbumPhotos(albumId).subscribe(res => {
        this.bachataPicsArray = res;
        this.state.set(ALBUM_PHOTOS_KEY, res as any);
      });
    }
  }

我在导入下面声明了 const 变量

const ALBUM_PHOTOS_KEY = makeStateKey('albumPhotos');

而且我还声明了属性

albumNames: any;

我假设我已经正确地完成了所有的导入我在 github in the gallery component 上有代码.

enter image description here

最佳答案

您走对了路,如果您在服务器端或浏览器端只执行一次查询而不是两次,则只需要以不同方式处理您的服务。

伪逻辑:

  • 如果服务器 -> 做http请求 -> 在传输状态设置值
  • 如果浏览器 -> 从传输状态获取值

为此,您可以像下面这样增强您的服务:

@Injectable()
export class FacebookEventsService {
    const ALBUM_PHOTOS_KEY: StateKey<number>;

    constructor(@Inject(PLATFORM_ID) private platformId: Object, private http: HttpClient) {
       this.ALBUM_PHOTOS_KEY = makeStateKey('albumPhotos');
    } 

    getBachaDiffFacebookEvents(): Observable<CalendarEvent[]> {
        // Here we check if server or browser side
        if (isPlatformServer(this.platformId)) {
            return this.getServerBachaDiffFacebookEvents();
        } else {
            return this.getBrowserBachaDiffFacebookEvents();
        }
    }

    getServerBachaDiffFacebookEvents(): Observable<CalendarEvent[]> {

           return this.http.get(this.facebookEventsUrl)
             .map(res => {

                  // Here save also result in transfer-state
                  this.transferState.set(ALBUM_PHOTOS_KEY, calendarEvents);

             });
     }


        getBrowserBachaDiffFacebookEvents(): Observable<CalendarEvent[]> {
           return new Observable(observer => {
            observer.next(this.transferState.get(ALBUM_PHOTOS_KEY, null));
          });
     }
}

更新

要使用此逻辑,您还需要:

  1. TransferHttpCacheModule(在 app.module.ts 中初始化)。

TransferHttpCacheModule installs a Http interceptor that avoids duplicate HttpClient requests on the client, for requests that were already made when the application was rendered on the server side.

https://github.com/angular/universal/tree/master/modules/common

  1. ServerTransferStateModule 在服务器端和 BrowserTransferStateModule 在客户端使用 TransferState

https://angular.io/api/platform-browser/TransferState

P.S.:请注意,如果您这样做并增强您的服务器,当然您将不再需要在上面显示的 getAlbum() 方法中设置传输状态的值

更新 2

如果您想像在 gallery.component.ts 中那样处理服务器端和浏览器端,您可以执行如下操作:

getAlbum(albumId: number) {

    if (isPlatformServer(this.platformId)) {

      if (!this.albumPhotos) {
        this.facebookService.getBachadiffAlbumPhotos(albumId).subscribe(res => {
          this.bachataPicsArray = res;
          this.state.set(ALBUM_PHOTOS_KEY, null);
        });
      }
    } else {

      this.albumPhotos = this.state.get(ALBUM_PHOTOS_KEY,null);
    }

  }

更新 3

事实是,服务器端永远不会调用您的操作 getAlbum。此操作仅在浏览器端使用,一旦页面呈现,当用户单击特定操作时。因此,在特定情况下使用转移状态是不正确/不需要的。

此外不确定您的服务中的 Observable 是否已正确订阅。

这里要改变什么才能让它运行:

gallery.component.ts

getAlbum(albumId: number) {

    this.facebookService.getBachadiffAlbumPhotos(albumId).subscribe(res => {
        this.albumPhotos = res;
    });
  }

facebook-events.service.ts

getBachadiffAlbumPhotos(albumId: number): Observable<Object> {

    this.albumId = albumId;
    this.facebookAlbumPhotosUrl =    `https://graph.facebook.com/v2.11/${this.albumId}/photos?limit=20&fields=images,id,link,height,width&access_token=${this.accessToken}`;

    return    Observable.fromPromise(this.getPromiseBachaDiffAlbumPhotos(albumId));
  }

private getPromiseBachaDiffAlbumPhotos(albumId: number): Promise<{}> {
    return new Promise((resolve, reject) => {
      this.facebookAlbumPhotosUrl = `https://graph.facebook.com/v2.11/${this.albumId}/photos?limit=20&fields=images,id,link,height,width&access_token=${this.accessToken}`;

      let facebookPhotos: FacebookPhoto[] = new Array();
      let facebookPhoto: FacebookPhoto;

      const params: HttpParams = new HttpParams();
      this.http.get(this.facebookAlbumPhotosUrl, {params: params})
        .subscribe(res => {

          let facebookPhotoData = res['data'];

          for (let photo of facebookPhotoData) {

            facebookPhotos.push(
              facebookPhoto = {
                id: photo.id,
                image: photo.images[3].source,
                link: photo.link,
                height: photo.height,
                width: photo.width
              });
          }

          resolve(facebookPhotos);
        }, (error) => {
          reject(error);
        });
    });
  }

更新 4

ngOnInit 在服务器端执行,这意味着我在这里的第一个答案必须在这种情况下使用。

此外,还要注意在服务器端您无权访问该窗口,因此调用 $

使用 gallery.component.ts 你可以做这样的事情来只运行一次 http 查询,但这并不能解决你所有的问题,我认为它仍然需要进一步改进。

ngOnInit() {
    if (isPlatformServer(this.platformId)) {
      this.facebookService.getBachadiffFacebookVideos().subscribe(res => {
        this.bachataVidsArray = res;
        this.state.set(VIDEOS_KEY, res as any);
      });


  this.facebookService.getBachadiffFacebookLastClassPictures().subscribe(res => {
        this.bachataPicsArray = res;
        this.state.set(LAST_CLASS_PICTURES_KEY, res as any);
      });

      this.facebookService.getBachadiffAlbumNames().subscribe(res => {
        this.bachataAlbumHeaderNames = res;
        this.state.set(ALBUM_NAMES_KEY, res as any);
      });
    } else {
      $('ul.tabs').tabs();

      this.bachataVidsArray = this.state.get(VIDEOS_KEY, null as any);
      this.bachataPicsArray = this.state.get(LAST_CLASS_PICTURES_KEY, null as any);
      this.bachataAlbumHeaderNames = this.state.get(ALBUM_NAMES_KEY, null as any);
    }
  }

关于javascript - Angular 传输状态不会阻止重复的 http 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47960049/

相关文章:

javascript - 刚刚使用 JavaScript 对象上的 console.log 评估了值

javascript - VUE - 数据对象总是作为引用传递还是有时被复制?

javascript - angular2-in-memory-web-api 404 错误

angular - 根据文件上传百分比显示 mat-spinner

angular - 服务器端渲染不等待 API 调用完成

angular - 服务器端渲染在 iis 中的 Angular 通用中不起作用

javascript - 悬停工具提示显示详细 jquery

javascript - 在皇家 slider 中添加视频

javascript - Json Pipe - 无法将字段设置为空

angular - 如何在我的 Angular 12 应用程序中实现 firebase app-check