Angular Table:从日期范围主题管道可观察到的数据
Angular Table: piping observable data from a date range subject
我有一个按预期工作的代码,但我怀疑代码是否错误,它只是由于未记录的功能而意外工作,或者代码是正确的,并且按设计预期工作。
我想要的是 material 日期范围选择器更新另一个组件的 table.
的数据源
我假设通过模板传递值的部分 app.component.html
<app-mytable ID="MyTable1"
[Start]="rangeFormGroup.value.start"
[End]="rangeFormGroup.value.end" ></app-mytable>
然后将这些值从 table mytable.component.ts
转发到 mytable-datasource.ts
private _Start: Date | undefined;
@Input()
public get Start(): Date | undefined {
return this._Start;
}
public set Start(value: Date | undefined) {
this._Start = value;
this.dataSource.start.next(value);
}
private _End: Date | undefined;
@Input()
public get End(): Date | undefined {
return this._End;
}
public set End(value: Date | undefined) {
this._End = value;
this.dataSource.end.next(value);
}
工作正常。
现在在数据源中我有 2 dates/subject
export class MytableDataSource extends DataSource<MytableItem> {
data: MytableItem[] = EXAMPLE_DATA;
start: Subject<Date> = new Subject<Date>();
end: Subject<Date> = new Subject<Date>();
最后是我的疑问和问题,都是关于我触发更改的部分。
let _start : Date
let _end : Date
this.start.subscribe(update => _start = update);
this.end.subscribe(update => _end = update);
return merge(
observableOf(this.data) ,this.paginator.page, this.sort.sortChange,
this.start.pipe(), this.end.pipe())
.pipe(map(() => {
console.log('merge piped');
return this.getPagedData(
this.getSortedData(
this.filterByDateRange(_start, _end,
[...this.data ])));
}));
即使我的测试表明结果正确并且更改传播良好,我也看不到代码中是什么保证 _start
和 _end
的更新总是在它们之前完成传递给 filterByDateRange
.
我认为括号内的那些是两个异步事件,因此第一个动作(update => ...
在 start/end 的 subscribe
之后)和第二个动作的顺序如何动作(this.filterByDateRange(...
在 merge
中的 start/end 的 pipe()
之后)确实保证以正确的顺序发生,后者总是在前者之后?
我更相信下面的方法是用地图替换订阅者的正确方法,如 中所述。
You can't return an observable from subscribe but if you use map
instead of subscribe then an Observable is returned.
let _start : Date
let _end : Date
return merge(
// observableOf(this.data) ,
// BTW you don't need this above
this.paginator.page, this.sort.sortChange,
this.start.pipe(map(update => {
_start = update;
return update;})),
this.end.pipe(map(update => {
_end = update;
return update;})))
.pipe(map(() => {
console.log('merge piped');
return this.getPagedData(
this.getSortedData(
this.filterByDateRange(_start, _end,
[...this.data ])));
}));
但是
由于 _start/_end 更新是副作用,我认为最好让它们只发生在独特的最终合并阶段:
let _start : Date
let _end : Date
return merge(
this.paginator.page, this.sort.sortChange,
this.start.pipe(map(update => {
return {'start':update};})),
this.end.pipe(map(update => {
return {'end':update};})))
.pipe(map((merged) => {
if ('start' in merged ) {
_start = merged.start;
}
if ('end' in merged ) {
_end = merged.end;
}
console.log('merge piped');
return this.getPagedData(
this.getSortedData(
this.filterByDateRange(_start, _end,
[...this.data ])));
}));
有损背压
映射可能比原始订阅者更好的另一个原因是应用去抖动(对用户输入),这样消费者(可能 运行 查询)就不会不知所措(但这超出了范围这个问题)。
IO 效果
另一个真实世界的例子是 http IO Effect,它需要(在函数式语言字典中)一个 flatMap
(过时的,它是 mergeMap
) 与缓存组成(当日期范围过滤器包含在先前获取的数据中时)
let _start : Date
let _end : Date
return merge(
this.paginator.page, this.sort.sortChange,
this.start.pipe(map(update => {
return {'start':update};})),
this.end.pipe(map(update => {
return {'end':update};})))
.pipe(map((merged) => {
var mustLoad = false;
if ('start' in merged ) {
if (merged.start) {
if (!_start || _start.getTime() > merged.start.getTime()) {
mustLoad = true;
}
_start = merged.start;
}
}
if ('end' in merged ) {
if (merged.end) {
if (!_end || _end.getTime() < merged.end.getTime()) {
mustLoad = true;
}
_end = merged.end;
}
}
if (mustLoad && _start && _end) {
return {"cache":[], "mustLoad":true}
} else {
mustLoad = false;
}
return {"cache":this.data, "mustLoad":false};
})).pipe(mergeMap ((a) => {
if (a.mustLoad) {
if (!this.database) {
console.log("Database not initialized!");
return observableOf([]);
}
console.log('firing API call');
return this.database.getBrokerFees(_start,_end);
}
return observableOf( a.cache);
})
).pipe(map(a => {
const filtered = this.filterByDateRange(_start, _end, a);
this.data = filtered;
const sorted = this.getSortedData(filtered);
var paged = [...sorted] // needs copy of sorted
paged = this.getPagedData(paged); // paged works inline and modify input
return paged;
}));
其他高阶运算符
另请参阅此 Comprehensive Guide 到高阶 RxJs 映射运算符:switchMap、mergeMap、concatMap(和 exhaustMap)。
此外,由于您是 dealing with Reactive Forms,因此您必须认识到:
Every control in the form (select, input, checkbox, etc…) has a valueChanges
property, an Observable that we can subscribe to in order to observe changes over time and transform values with RxJS operators in a functional way.
因此,我想如果您直接将可观察到的标准形式输入到合并映射中,您可以避免问题中的自定义日期主题,尽管您可能更喜欢已经定义的主题来实现您的特定缓存策略。
我有一个按预期工作的代码,但我怀疑代码是否错误,它只是由于未记录的功能而意外工作,或者代码是正确的,并且按设计预期工作。
我想要的是 material 日期范围选择器更新另一个组件的 table.
的数据源我假设通过模板传递值的部分 app.component.html
<app-mytable ID="MyTable1"
[Start]="rangeFormGroup.value.start"
[End]="rangeFormGroup.value.end" ></app-mytable>
然后将这些值从 table mytable.component.ts
转发到 mytable-datasource.ts
private _Start: Date | undefined;
@Input()
public get Start(): Date | undefined {
return this._Start;
}
public set Start(value: Date | undefined) {
this._Start = value;
this.dataSource.start.next(value);
}
private _End: Date | undefined;
@Input()
public get End(): Date | undefined {
return this._End;
}
public set End(value: Date | undefined) {
this._End = value;
this.dataSource.end.next(value);
}
工作正常。
现在在数据源中我有 2 dates/subject
export class MytableDataSource extends DataSource<MytableItem> {
data: MytableItem[] = EXAMPLE_DATA;
start: Subject<Date> = new Subject<Date>();
end: Subject<Date> = new Subject<Date>();
最后是我的疑问和问题,都是关于我触发更改的部分。
let _start : Date
let _end : Date
this.start.subscribe(update => _start = update);
this.end.subscribe(update => _end = update);
return merge(
observableOf(this.data) ,this.paginator.page, this.sort.sortChange,
this.start.pipe(), this.end.pipe())
.pipe(map(() => {
console.log('merge piped');
return this.getPagedData(
this.getSortedData(
this.filterByDateRange(_start, _end,
[...this.data ])));
}));
即使我的测试表明结果正确并且更改传播良好,我也看不到代码中是什么保证 _start
和 _end
的更新总是在它们之前完成传递给 filterByDateRange
.
我认为括号内的那些是两个异步事件,因此第一个动作(update => ...
在 start/end 的 subscribe
之后)和第二个动作的顺序如何动作(this.filterByDateRange(...
在 merge
中的 start/end 的 pipe()
之后)确实保证以正确的顺序发生,后者总是在前者之后?
我更相信下面的方法是用地图替换订阅者的正确方法,如
You can't return an observable from subscribe but if you use map instead of subscribe then an Observable is returned.
let _start : Date
let _end : Date
return merge(
// observableOf(this.data) ,
// BTW you don't need this above
this.paginator.page, this.sort.sortChange,
this.start.pipe(map(update => {
_start = update;
return update;})),
this.end.pipe(map(update => {
_end = update;
return update;})))
.pipe(map(() => {
console.log('merge piped');
return this.getPagedData(
this.getSortedData(
this.filterByDateRange(_start, _end,
[...this.data ])));
}));
但是
由于 _start/_end 更新是副作用,我认为最好让它们只发生在独特的最终合并阶段:
let _start : Date
let _end : Date
return merge(
this.paginator.page, this.sort.sortChange,
this.start.pipe(map(update => {
return {'start':update};})),
this.end.pipe(map(update => {
return {'end':update};})))
.pipe(map((merged) => {
if ('start' in merged ) {
_start = merged.start;
}
if ('end' in merged ) {
_end = merged.end;
}
console.log('merge piped');
return this.getPagedData(
this.getSortedData(
this.filterByDateRange(_start, _end,
[...this.data ])));
}));
有损背压
映射可能比原始订阅者更好的另一个原因是应用去抖动(对用户输入),这样消费者(可能 运行 查询)就不会不知所措(但这超出了范围这个问题)。
IO 效果
另一个真实世界的例子是 http IO Effect,它需要(在函数式语言字典中)一个 (过时的,它是 flatMap
mergeMap
) 与缓存组成(当日期范围过滤器包含在先前获取的数据中时)
let _start : Date
let _end : Date
return merge(
this.paginator.page, this.sort.sortChange,
this.start.pipe(map(update => {
return {'start':update};})),
this.end.pipe(map(update => {
return {'end':update};})))
.pipe(map((merged) => {
var mustLoad = false;
if ('start' in merged ) {
if (merged.start) {
if (!_start || _start.getTime() > merged.start.getTime()) {
mustLoad = true;
}
_start = merged.start;
}
}
if ('end' in merged ) {
if (merged.end) {
if (!_end || _end.getTime() < merged.end.getTime()) {
mustLoad = true;
}
_end = merged.end;
}
}
if (mustLoad && _start && _end) {
return {"cache":[], "mustLoad":true}
} else {
mustLoad = false;
}
return {"cache":this.data, "mustLoad":false};
})).pipe(mergeMap ((a) => {
if (a.mustLoad) {
if (!this.database) {
console.log("Database not initialized!");
return observableOf([]);
}
console.log('firing API call');
return this.database.getBrokerFees(_start,_end);
}
return observableOf( a.cache);
})
).pipe(map(a => {
const filtered = this.filterByDateRange(_start, _end, a);
this.data = filtered;
const sorted = this.getSortedData(filtered);
var paged = [...sorted] // needs copy of sorted
paged = this.getPagedData(paged); // paged works inline and modify input
return paged;
}));
其他高阶运算符
另请参阅此 Comprehensive Guide 到高阶 RxJs 映射运算符:switchMap、mergeMap、concatMap(和 exhaustMap)。
此外,由于您是 dealing with Reactive Forms,因此您必须认识到:
Every control in the form (select, input, checkbox, etc…) has a
valueChanges
property, an Observable that we can subscribe to in order to observe changes over time and transform values with RxJS operators in a functional way.
因此,我想如果您直接将可观察到的标准形式输入到合并映射中,您可以避免问题中的自定义日期主题,尽管您可能更喜欢已经定义的主题来实现您的特定缓存策略。