Angular 转移状态不阻止重复 http 调用
Angular Transfer State not preventing repeat http calls
我将 http 请求作为一项服务注入到我的组件并从那里订阅。由于我在我的应用程序中引入了 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 ALBUM_PHOTOS_KEY = makeStateKey('albumPhotos');
而且我还声明了 属性
albumNames: any;
我假设我已经正确地完成了所有导入我有 github in the gallery component 上的代码。
你走对了,如果你在服务器或浏览器端只执行一次而不是两次查询,你只需要以不同的方式处理你的服务。
伪逻辑:
- 如果服务器 -> 做 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));
});
}
}
更新
要使用此逻辑,您还需要:
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
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 永远不会在服务器端调用。此操作仅在浏览器端使用,一旦页面呈现,当用户单击特定操作时。因此,在特定情况下使用转移状态不是 correct/needed。
此外不确定您的服务中的 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 是在服务器端执行的,这意味着我在这里的第一个答案必须在这种情况下使用。
此外,还要注意在服务器端您无权访问 window,因此调用 $
使用 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);
}
}
我将 http 请求作为一项服务注入到我的组件并从那里订阅。由于我在我的应用程序中引入了 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 ALBUM_PHOTOS_KEY = makeStateKey('albumPhotos');
而且我还声明了 属性
albumNames: any;
我假设我已经正确地完成了所有导入我有 github in the gallery component 上的代码。
你走对了,如果你在服务器或浏览器端只执行一次而不是两次查询,你只需要以不同的方式处理你的服务。
伪逻辑:
- 如果服务器 -> 做 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));
});
}
}
更新
要使用此逻辑,您还需要:
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
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 永远不会在服务器端调用。此操作仅在浏览器端使用,一旦页面呈现,当用户单击特定操作时。因此,在特定情况下使用转移状态不是 correct/needed。
此外不确定您的服务中的 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 是在服务器端执行的,这意味着我在这里的第一个答案必须在这种情况下使用。
此外,还要注意在服务器端您无权访问 window,因此调用 $
使用 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);
}
}