#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 как средние данные []).