Преобразование массива объекта, возвращаемого из наблюдаемого, в массив другого типа - PullRequest
0 голосов
/ 11 января 2019

Ситуация такая: у меня "служба миссии", возвращающая наблюдаемый массив Миссии:

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

Проблема в том, что это не хорошее разделение интересов. Компонент средства просмотра не должен иметь дело со всей этой бизнес-логикой. Я хочу отделить приведение типа Миссия (возвращаясь с удаленного сервера), чтобы ввести Узел в другой сервис.

Как я могу сделать это асинхронно?

Редактировать после предложений по реализации:

Я все еще делаю что-то не так. У меня есть две версии метода getNodes в treeHandlerService, как предложено. Первый - с предложенной имплантацией @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!
        });
  }

Увы, когда я иду к компоненту, чтобы прочитать treeHandlerService.Nodes (массив, который был обновлен асинхронно), он все еще не определен (то есть асинхронное действие еще не закончено).

Другая версия укорочена, и я сразу попытался вернуть значение:

  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 голосов
/ 11 января 2019

Я думаю, что это можно еще упростить следующим образом:

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 и toArray, я просто использую оператор map для Rxjs с методом map в массиве для выполнения необходимых действий.

А затем в вашем классе компонентов:

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

  ...

}
0 голосов
/ 11 января 2019

Вы можете сделать это:

// 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;
    });
...