使用NGRX&同步处理依赖于另一个Observable的Observable。角

时间:2018-04-06 16:04:58

标签: angular rxjs ngrx

 ngOnInit(): void {
    this.store.dispatch(new patients.Load([]));
    this.patients$ = this.store.select(fromPatients.getAll);

    this.patients$.map(p =>{ //  patients$: Observable<Patient[]>;
      this.rows = p.map(pat => { //I use this on the front end
        return {
          username: pat.username,
          id: pat.id,
          hasAlert: this.hasAlerts(pat), //made this an observable for the async pipe in view
          settings: "Settings"
        };
      });
      this.table.recalculatePages();
      console.log(this.rows);
      console.log("This happens first");
    }).subscribe();

  }
  hasAlerts(pat: Patient): Observable<boolean> {
    var shouldAlert$ = Observable.of(false);//this value is always taken

      this.observations2$ = this.dataService.fetchItems<Observation>(
        "Observation",
        null,
        pat.id// How would i pass this in with mergeMap()?  
      );

      this.observations2$.subscribe(curObservation => {
        if (curObservation.category.coding[0].code == "GlucoseEvent"){ 
          shouldAlert$ = Observable.of(true);
          console.log("should alert$", shouldAlert$);
        }
      });

    console.log("this happens second");
    return shouldAlert$;
  }

在上面的代码中,我解析了一个名为patient $的观察者,其中包含一系列患者。然后我将这些患者映射到我在客户端显示的名为this.rows的对象数组。

我的问题涉及hasAlert属性,该属性在对hasAlerts()的方法调用中处理另一个observable本身。此方法调用hasAlerts()不会同步发生,因此console.log(&#34;这首先发生&#34;);在我的hasAlerts方法可以在If语句中执行逻辑以确定它是应该设置为true还是false之前发生,而只是使用它在hasAlerts()的第一行中初始化的值。由console.log确认(&#34;这发生在第二个&#34;);显示第二。

hasAlerts()可以返回一个布尔值而不是一个可观察的,我试图看看是否可以在前端使用asycn管道来解决我的问题(它没有)。

我相信解决这个问题的方法涉及使用mergemap但是我不知道我将如何传递hasAlerts方法需要的pat.id?或者这可能不是解决当前问题的正确方法,即异步执行此代码。

我目前正在尝试使用这个this question about mergemap来解决我的问题,但是传递了第二个observable在hasAlerts中的pat.id我还没有想到。 1

按照Piccis的想法更新了代码。

this.patients$.map(p =>{ //  patients$: Observable<Patient[]>;
      this.rows = p.map(pat => { //I use this on the front end
        return {
          username: pat.username,
          id: pat.id,
          hasAlert: false, //set the default value
          settings: "Settings"
        };
      })
    }).do(data => console.log("data1",data))
  //   .switchMap(p => Observable.from(p))
  //   .do(data => console.log("data2",data)) // creates a stream of Observable<Patient>
  //   .mergeMap(patient => this.dataService.fetchItems<Observation>(
  //       "Observation",
  //       null,
  //       "pat..frank"//patient[0].id//"pat..frank"//patient.id// patient should be your guy          
  //     )
  //     .map(curObservation => {
  //       console.log("currOBS",curObservation); 

  //       if (curObservation.category.coding[0].code == "GlucoseEvent"){
  //         var shouldAlert$ = true;
  //         console.log("should alert$", shouldAlert$);
  //       }
  //     })
  //   ).do(data => console.log(data))
  //  .toArray()
   .subscribe(
      patients => {
          this.table.recalculatePages();
          console.log(this.rows);
      }
   )

Data1返回一组患者。我需要在中间注释掉,因为switchmap行有一个语法错误说&#34;类型的论证&#39; void&#39;不能分配给类型参数&#39; ArrayLike&lt; {}&gt;&#39;&#39;&#34;

3 个答案:

答案 0 :(得分:1)

您正在使用mergeMap查找正确的方向,但您需要重新编写代码。

您从this.patients$开始,这是一个Observable。

从这里你需要创建一个Observable流。这可以使用Observable的静态方法from来完成。

现在,您可以管理每位患者并通过服务curObservation获取他/她fetchItems

最终重新创建数组,然后订阅。

最终结果可能看起来像这些行

    ngOnInit(): void {
            this.store.dispatch(new patients.Load([]));
            this.patients$ = this.store.select(fromPatients.getAll);

            this.patients$.map(p =>{ //  patients$: Observable<Patient[]>;
              this.rows = p.map(pat => { //I use this on the front end
                return {
                  username: pat.username,
                  id: pat.id,
                  hasAlert: false, //set the default value
                  settings: "Settings"
                };
              });
            })
            .do(data => consoel.log(data))  // check  whether data is an array of Patients as it is supposed to be
            .switchMap(p => Observable.from(p)) // creates a stream of Observable<Patient>
            .do(data => console.log(data))  // check whether data is a single Patient
            .mergeMap(patient => this.dataService.fetchItems<Observation>(
                "Observation",
                null,
                patient.id// patient should be your guy          
              )
              .map(curObservation => {
                if (curObservation.category.coding[0].code == "GlucoseEvent"){ 
                  shouldAlert$ = true;
                  console.log("should alert$", shouldAlert$);
                }
              })
            )
           .toArray()
           .subscribe(
              patients => {
                  this.table.recalculatePages();
                  console.log(this.rows);
              }
           )
        }

更新 - 基本机制

如果我们删除您案例的所有细节,上面代码段中实现的基本机制如下

import {Observable} from 'rxjs';

const obs1 = Observable.of([1, 2, 3, 4, 5, 6]);

obs1
.switchMap(n => Observable.from(n))
.mergeMap(n => Observable.of(n*2))
.toArray()
.subscribe(console.log)

答案 1 :(得分:1)

基本问题是组合两个异步调用,可以使用zip()完成。

注意,我最初使用forkJoin()发布了一个解决方案,但这不适用于ngrx select(),因为select永远不会完成 - 因此forkJoin永远不会触发)。

