将可观察返回的对象数组转换为另一种类型的数组

时间:2019-01-11 17:17:42

标签: angular rxjs angular-services angular2-observables

这种情况是这样的:我有“任务服务”返回了可观察到的任务数组:

  getMissions(): Observable<Mission[]> {
  return this.http.get<Mission[]>(this.MissionUrl).pipe(
    tap(_ => this.log('Fetched missions.')),
    catchError(this.handleError('getMissions: ', []))
  );

以及组件内部完美运行的订户功能:

export class TreeViewerComponent implements OnInit {
  missions: Mission[];
  Nodes: Node[]=[];
  Edges: Edge[];

  constructor(private missionService: MissionService,
              private messageService: MessageService) { }

  ngOnInit() {
      this.getNodes();

  }

    getNodes(): void {
      this.missionService.getMissions()
      .subscribe(missions => {
          this.missions = missions;
          this.doThis();
        })
}

    doThis(): void{
      console.log('tree-viewer component1: '+this.missions);
      for (var i=0;i<this.missions.length;i++)
      {
        var tempNode= {id: this.missions[i].key, label: this.missions[i].Title};
        console.log(tempNode);
        this.Nodes.push(tempNode);
      }
      this.getEdges();
      this.wrapData();
    }

问题是,这不是很好的关注点分离。查看器组件不应处理所有这些业务逻辑。我想将类型从Mission(从远程服务器返回)中的类型转换为Node放入另一个服务中。

我该如何异步呢?

在实施建议后进行修改:

我仍然在做错事。如建议的那样,我在treeHandlerService中有两个版本的getNodes方法。第一个是@fmontes的建议,确实有效:

  getNodes(): void
  {
    this.missionService
        .getMissions()
        .pipe(
            flatMap((missions: Mission[]) => missions), // this will pass each mission to the map
            map((mission: Mission) => { // this will turn each Mission into a Node
                return new Node(mission.key, mission.Title) ;
            }),
            toArray() // this will group the nodes into an array of nodes
        )
        .subscribe((nodes: Node[]) => {
            this.Nodes = nodes;
            this.log('nodes: '+this.Nodes[0].label);//the log shows that it is working!
        });
  }

A,当我转到组件以读取treeHandlerService.Nodes(异步更新的数组)时,它仍然是未定义的(意味着asynchronos活动尚未完成)。

另一个版本是缩短版本,我试图立即返回该值:

  getNodes(): Observable<Node[]> {
    return this.missionService.getMissions().pipe(
      map((mission: Mission) => { // this will turn each Mission into a Node
          return new Node(mission.key, mission.Title) ;
      }),
      toArray()
    )
  };

但是它甚至不能编译!我收到以下错误:

ERROR in src/app/tree-handler.service.ts(39,7): error TS2345: Argument of type 'OperatorFunction<Mission, Node>' is not assignable to parameter of type 'OperatorFunction<Mission[], Node>'.

类型'任务'不能分配给类型'任务[]'。     类型“任务”中缺少属性“长度”。

请帮助大家...

2 个答案:

答案 0 :(得分:0)

您可以这样做:

// Don't forget to import the operators
import {map, flatMap, toArray} from 'rxjs/operators';


this.missionService
    .getMissions()
    .pipe(
        flatMap((missions: Mission[]) => missions), // this will pass each mission to the map
        map((mission: Mission) => { // this will turn each Mission into a Node
            return { id: mission.key, label: mission.Title };
        }),
        toArray() // this will group the nodes into an array of nodes
    )
    .subscribe((nodes: Node[]) => {
        this.nodes = nodes;
    });

答案 1 :(得分:0)

我认为可以这样进一步简化:

import { map, catchError } from 'rxjs/operators';

export class MissionService {

  ...

  getMissions(): Observable < Mission[] > {
    return this.http.get < Mission[] > (this.MissionUrl).pipe(
      map(missions => missions.map(mission => ({ 
        id: mission.key,
        label: mission.Title }) 
      )),
      catchError(this.handleError('getMissions: ', []))
    );
  }

  ...

}

在这里,我没有在数组上使用flatMap方法,而是简单地使用Rxjs的toArray运算符和map方法,而不是另外使用mapexport class TreeViewerComponent implements OnInit { missions: Mission[]; Nodes: Node[] = []; Edges: Edge[]; constructor( private missionService: MissionService, private messageService: MessageService ) {} ... getNodes(): void { this.missionService.getMissions() .subscribe(missions => { this.missions = missions; }); } ... } 运算符有需要的人。

然后在您的组件类中:

var managedContext: NSManagedObjectContext!

let fetchRequest : NSFetchRequest<TrackInfo> = TrackInfo.fetchRequest()
        fetchResults = try! managedContext.fetch(fetchRequest)
        for (i, _) in Global.Vars.numberOfTrackButtons! {


        let workingTrackInfo = fetchResults.randomElement()!
        print("current track is: \(workingTrackInfo)")