我有一些数据通过网络请求进入Angular应用程序的组件。这是observable
,看起来像这样:
this.streamFiltersService.getByFilters(
this.page - 1, this.pagesize, this.currentStage, this.language = filters['language'], this.location = filters['location'],
this.zipcode = filters['zip'], this.firstName = filters['firstName'], this.lastName = filters['lastName'],
this.branch = filters['branch'], fn);
这里有一个名为fn
的回调,它看起来像这样:
let fn = resRecordsData => {
this.records = resRecordsData;
};
我遇到的问题是,随着大量触发过滤器进入组件,我最终遇到了多个网络请求-对于用户来说,屏幕数据会改变多次。此外,由于这是异步的,因此有时最初的请求之一会最后返回,并且过滤器不会应用到用户看到的屏幕中。
完整功能如下:
public onFilterReceived(values)
{
let filters = {};
if (values) {
filters = values;
}
this.route.params.subscribe(
(params: any) => {
this.page = params['page'];
}
);
console.log('onFilterReceived() firing...');
let fn = resRecordsData => {
this.records = resRecordsData;
};
this.streamFiltersService.getByFilters(
this.page - 1, this.pagesize, this.currentStage, this.language = filters['language'], this.location = filters['location'],
this.zipcode = filters['zip'], this.firstName = filters['firstName'], this.lastName = filters['lastName'],
this.branch = filters['branch'], fn);
}
作为后续,当我在console.log
内的回调上放置onFilterReceived()
时,如下所示:
let fn = async resRecordsData => {
console.log('records: ', resRecordsData);
this.records = await resRecordsData;
};
打印到控制台的内容是这样的:
records: {ok: true, status: 200, statusText: "OK", data: Array(1), count: 1}
records: {ok: true, status: 200, statusText: "OK", data: Array(12), count: 115}
records: {ok: true, status: 200, statusText: "OK", data: Array(1), count: 1}
records: {ok: true, status: 200, statusText: "OK", data: Array(1), count: 1}
records: {ok: true, status: 200, statusText: "OK", data: Array(1), count: 1}
records: {ok: true, status: 200, statusText: "OK", data: Array(12), count: 115}
records: {ok: true, status: 200, statusText: "OK", data: Array(12), count: 115}
records: {ok: true, status: 200, statusText: "OK", data: Array(12), count: 115}
records: {ok: true, status: 200, statusText: "OK", data: Array(12), count: 115}
records: {ok: true, status: 200, statusText: "OK", data: Array(12), count: 115}
请注意,由于应用了过滤器,因此正确的值为Array(1)。如您所见,由于这些调用是异步的,因此它们会无序返回。我理想的情况是一个电话,结果如下:
records: {ok: true, status: 200, statusText: "OK", data: Array(1), count: 1}
基于以下建议,我尝试将建议的运算符链接在onFilterReceived()
的回调中,如下所示:
public async onFilterReceived(values)
{
let filters = {};
if (values) {
filters = values;
}
this.route.params.subscribe(
(params: any) => {
this.page = params['page'];
}
);
console.log('onFilterReceived() firing...');
let fn = async resRecordsData => {
await resRecordsData
.distinctUntilChanged()
.debounceTime(1000)
.switchMap( resRecordsData => {
this.records = resRecordsData;
});
console.log('records: ', this.records);
};
this.streamFiltersService.getByFilters(
this.page - 1, this.pagesize, this.currentStage, this.language = filters['language'], this.location = filters['location'],
this.zipcode = filters['zip'], this.firstName = filters['firstName'], this.lastName = filters['lastName'],
this.branch = filters['branch'], fn);
}
...但是我最终遇到一个错误:
error_handler.js:60错误:未被捕获(承诺):TypeError: resRecordsData.distinctUntilChanged不是函数
答案 0 :(得分:2)
因此,如果我正确理解您的问题:
this.records
)这与经常重复的autocomplete example with rxjs类似。
您将需要的代码如下:
$filterChanges
.distinctUntilchanged()
.debounceTime(1000)
.switchMap(filters => getByFilters(.....))
.subscribe(records => {
// modify global state with the records
});
因此,您首先要获取过滤器及其更改的流-例如{ page, pagesize, currentStage, language }
。您可能必须在过滤器对象的compareFunc
上添加distinctUntilChanged
,因为它是非原始的。
下面是一个粗略的非角度实现示例,可以帮助您:
/* make our html filter elements observable */
var carTypeFilter = document.getElementById('cars');
var hornsCheckbox = document.getElementById('horns');
var $carsFilter = Rx.Observable.fromEvent(carTypeFilter, 'change')
.map(evt => ({ filter: 'car', value: evt.target.value }));
var $hornsFilter = Rx.Observable.fromEvent(hornsCheckbox, 'change')
.map(evt => ({ filter: 'horns', value: evt.target.checked }));
/* we want to have one value containing the current logical grouping of filters */
var $filtersCombined = Rx.Observable.merge($carsFilter,$hornsFilter)
.scan((allCurrentFilters, currFilterItem) => {
allCurrentFilters[currFilterItem.filter] = currFilterItem.value;
return allCurrentFilters;
}, {});
var $debouncedFilterChanges = $filtersCombined
/* for distinctUntilChanged to work
you would need to have a compareFunction to evaluate
if all the key:value pairs of the filters are the same
not going to do that in this example; it will only filter out
filter sets after franctic clicking but only result in an additional
request being done to the backend */
// .distinctUntilChanged()
/* for every distinct filterSetChange debounce it so
we know that the user has stopped fiddling with the inputs */
.debounceTime(500);
var $filteredServerResults = $debouncedFilterChanges
.switchMap(filters => getFilteredData(filters.car, filters.horns));
$filteredServerResults.subscribe(data => {
document.getElementById('results').innerText = JSON.stringify(data);
});
/*
mock function which simulates doing async call to server with slow respons
*/
function getFilteredData(car, horns){
//note that the car or horns can be undefined
return Rx.Observable.of(`retrieved values for: ${car}-${horns}`)
.delay(1500); // mimic slow response, user can have already asked newer results
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.12/Rx.js"></script>
</head>
<body>
<div>
<select id="cars">
<option value="">Select one...</option>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
<label for="cars">Car</label>
</div>
<div>
<input type="checkbox" id="horns" name="feature"
value="horns" />
<label for="horns">HasHorns</label>
</div>
<h1>results...</h1>
<pre id="results"></pre>
</body>
</html>