Tengo problemas para entender cómo debo trabajar con Observables para cumplir con mis requisitos. Como ejemplo, digamos que tengo una aplicación y para la internacionalización, estoy usando el servicio REST con todas las etiquetas y traducciones. Algunas etiquetas pueden cambiar según las acciones realizadas en la página. Y no estoy seguro de cómo recuperar datos del servicio y usarlos en diferentes métodos.
Veamos este pseudocódigo:
labels: KeyValuePair[]; userDetails: UserDetails; displayedColumns: string[]; // services below generated thanks to ng-swagger-gen constructor( labelService: LabelService, userService: UserService ){} ngOnInit() { this.userService.get().subscribe( (userData) => { this.userDetails = userData; console.log(userData); }, (error) => { this.errorMessage = error; } ); this.labelService.get(this.userDetails.getLanguage()).subscribe( (labelsResponse) => { this.labels = labelsResponse; // use the labels in HTML template console.log(labelsResponse); }, (error) => { this.errorMessage = error; } ); this.retrieveTableColumns(); } retrieveTableColumns() { this.displayedColumns = this.labels.filter(/* filter columns */); } executeMethods() { // method executed from HTML template this.doSomethingWithUserDetails(); this.doSomethingWithLabels(); } doSomethingWithUserDetails() { // 1. do differnet things and use it in HTML template // 2. check one of properties and based on this this.setColumns(); } setColumns() { // columns used in mat-table if (this.userDetails.allowedForAction) { this.displayedColumns = this.columnsDef.concat(['action']); this.hiddenColumns.push(this.displayedColumns.length); } else { this.displayedColumns = this.columnsDef; } } doSomethingWithLabels() { // do some magic using retrieved labels }Para generar el servicio de cliente web, he usado ng-swagger-gen .
¿Cómo manejar los Observables para lograr esto? Entonces, para poder usar datos en diferentes lugares. Escuché sobre convertirlo con .toPromise y usar async await await , pero también escuché que esto es antipatrón... Así que no estoy seguro de qué hacer aquí... Estoy trabajando con Angular 9 si es importante.
Simplemente mantienes la respuesta como un observable:
labels$ = this.labelService.get(this.userDetails.getLanguage())Y en el Html usas una tubería asíncrona:
*ngFor="let label of labels$ | async"Si desea transformar los datos en su mecanografiado, use '.pipe()' en el observable y cree uno nuevo:
newLabels$ = labels$.pipe( map(labels => labels.filter(label => label.id > 5)), // Or whatever you want to filter, sorry no inspiration tap(labels => console.log(labels)), // more pipes ... )Puede crear sus propias funciones para usar en estas tuberías.
También puede simplemente escribir una función tomando cualquier objeto dentro del observable como parámetro y simplemente usar la función en su html:
*ngFor="let label of labelsSortedByName((labels$ | async)!)" Estoy usando un ngFor como ejemplo porque se usa mucho, pero puedes usarlo en cualquier lugar. Básicamente, intente no suscribirse en su código, solo use tuberías asíncronas. Te divertirás mucho aprendiendo sobre observables de orden superior, combinando múltiples observables.
Ejemplo:
first$ = of("en") // An observable representing the result of your first request second(lang: string): Observable<string> { // Request requiring a string argument return of(lang + " language") } result$ = this.first$.pipe( // The resulting observable of the second request mergeMap(r => { return this.second(r) }) )En plantilla:
{{ result$ | async }} <!-- Result: 'en language' --> En este ejemplo, first$ y second() son solo simulacros de sus solicitudes. Me doy cuenta de que su segunda solicitud hace algo más que simplemente concatenar cadenas, pero la idea es la misma. Toma un argumento del tipo que tiene el primer Observable y se devuelve un nuevo Observable.
Aquí hay un Stackblitz para que juegues.