Estoy llamando a un servicio API que devuelve un Observable, que contiene una matriz de elementos.
apiMethod(input: Input): Observable<ResultElement[]>
A partir de esto, he estado eligiendo el primer elemento de la matriz, suscribiéndome a eso. Luego usó ese elemento para enrutar a otra página como esta:
this.apiService .apiMethod(input) .pipe(map((results) => results[0]) .subscribe( (result) => { return this.router.navigate('elements/', result.id) } )Esto funciona bien.
El problema es que no quiero usar solo el primer elemento, quiero que aparezca un MatDialog u otro similar, y darle al usuario la opción de qué elemento elegir, y LUEGO enrutar al correcto.
Sin embargo, si la lista solo contiene un elemento, el cuadro de diálogo no debería mostrarse y el usuario debería ser enrutado inmediatamente.
Intenté abrir un cuadro de diálogo en la .pipe(map()) , pero subscribe() ocurre antes de que obtenga una respuesta del usuario, lo que hace que falle. Y no estoy seguro de si ese es el enfoque correcto. ¿Cómo resolvería alguno de ustedes este problema?
Editar Terminó haciendo en parte lo que sugirió @BizzyBob:
Cambiando el mapa a switchmap en la llamada API, haciéndolo de esta manera:
this.apiService .apiMethod(input) .pipe(switchMap((results) => this.mapToSingle(results) .subscribe( (result) => { return this.router.navigate('elements/', result.id) } ) Con mapToSingle(ResultElement[]) siendo así:
private mapToSingle(results: ResultElement[]): Observable<ResultElement> { if (result.length === 1){ return of(results[0]); } const dialogConfig = new MatDialogConfig<ResultElement[]>(); dialogConfig.data = results; const dialogRef = this.dialog.open(ResultDialogComponent, dialogConfig); return dialogRef.afterClosed(); }Dos opciones posibles:
El siguiente ejemplo es un boceto del uso de un Observable para realizar un seguimiento del estado del componente.
readonly getResultsSubject = new Subject<MyInput>(); readonly resultSelectedSubject = new Subject<ResultType>(); private readonly apiResults$ = this.getResultsSubjects.pipe( switchMap((input) => this.apiMethod(input)) ); readonly state = combineLatest([ this.apiResults$.pipe(map(results => (s) => results.length === 1 ? { ...s, results, selectedResult: x[0], showModal: false } : { ...s, results, showModal: results.length > 1 })), this.resultSelectedSubject.pipe(map(selectedResult => (s) => ({ ...s, selectedResult }))) ]).pipe( scan((s, reducer) => reducer(s), { }), shareReplay(1) ); ngOnInit() { this.state.pipe( filter(x => !!x.selectedResult) ).subscribe(x => this.router.navigate('elements/', x.selectedResult.id)); }Últimamente estoy usando mucho este patrón. Hace que sea bastante fácil que crezca el número de acciones y propiedades del estado.
Crearía un DialogComponent que tome la lista de opciones como entrada y emita el elemento elegido cuando esté cerrado.
Luego, cree un método auxiliar ( quizás promptUser ) que simplemente devuelva un observable que emita el valor seleccionado:
this.apiService.apiMethod(input) .pipe( switchMap(results => results.length > 1 ? this.promptUser(results) : of(results[0]) ) ) .subscribe( result => this.router.navigate('elements/', result.id) ); Aquí simplemente usamos switchMap para devolver un observable que emita el elemento adecuado. Si la longitud es mayor que 1, devolvemos el método auxiliar que muestra el cuadro de diálogo y emite el elemento elegido; de lo contrario, solo emitimos el primer ( único ) elemento. Tenga en cuenta que envolvimos el valor simple con of ya que dentro de switchMap, necesitamos devolver observable.
En cualquier caso, el elemento deseado es emitido y recibido por su devolución de llamada de suscripción.
Lo resolvería usando el siguiente método:
options: any; this.apiService .apiMethod(input) .subscribe( (result) => { if (result.length === 1) { this.router.navigate([result[0]]); return; } options = result; } ) <modal-component *ngIf="options.length > 0"></modal-component> html <div (click)="redirect(value)">option 1</div> ts redirect(value) { this.router.navigate([value]); }eso seria lo mas directo