Angular 角度表:来自日期范围主题的管道可观察数据

Angular 角度表:来自日期范围主题的管道可观察数据,angular,typescript,rxjs,observable,Angular,Typescript,Rxjs,Observable,我有一个按预期工作的代码,但我怀疑代码是否错误,它只是由于一个未记录的特性意外工作,或者代码是正确的,它按设计预期工作 我想要的是一个物料日期范围选择器,它更新另一个组件表的数据源 我假设通过模板app.component.html <app-mytable ID="MyTable1" [Start]="rangeFormGroup.value.start" [End]="rangeFormGroup.value.e

我有一个按预期工作的代码,但我怀疑代码是否错误,它只是由于一个未记录的特性意外工作,或者代码是正确的,它按设计预期工作

我想要的是一个物料日期范围选择器,它更新另一个组件表的数据源

我假设通过模板
app.component.html

  <app-mytable ID="MyTable1" 
    [Start]="rangeFormGroup.value.start" 
    [End]="rangeFormGroup.value.end" ></app-mytable>
正在工作并且正确

现在在数据源中,我有两个日期/主题

export class MytableDataSource extends DataSource<MytableItem> {
  data: MytableItem[] = EXAMPLE_DATA;
  start: Subject<Date> = new Subject<Date>();
  end: Subject<Date> = new Subject<Date>();
即使我的测试表明结果是正确的,并且更改传播得很好,我也看不到代码中有什么可以保证对
\u start
\u end
的更新总是在传递到
FilterByDaterRange
之前完成


我认为括号内的是两个异步事件,因此第一个操作(
update=>…
在开始/结束的
subscribe
之后)和第二个操作(
this.filterByDateRange(…
merge
中开始/结束的
pipe()
之后)的顺序如何确实保证按正确的顺序发生,后者总是在前者之后?

我更确信,以下方法是正确的,用地图替换订阅者,如图1所示

您不能从subscribe返回可观察的,但如果您使用map 然后返回一个可观察对象,而不是subscribe

但是 由于_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效应,它需要(在函数式语言词典中)一个由缓存组成的
flatMap
(过时,它是
mergeMap
)(当日期范围过滤器包含在先前获取的数据中时)

let\u开始:日期
让我们结束:日期
返回合并(
this.paginator.page,this.sort.sortChange,
this.start.pipe(映射(更新=>{
返回{'start':update};})),
this.end.pipe(映射(更新=>{
返回{'end':update};})
.pipe(映射((合并)=>{
var mustLoad=false;
如果(合并中的“开始”){
if(merged.start){
如果(!_start ||_start.getTime()>合并的.start.getTime()){
mustLoad=true;
}
_start=merged.start;
}
}
如果(合并中的“结束”){
如果(合并。结束){
如果(!_end ||_end.getTime(){
如果(a.mustLoad){
如果(!this.database){
log(“数据库未初始化!”);
返回观察值([]);
}
log(“触发API调用”);
返回this.database.getBrokerFees(\u start,\u end);
}
返回observeof(a.cache);
})
).pipe(图a=>{
const filtered=this.filterByDaterRange(_start,_end,a);
this.data=已过滤;
const sorted=this.getSortedData(已过滤);
var paged=[…排序]//需要排序的副本
paged=this.getPagedData(paged);//paged内联工作并修改输入
返回页面;
}));
其他高阶算子 对于更高阶的RxJs映射操作符,请参见此内容:switchMap、mergeMap、concatMap(和defaustMap)

此外,既然你是,重要的是你要意识到:

表单中的每个控件(选择、输入、复选框等)都有一个
valueChanges
属性,我们可以订阅该属性,以便观察随时间的变化,并使用RxJS运算符以功能方式转换值

因此,如果您直接将可观察到的标准表单输入到合并映射中,我想您可以避免问题中的自定义日期主题,尽管您可能更喜欢已经定义的主题来实现特定的缓存策略

  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 ])));
    }));
  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 ])));
    }));
  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 ])));
    }));
  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;
      }));