navigationend - Cómo hacer llamadas observables anidadas en Angular2
observable angular 6 (4)
Estoy teniendo algunos problemas para hacer llamadas observables anidadas. Con eso me refiero a una llamada a un servicio http que recupera a un usuario, luego obteniendo la identificación del usuario para hacer otra llamada http y, finalmente, muestra los resultados en la pantalla.
1) HTTP GET 1: obtener el usuario
2) HTTP GET 2: obtenga las preferencias del usuario pasando un identificador único como parámetro
Esto se traduce en el siguiente código en el componente Blah.ts
:
versión 1 - este código no muestra nada
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
});
}
Versión 2 : este código funciona pero me parece que me parece incorrecto:
this.userService.getUser().subscribe(u => {
this.user = u;
this.userService.getPreferences(this.user.username).subscribe(prefs => {
this.preferences = prefs;
});
});
Y esta es la plantilla:
<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-->
No creo que tenga sentido mostrar el servicio, que simplemente hace llamadas http get()
como:
http.get(''http://blablah/users/'')
.map((response) => response.json())
Por favor, sugiera cuál es el mejor enfoque de trabajo para definir una cadena de Observables.
Deberías leer un poco sobre los operadores de rxjs. Sus ejemplos son muy detallados y utilizan flatMap
y el map
de una manera que no deben usarse. Además, su primer ejemplo no puede funcionar porque no está suscrito a Observable.
Esto hará lo que necesites:
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);
}
A partir de rxjs 5.5 debe utilizar los operadores pipeables :
ngOnInit() {
this.userService.getUser().pipe(
tap(u => this.user = u),
flatMap(u => this.userService.getPreferences(u.username))
).subscribe(p => this.preferences = p);
}
La versión 1 es la mejor y debería funcionar, solo olvidaste suscribirte a:
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();
}
Muy bien, después de un día de dificultades y compilación de información de Internet, he aquí lo que aprendí sobre el encadenamiento de los Observables (Llamar a los Observables en una secuencia, uno después del otro):
Estoy trabajando en un sitio web Angular2 (4) y este sitio utiliza una API de back-end de Java para obtener / configurar / modificar la información en la base de datos.
Mi problema fue que tuve que hacer dos llamadas API (HTTP POST) en una secuencia que devuelve Observables (RxJS).
Tengo Operation1 y Operation2. La operación 2 debe ejecutarse después de la finalización de la operación1.
Variante1 -> Al principio lo hice uno dentro de otro (como funciones anidadas en 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);
}
);
A pesar de que este código es legítimo y está funcionando, tuve el requisito de encadenar estos Observables uno tras otro, como cómo se hace con las funciones asíncronas con Promesas. Una forma es convertir los Observables en Promesas.
Otra forma es usar RxJS flatMap:
Variante2 -> Otra forma es hacer esto con flatMap que, como he entendido, es similar a Promesas:
this.someService.operation1(someParameters)
.flatMap(u => this.someService.operation2(otherParameters))
.subscribe(function(){
return this.refreshPageMyFunction()
},
function (error) {
console.log(error);
}
);
Variant3 -> Lo mismo con las funciones de flecha:
this.someService.operation1(someParameters)
.flatMap(() => this.someService.operation2(otherParameters))
.subscribe(() => this.refreshPageMyFunction(),
error => console.log(error)
);
Los métodos que devuelven los Observables son básicamente estos:
operation1(someParameters): Observable<any> {
return this.http.post(''api/foo/bar'', someParameters);
}
operation2(otherParameters): Observable<any> {
return this.http.post(''api/some/thing'', otherParameters);
}
Recursos adicionales y comentarios útiles:
This post approved answer by @j2L4e: https://.com/a/40803745/2979938
https://.com/a/34523396/2979938
https://.com/a/37777382/2979938
Usted es correcto, suscripciones anidadas son incorrectas ...
plano es correcto
esto debería ayudar
https://embed.plnkr.co/mqR9jE/preview
o lea este tutorial
https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
algún código ...
// 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
Aquí, la URL de solicitud es una entrada emitida desde un flujo / Observable diferente.
ahora suscríbete a responseStream