Простой фильтр на массиве RXJS Observable

Я начинаю свой проект с Angular2, и разработчики, похоже, рекомендуют RXJS Observable вместо Promises.

Я достиг, чтобы получить список элементов (эпосов) с сервера. Но как я могу отфильтровать эльмы, используя, например, идентификатор?

Следующий код - это извлечение из моего приложения и теперь показывает окончательное рабочее решение. Пусть надеется, что это поможет кому-то.

@Injectable()
export class EpicService {

  private url = CONFIG.SERVER + '/app/';  // URL to web API

  constructor(private http:Http) {}

  private extractData(res:Response) {
    let body = res.json();
    return body;
  }

  getEpics():Observable<Epic[]> {
    return this.http.get(this.url + "getEpics")
      .map(this.extractData)
      .catch(this.handleError);
  }

  getEpic(id:string): Observable<Epic> {
    return this.getEpics()
      .map(epics => epics.filter(epic => epic.id === id)[0]);
  }
}

export class EpicComponent {

  errorMessage:string;
  epics:Epic[];
  epic:Epic;

  constructor(
    private requirementService:EpicService) {
  }

  getEpics() {
    this.requirementService.getEpics()
      .subscribe(
        epics => this.epics = epics,
        error => this.errorMessage = <any>error);
  }

  // actually this should be eventually in another component
  getEpic(id:string) {
    this.requirementService.getEpic(id)
        .subscribe(
        epic => this.epic = epic,
        error => this.errorMessage = <any>error);
  }
}

export class Epic {
  id: string;
  name: string;
}

Заранее благодарим вас за помощь.

Ответы

Ответ 1

Вам нужно будет фильтровать фактический массив, а не наблюдаемый, обернутый вокруг него. Таким образом, вы сопоставляете содержимое Observable (которое является Epic[]) с фильтрованным Epic.

getEpic(id:number): Observable<Epic> {
  return this.getEpics()
     .map(epics => epics.filter(epic => epic.id === id)[0]);
}

Затем вы можете subscribe до getEpic и делать с ним все, что хотите.

Ответ 2

Вы можете сделать это, используя методы flatMap и filter Observable вместо метода фильтра массива JS в map. Что-то вроде:

this.getEpics() // [{id: 1}, {id: 4}, {id: 3}, ..., {id: N}]
    .flatMap((data) => data.epics)
    .filter((epic) => epic.id === id) // checks {id: 1}, then {id: 2}, etc
    .subscribe((result) => ...); // do something epic!!!

flatMap предоставит сингулярные индексы для фильтрации, а затем вы сможете продолжить то, что произойдет дальше с результатами.

Если TypeScript выдает ошибку, указывающую, что вы не можете сравнивать строку и число, независимо от использования == в фильтре, просто добавьте до epic.id в фильтр, за Angular docs:

    .flatMap(...)
    .filter((epic) => +epic.id === id) // checks {id: 1}, then {id: 2}, etc
    .subscribe(...)

Ответ 3

Observables ленивы. Вы должны позвонить subscribe, чтобы сообщить observable отправить запрос.

  getEpic(id:number) {
    return this.getEpics()
           .subscribe(x=>...)
           .filter(epic => epic.id === id);
  }

Ответ 4

Вам нужно подписаться на Observable, чтобы получить данные, поскольку HTTP-вызовы асинхронны в JavaScript.

getEpic(id: number, callback: (epic: Epic) => void) {
    this.getEpics().subscribe(
        epics: Array<Epic> => {
            let epic: Epic = epics.filter(epic => epic.id === id)[0];
            callback(epic);
        }
    );
}

Вы можете вызвать этот метод, например:

this.someService.getEpic(epicId, (epic: Epic) => {
    // do something with it
});