角度传输状态不阻止重复http调用

时间:2017-12-24 11:08:08

标签: javascript angular angular-universal ssr

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

我有点击时调用的方法,它向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

1 个答案:

答案 0 :(得分:5)

如果你在服务器或浏览器端只执行一次而不是两次执行查询,那么你就是正确的通行证,只需要以不同的方式处理你的服务。

伪逻辑:

  • 如果服务器 - >做http请求 - >在transfer-state中设置值
  • 如果浏览器 - >从转移状态中获取价值

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

@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中初始化)。
  2.   

    TransferHttpCacheModule安装一个避免使用的Http拦截器   对于客户端的请求,重复HttpClient请求   在服务器端呈现应用程序时已经完成。

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

      服务器端的
    1. ServerTransferStateModule和客户端的BrowserTransferStateModule使用TransferState
    2. https://angular.io/api/platform-browser/TransferState

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

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