Как сделать вложенные наблюдаемые вызовы в Angular2
У меня возникают проблемы с вложенными вызовами Observable. Под этим я подразумеваю вызов службы http, которая извлекает пользователя, затем получает идентификатор от пользователя, чтобы сделать еще один http-вызов, и, наконец, отобразить результаты на экране.
1) HTTP GET 1: получить пользователя
2) HTTP GET 2: получить предпочтения пользователя, передавая уникальный идентификатор в качестве параметра
Это означает следующий код в компоненте Blah.ts
:
версия 1 - этот код ничего не отображает
ngOnInit() {
this.userService.getUser()
.flatMap(u => {
this.user = u; // save the user
return Observable.of(u); // pass on the Observable
})
.flatMap(u => this.userService.getPreferences(this.user.username)) // get the preferences for this user
.map(p => {
this.preferences = p; // save the preferences
});
}
версия 2 - этот код работает, но кажется неправильным подходом ко мне:
this.userService.getUser().subscribe(u => {
this.user = u;
this.userService.getPreferences(this.user.username).subscribe(prefs => {
this.preferences = prefs;
});
});
И это шаблон:
<h3>User</h3>
<div class="row col-md-12">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">User details</h3>
</div>
<div class="panel-body">
<table class="table table-condensed">
<thead>
<tr>
<th>Username</th>
<th>Full Name</th>
<th>Enabled</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{user?.username}}</td>
<td>{{user?.fullName}}</td>
<td>{{user?.enabled}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- end of col 1-->
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">User preferences</h3>
</div>
<div class="panel-body">
<table class="table table-condensed">
<thead>
<tr>
<th>Language</th>
<th>Locale</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{preferences?.preferences?.get('language')}}</td>
<td>{{preferences?.preferences?.get('locale')}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- end of col 2-->
</div>
<!-- end of row 1-->
Я не думаю, что есть какой-то момент в показе сервиса, который просто вызывает вызовы http get()
:
http.get('http://blablah/users/')
.map((response) => response.json())
Пожалуйста, предложите, какой из них лучше всего подходит для определения цепи наблюдаемых.
Ответы
Ответ 1
Вы должны немного почитать на операторах rxjs. Ваши примеры очень многословны и используют flatMap
и map
таким образом, что они не должны использоваться. Также ваш первый пример не может работать, потому что вы не подписываетесь на Observable.
Это сделает то, что вам нужно:
ngOnInit() {
this.userService.getUser()
.do(u => this.user = u) //.do just invokes the function. does not manipulate the stream, return value is ignored.
.flatMap(u => this.userService.getPreferences(u.username))
.subscribe(p => this.preferences = p);
}
Начиная с rxjs 5.5 вы должны использовать пригодные для работы операторы:
ngOnInit() {
this.userService.getUser().pipe(
tap(u => this.user = u),
flatMap(u => this.userService.getPreferences(u.username))
).subscribe(p => this.preferences = p);
}
Ответ 2
Хорошо, поэтому после дня борьбы и сбора информации из Интернета вот что я узнал о цепочке Observables (Calling Observables в последовательности - один за другим):
Я работаю над сайтом Angular2 (4), и этот сайт использует API-интерфейс java для получения/установки/изменения информации в базе данных.
Моя проблема заключалась в том, что мне пришлось выполнить два вызова API (HTTP POST) в последовательности, которая возвращает Observables (RxJS).
У меня есть Operation1 и Operation2. Операция 2 должна выполняться после завершения работы1.
Вариант1 → Сначала я сделал это один внутри другого (например, вложенные функции в javascript):
this.someService.operation1(someParameters).subscribe(
resFromOp1 => {
this.someService.operation2(otherParameters).subscribe(
resFromOp2 => {
// After the two operations are done with success
this.refreshPageMyFunction()
},
errFromOp2 => {
console.log(errFromOp2);
}
);
},
errFromOp1 => {
console.log(errFromOp1);
}
);
Несмотря на то, что этот код является законным и работает, у меня было требование связать эти Наблюдатели один за другим, как это делается с асинхронными функциями с обещаниями. Один из способов - преобразовать Observables в Promises.
Другим способом является использование RxJS flatMap:
Вариант2 → Другой способ сделать это с помощью flatMap, который, как я понял, похож на Promises, тогда:
this.someService.operation1(someParameters)
.flatMap(u => this.someService.operation2(otherParameters))
.subscribe(function(){
return this.refreshPageMyFunction()
},
function (error) {
console.log(error);
}
);
Вариант3 → То же самое со стрелочными функциями:
this.someService.operation1(someParameters)
.flatMap(() => this.someService.operation2(otherParameters))
.subscribe(() => this.refreshPageMyFunction(),
error => console.log(error)
);
Методы, возвращающие Observables, в основном таковы:
operation1(someParameters): Observable<any> {
return this.http.post('api/foo/bar', someParameters);
}
operation2(otherParameters): Observable<any> {
return this.http.post('api/some/thing', otherParameters);
}
Дополнительные ресурсы и полезные комментарии:
This post approved answer by @j2L4e: https://stackoverflow.com/a/40803745/2979938
https://stackoverflow.com/a/34523396/2979938
https://stackoverflow.com/a/37777382/2979938
Ответ 3
вы правы, вложенные подписчики ошибочны...
правильная карта
это должно помочь
https://embed.plnkr.co/mqR9jE/preview
или прочитать этот учебник
https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
некоторый код...
// responseStream: stream of JSON responses
var responseStream = requestStream
// We use flatMap instead of map to prevent this stream being a metastream - i.e. stream of streams
.flatMap(requestUrl => {
// Convert promise to stream
return Rx.Observable.fromPromise($.getJSON(requestUrl));
}).publish().refCount(); // Make responseStream a hot observable, prevents multiple API requests
// see https://gist.github.com/staltz/868e7e9bc2a7b8c1f754#gistcomment-1255116
здесь URL-адрес запроса - это вход, испускаемый из другого потока /Observable.
подписаться на responseStream
Ответ 4
Версия 1 является лучшей и должна работать, вы просто забыли подписаться на:
ngOnInit() {
this.userService.getUser()
.flatMap(u => {
this.user = u; // save the user
return Observable.of(u); // pass on the Observable
})
.flatMap(u => this.userService.getPreferences(this.user.username)) // get the preferences for this user
.map(p => {
this.preferences = p; // save the preferences
})
.subscribe();
}