将第一次获取返回的Observable<Patient[]>转换为Observable<Patient>,因为它对zip运算符更方便。

下一个问题是第二个异步依赖于第一个异步(pat.id)的结果 - 使用concatMap()构建一个异步。

注意,我最初建议mergeMap(),但concatMap()保证hasAlert $与患者$的排序相同。这很重要,因为this.dataService.fetchItems()可能会无序返回个别提取物)。

import { zip } from 'rxjs/observable/zip';
...

ngOnInit(): void {
  this.store.dispatch(new patients.Load([]));

  const patient$ = this.store.select(fromPatients.getAll)
    .mergeMap(value => value); // convert from Observable<patients[]> to Observable<patient>

  const hasAlert$ = patient$.concatMap(patient => {
    return this.dataService.fetchItems<Observation>('Observation' null, patient.id)
      .map(curObservation => curObservation.category.coding[0].code === 'GlucoseEvent')
    );
  })

  zip(patient$, hasAlert$)  // combine values from both asyncs
    .map(([patient, hasAlert]) => {
      return {
        username: patient.username,
        id: patient.id,
        hasAlert,
        settings: "Settings"
      };
    })
    .toArray()
    .subscribe(rows => {
      this.rows = rows;
      this.table.recalculatePages();
    }); 
}

测试答案片段的Rx逻辑。

console.clear();
const { zip, from, of } = Rx.Observable;
/* in Angular with Rxjs v5.5, use 
  import { zip } from 'rxjs/observable/zip';
  import { from } from 'rxjs/observable/of';
  import { of } from 'rxjs/observable/from';
*/

// Simulate other observables
const storeSelectFromPatientsGetAll$ = () =>
  of([{id: 1, username: 'Fred'}, {id: 2, username: 'Joan'}]);
const dataServiceFetchItems$ = (type, something, id) =>
  of({ category: { coding: [{code: 'GlucoseEvent'}] }})

// Testing the ngOnInit code
const patient$ = storeSelectFromPatientsGetAll$()
  .mergeMap(value => value);

const hasAlert$ = patient$.concatMap(patient => {
  return dataServiceFetchItems$('Observation', null, patient.id)
    .map(curObservation => curObservation.category.coding[0].code === 'GlucoseEvent');
});

zip(patient$, hasAlert$)  // wait for resolution of both asyncs
  .map(([patient, hasAlert]) => {
    return {
      username: patient.username,
      id: patient.id,
      hasAlert,
      settings: 'Settings'
    };
  })
  .toArray()
  .subscribe(rows => {
    console.log(rows);
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.8/Rx.js"></script>

答案 2 :(得分:0)

这就是我能够通过不同的方法让这个逻辑为我工作的方式。我需要通过最终的角度ngrx视频和一些rxjs教程来更好地理解我在其他两个答案中出错的地方,因为提供的示例运作良好。

适用于我的方法

  • 在hasAlerts方法中处理来自数据服务的观察值时使用过滤器,并将符合条件的任何观察值添加到该值并返回它。

  • 将hasAlerts属性设置为false,然后为该给定患者调用hasAlerts()方法并修改该行上的该属性,然后返回该行。

    ngOnInit(): void {
    this.store.dispatch(new patients.Load([]));
    this.patients$ = this.store.select(fromPatients.getAll);
    
    this.patients$.map(p =>{ //  patients$: Observable<Patient[]>;
      this.rows = p.map(pat => { //I use this on the front end
        var rowx= {
          username: pat.username,
          id: pat.id,
          hasAlert: false, //made this an observable for the async pipe in view
          settings: "Settings"
        };
    
        this.hasAlerts(pat).do(x => {
            observations++;
            if (observations>0)
            {
              rowX.hasAlert=true;
            }
          }).subscribe();
        return rowX;
      });
    }).subscribe(
    ()=>{
    
    },
    ()=>{
      this.table.recalculatePages();
    });
    
    }
     hasAlerts(pat: Patient): Observable<Observation> {
    
      var obs$ = this.dataService.fetchItems<Observation>(
        "Observation",
        null,
        pat.id
      ).filer(function(curObservation){
         if (curObservation.category.coding[0].code == "GlucoseEvent"){ 
           return true;
         }
         else{
           return false;
         }
      });
    
    return obs$;
    }