Angular 2: как использовать наблюдаемый фильтр

#angular #rxjs #observable

#angular #rxjs #наблюдаемый

Вопрос:

У меня есть служба, которая вызывает API следующим образом:

 return this._http
        .post(appSettings.apiUrl   'SomeController/SomeAction', params, {withCredentials: true, headers: this.headers})
        .timeoutWith(appSettings.maxTimeHttpCalls, Observable.defer(() => Observable.throw(this._feedbackService.timeout())))
        .map((response: Response) => response.json().data);
  

Теперь я хочу реализовать функцию фильтра для этого вызова, используя rxjs/add/operator/filter , но я не могу заставить ее работать должным образом.

Это подход, который я выбрал:

 return this._http
        .post(appSettings.apiUrl   'SomeController/SomeAction', params, {withCredentials: true, headers: this.headers})
        .timeoutWith(appSettings.maxTimeHttpCalls, Observable.defer(() => Observable.throw(this._feedbackService.timeout())))
        .filter(response => response.json().data.Type === 5)
        .map((response: Response) => response.json().data);
  

Но независимо от того, что я фильтрую, ngFor цикл ничего не производит, пока там есть фильтр. Если я удалю его, все будет работать так, как ожидалось.

Должен ли я добавлять фильтр до или после map ?

Могу ли я фильтровать JSON-ответ подобным образом или мне нужно использовать другой синтаксис?

Пример JSON

Вот пример того, как выглядит ответ JSON:

 data: [
    {
        "Type": 5,
        "Description": "Testing",
        "ID": "001525"
    }
]
  

Комментарии:

1. Наиболее вероятным объяснением является то, что там поле типа поля данных json не равно 5. Что такое JSON?

Ответ №1:

filter() Должно ли быть до или после map() , зависит от того, что вы хотите сделать.

Я думаю, в вашем случае map() следует идти раньше filter() , потому что вы хотите сначала декодировать данные из JSON, а затем фильтровать их. То, что у вас есть сейчас, ничего не вернет, если условие in filter() будет разрешено false , потому что вы используете in для всего response . Возможно, это то, к чему вы стремитесь…

Я не знаю, какова ваша структура ответа, но я бы выбрал что-то подобное, что имеет больше смысла:

 map((response: Response) => response.json().data),
filter(data => data.Type === 5),
  

Редактировать:

Я бы использовал concatMap() with from() для преобразования массива в наблюдаемый поток:

 pipe(
  map(content => response.json().data),
  concatMap(arr => Observable.from(arr)),
  filter(item => item.Type === 5),
).subscribe(val => console.log(val));
  

Смотрите живую демонстрацию: http://plnkr.co/edit/nu7jL7YsExFJMGpL3YuS?p=preview

Январь 2019: обновлено для RxJS 6

Комментарии:

1. Я добавил несколько примеров JSON. Я попробовал ваш способ с фильтром последним, и фильтрация на обоих data.Type === 5 и data.ID === '001525' , но оба терпят неудачу.

2. @GlennUtter Это то, что я подумал. data представляет собой массив объектов, но map((response: Response) => response.json().data) возвращает его как один большой массив, который затем передается в filter. Итак, вы хотите повторить результат и вернуть только объекты, где data.Type === 5 ?

3. @GlennUtter, чем это немного сложнее. Смотрите мой обновленный ответ с демонстрацией.

4. Спасибо, сэр. Я изменил свой код, но я получаю Property 'Type' does not exist on type '{}' , когда делаю это внутри оператора filter.

5. @GlennUtter Хорошо, это ошибка машинописи при компиляции? Вы можете использовать item['Type'] вместо или определить правильный тип в .filter(item:Whatevertypeitis => ...

Ответ №2:

Вот еще один пример, основанный на ответе @martin:

   public searchMediData(searchtearm: string) : Observable<MediData[]> 
  {  

    return this.http
               .get(this.url)
               .map(response => { 
                  let data = response.json();
                  let medidata = data as MediData[];
                  return medidata;
                })
                .concatMap(array => Observable.from(array))
                .filter(medi => {
                        let searchTearmUpperCase = searchtearm.toUpperCase();
                        let mediNameUpperCase = medi.Name.toUpperCase();                       
                        return mediNameUpperCase.includes(searchTearmUpperCase);
                 })
                .toArray();
  }
  

Комментарии:

1. вы можете просто написать .map(response => response.json().data как средние данные []).