在 Angular 2 API 驱动的树视图中防止重复的 HTTP 请求
Prevent duplicate HTTP requests in Angular 2 API-driven tree-view
我有一个 Angular 2 应用程序,它调用 JSON API 将数据加载到嵌套的 HTML 列表 (<ol>
) 中。本质上是一个动态生成的树视图。由于来自 API 的数据集最终会变得非常大,因此当用户打开一个分支时,树会逐渐填充(用户打开一个分支,API 被调用时带有节点的 ID打开后,API returns 比节点的直接子节点的 JSON 提要,Angular 将返回的 JSON 绑定到一些新的 HTML <li>
个元素,树展开以显示新分支)。
您可以在此 Plunkr 中看到它的实际效果。它使用递归指令并且运行良好。 目前,因为我无法打开实际的 API 最多 public 请求,它只是调用静态 JSON 提要,因此每个节点的返回数据只是重复,但希望你明白了。
我现在要解决的问题是在分支关闭然后重新打开时防止无关的 HTTP 调用。阅读 HTTP client docs 后,我希望这会像修改订阅数据服务的方法一样简单,以将 .distinctUntilChanged()
方法链接到 app/content-list.component.ts
文件,如下所示:
getContentNodes() {
this._contentService.getContentNodes(this._startNodeId)
.distinctUntilChanged()
.subscribe(
contentNodes => this.contentNodes = contentNodes,
error => this.errorMessage = <any>error
);
}
但是,当我打开浏览器网络检查器时,每次重新打开相同的树分支时它仍然会调用 API。
谁能告诉我如何解决这个问题?
非常感谢。
编辑:
我正在尝试执行下面@Thierry Templier 的回答;缓存 API 返回的数据。所以内容服务现在是:
import {Injectable} from 'angular2/core';
import {Http, Response} from 'angular2/http';
import {Headers, RequestOptions} from 'angular2/http';
import {ContentNode} from './content-node';
import {Observable} from 'rxjs/Observable';
@Injectable()
export class ContentService {
constructor (private http: Http) {}
private _contentNodesUrl = 'app/content.json';
_cachedData: ContentNode[];
getContentNodes (parentId:number) {
if (this._cachedData) {
return Observable.of(this._cachedData);
} else {
return this.http.get(this._contentNodesUrl + parentId)
.map(res => <ContentNode[]> res.json())
.do(
(data) => {
this._cachedData = data;
})
.catch(this.handleError);
}
}
private handleError (error: Response) {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
但是,这里发生的事情是当页面加载时,this._cachedData
返回 false 并且 API 调用触发并使用返回值填充 this._cachedData
(data
),这是正确的。但是,当在网页UI中打开树节点时,以下行运行重复了数千次,直到最终浏览器崩溃:
return Observable.of(this._cachedData);
关于 Angular 2,我仍然处于起步阶段,所以非常感谢任何关于为什么这对我的学习没有帮助的指示。
我将使用以下方法缓存每个节点的数据:
getData() {
if (this.cachedData) {
return Observable.of(this.cachedData);
} else {
return this.http.get(...)
.map(res => res.json())
.do((data) => {
this.cachedData = data;
});
}
}
"problem"和distinctUntilChanged
是需要使用相同的数据流。但你的情况并非如此,因为你对不同的数据流执行了多个请求...
有关详细信息,请参阅此问题:
我想我参加派对有点晚了,但也许这会对某些人有所帮助。
一个好主意是还缓存正在进行的请求。所以创建两个缓存:
observableCache
dataCache
一个用例可能是我们动态创建 components
(即在 *ngFor
中)并且每个组件在其 ngInit
生命周期阶段进行数据调用的情况。
这样,即使第一个请求未完成,应用程序也不会对相同数据发出第二个请求,但会订阅现有的缓存 Observable。
getData() {
if (this.cachedData) {
console.log('data already available');
// if `cachedData` is available return it as `Observable`
return Observable.of(this.cachedData);
}
else if (this.observableCache) {
console.log('request pending');
// if `this.observableCache` is set then the request is in progress
// return the `Observable` for the ongoing request
return this.observableCache;
}
else {
console.log('send new request');
// set observableCache as new Observable returned by fetchData method
this.observableCache = this.fetchData();
}
return this.observableCache;
}
fetchData() {
const url = String('your endpoint url');
return this.http.get(url)
.map(rawData => {
// when the cached data is available we don't need the `Observable` reference
this.observableCache = null;
// set cachedData
this.dataCache = new Data(rawData);
return this.dataCache;
})
.share(); // critical, for multiple subscribings to the same observable
}
我有一个 Angular 2 应用程序,它调用 JSON API 将数据加载到嵌套的 HTML 列表 (<ol>
) 中。本质上是一个动态生成的树视图。由于来自 API 的数据集最终会变得非常大,因此当用户打开一个分支时,树会逐渐填充(用户打开一个分支,API 被调用时带有节点的 ID打开后,API returns 比节点的直接子节点的 JSON 提要,Angular 将返回的 JSON 绑定到一些新的 HTML <li>
个元素,树展开以显示新分支)。
您可以在此 Plunkr 中看到它的实际效果。它使用递归指令并且运行良好。 目前,因为我无法打开实际的 API 最多 public 请求,它只是调用静态 JSON 提要,因此每个节点的返回数据只是重复,但希望你明白了。
我现在要解决的问题是在分支关闭然后重新打开时防止无关的 HTTP 调用。阅读 HTTP client docs 后,我希望这会像修改订阅数据服务的方法一样简单,以将 .distinctUntilChanged()
方法链接到 app/content-list.component.ts
文件,如下所示:
getContentNodes() {
this._contentService.getContentNodes(this._startNodeId)
.distinctUntilChanged()
.subscribe(
contentNodes => this.contentNodes = contentNodes,
error => this.errorMessage = <any>error
);
}
但是,当我打开浏览器网络检查器时,每次重新打开相同的树分支时它仍然会调用 API。
谁能告诉我如何解决这个问题?
非常感谢。
编辑:
我正在尝试执行下面@Thierry Templier 的回答;缓存 API 返回的数据。所以内容服务现在是:
import {Injectable} from 'angular2/core';
import {Http, Response} from 'angular2/http';
import {Headers, RequestOptions} from 'angular2/http';
import {ContentNode} from './content-node';
import {Observable} from 'rxjs/Observable';
@Injectable()
export class ContentService {
constructor (private http: Http) {}
private _contentNodesUrl = 'app/content.json';
_cachedData: ContentNode[];
getContentNodes (parentId:number) {
if (this._cachedData) {
return Observable.of(this._cachedData);
} else {
return this.http.get(this._contentNodesUrl + parentId)
.map(res => <ContentNode[]> res.json())
.do(
(data) => {
this._cachedData = data;
})
.catch(this.handleError);
}
}
private handleError (error: Response) {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
但是,这里发生的事情是当页面加载时,this._cachedData
返回 false 并且 API 调用触发并使用返回值填充 this._cachedData
(data
),这是正确的。但是,当在网页UI中打开树节点时,以下行运行重复了数千次,直到最终浏览器崩溃:
return Observable.of(this._cachedData);
关于 Angular 2,我仍然处于起步阶段,所以非常感谢任何关于为什么这对我的学习没有帮助的指示。
我将使用以下方法缓存每个节点的数据:
getData() {
if (this.cachedData) {
return Observable.of(this.cachedData);
} else {
return this.http.get(...)
.map(res => res.json())
.do((data) => {
this.cachedData = data;
});
}
}
"problem"和distinctUntilChanged
是需要使用相同的数据流。但你的情况并非如此,因为你对不同的数据流执行了多个请求...
有关详细信息,请参阅此问题:
我想我参加派对有点晚了,但也许这会对某些人有所帮助。 一个好主意是还缓存正在进行的请求。所以创建两个缓存:
observableCache
dataCache
一个用例可能是我们动态创建 components
(即在 *ngFor
中)并且每个组件在其 ngInit
生命周期阶段进行数据调用的情况。
这样,即使第一个请求未完成,应用程序也不会对相同数据发出第二个请求,但会订阅现有的缓存 Observable。
getData() {
if (this.cachedData) {
console.log('data already available');
// if `cachedData` is available return it as `Observable`
return Observable.of(this.cachedData);
}
else if (this.observableCache) {
console.log('request pending');
// if `this.observableCache` is set then the request is in progress
// return the `Observable` for the ongoing request
return this.observableCache;
}
else {
console.log('send new request');
// set observableCache as new Observable returned by fetchData method
this.observableCache = this.fetchData();
}
return this.observableCache;
}
fetchData() {
const url = String('your endpoint url');
return this.http.get(url)
.map(rawData => {
// when the cached data is available we don't need the `Observable` reference
this.observableCache = null;
// set cachedData
this.dataCache = new Data(rawData);
return this.dataCache;
})
.share(); // critical, for multiple subscribings to the same observable
}