使用反应性扩展定期保存更​​改

时间:2018-06-21 17:57:55

标签: angular rxjs reactivex

我有一个Angular应用,其中的mat-table非常大。该表的大多数单元都是mat-select组件(来自Angular Material),并且当mat-select中的值更改时,需要保存该值,但要遵循以下规则:

  1. 每7秒保存一次自上次保存以来所做的所有更改。
  2. 如果认为一行已完成,请在那一刻触发保存,并保存自上次保存以来所做的所有更改。

我的攻击计划是这样的:

  1. 在任何selectionChange中的任何mat-select上,将整个数据对象发射到Subject上。
  2. 订阅该Subject,并通过以下管道运行它:
    • 根据上述条件缓冲可观察的流
    • 过滤掉空数组(缓冲区窗口中未发出任何内容)
    • 将整个更改数组发布到服务器。服务器知道如何处理它。
  3. 缓冲区条件本身就是一个Observable,它在发出任何内容时都会触发缓冲区。
    • 我希望它在7秒钟过去或数据行完成时发出。因此,我使用combineLatest组合了两个Observable,interval(7000)Subject要保存的数据,并对其进行过滤以仅获取完整的数据。并只取其中一个发出的最后一个。

这是我所做的(简化):

my.component.html

  <mat-table>
  <ng-container matColumnDef='someData'>
    <mat-header-cell *matHeaderCellDef>Some Data</mat-header-cell>
    <mat-cell *matCellDef='let row'>
      <mat-form-field>
        <mat-select [(ngModel)]='row.someData' (selectionChange)='dataToSave.next(row)'>
          <mat-option [value]='3'>High</mat-option>
          <mat-option [value]='2'>Medium</mat-option>
          <mat-option [value]='1'>Low</mat-option>
        </mat-select>
      </mat-form-field>
    </mat-cell>
  </ng-container>
  ...
</mat-table>

my.component.ts

import { Component, OnInit, AfterViewInit, OnDestroy } from '@angular/core';
import { DataService } from '../service/data.service';
import { Subject } from 'rxjs/Subject';
import { filter, switchMap, buffer, startWith } from 'rxjs/operators';
import { interval } from 'rxjs/observable/interval';
import { combineLatest } from 'rxjs/observable/combineLatest';

@Component({
  selector: 'app-data-grid',
  templateUrl: './data-grid.component.html',
  styleUrls: ['./data-grid.component.scss']
})
export class DataGridComponent implements OnInit, AfterViewInit, OnDestroy {
  dataToSave = new Subject<any>();

  // trigger the buffer either when 7 seconds has passed, or a row has been completed.
  bufferCondition = combineLatest(
    interval(7000).pipe(
      startWith(0)
    ),
    this.dataToSave.pipe(
      startWith({}),
      filter(row => this.checkAllFieldsCompleted(row))
    )
  );

  constructor(private dataService: DataService) { }

  ngAfterViewInit() {
    this.dataToSave.pipe(
      buffer(this.bufferCondition),
      filter(dataArray => data && dataArray.length > 0),
      switchMap(dataArray => this.dataService.saveData(dataArray))
    ).subscribe(dataSaved => { 
      /* success */ 
    }, err => { 
      /* error */
    });
  }

  checkAllFieldsCompleted(row: any): boolean {
    return /* logic to check if complete */
  }
    ...
}

我认为这将是反应式扩展的完美使用!但我似乎不太能使它正常工作。如果仅使用bufferTime运算符,则一切正常。因此,我认为错误在于我如何定义自定义缓冲区条件。

1 个答案:

答案 0 :(得分:0)

经过一番挖掘和反复试验后,我有了解决方案。

我从this website中学到了

  

请注意,combineLatest 在每个可观察对象发出至少一个值之前不会发出初始值

在我的缓冲条件下,我试图将两个可观察值组合在一起:7秒间隔和一行完成的时间。我注意到预期的行为只是在我第一次完成一行之后 开始-直到那时它才会每7秒开始保存一次。问题是filter上的dataToSave运算符可观察到。 combineLatest不会发出任何东西,直到两个观察物都发出了东西。

我更改了此代码:

bufferCondition = combineLatest(
  interval(7000).pipe(
    startWith(0)
  ),
  this.dataToSave.pipe(
    startWith({}),
    filter(row => this.checkAllFieldsCompleted(row))
  )
);

对此:

bufferCondition = combineLatest(
  interval(7000),
  this.dataToSave.pipe(
    filter((row, index) => index === 0 || this.checkAllFieldsCompleted(row))
  )
);

,效果很好!基本上,我不需要startWith运算符,并且在filter上,我具有可观察到的对数据的第一个更改,但在行完成之前没有其他更改。