Steve Silva
Amigos, la forma del suscribe sale deprecado (aunque igual funciona), revisando la documentación se podría hacer así:
Jimmy Buriticá Londoño
Reinaldo Mendoza
Gracias por la aclaracion, muy buen aporte
Las peticiones HTTP que tu aplicación realiza pueden fallar por una u otra razón. Recuerda que una aplicación profesional tiene que contemplar estos escenarios y estar preparada para cuando esto suceda.
Manejo de Errores con RxJS
Ya hemos visto anteriormente la función retry() de RxJS para reintentar una petición en caso de fallo. Pero si el error persiste, veamos cómo es posible manejar los mismos.
1. Imports desde RxJS
Presta atención a las siguientes importaciones desde RxJS y @angular/common/http que utilizaremos.
// service/api.service.ts import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class ApiService { // ... }
2. Manejo de errores con catchError()
Agrega a los métodos que realizan las solicitudes HTTP un .pipe() para manipular los datos que el observable emita antes de enviarlos al componente.
Aquí estamos utilizando catchError de RxJS para capturar los errores y le pasamos un método personalizado llamado handleErrors() para centralizar el manejo de los mismos (Así no repetimos el mismo código en todos los pipes).
// service/api.service.ts getProduct(idProduct: number): Observable<Product> { return this.http.get<Product>(`https://example.com/api/productos/${idProduct}`) .pipe( catchError((err: HttpErrorResponse) => { return this.handleErrors(err) }) ); } handleErrors(error: HttpErrorResponse): Observable<never> { if (error.status == HttpStatusCode.Forbidden) return throwError('No tiene permisos para realizar la solicitud.'); if (error.status == HttpStatusCode.NotFound) return throwError('El producto no existe.'); if (error.status == HttpStatusCode.InternalServerError) return throwError('Error en el servidor.'); return throwError('Un error inesperado ha ocurrido.'); }
3. Identifica el tipo de error
Dicho método recibe un parámetro del tipo HttpErrorResponse que Angular nos provee para tipar errores HTTP. Utilizando el enumerado HttpStatusCode puedes determinar qué tipo de error se produjo. Si fue un error 403, 404, 500, etc. Fácilmente, puedes verificarlo y devolver un error al componente con throwError y un mensaje personalizado.
NOTA: Más allá de este enum que Angular nos facilita, es muy importante que como desarrollador de software sepas diferenciar lo que significa un 401, 403, 404, 500, 501, entre otros estados HTTP. Poco a poco lo irás aprendiendo.
4. Captura los errores en los componentes
En tu componente, captura los errores en las suscripciones de la siguiente manera:
// components/catalogo/catalogo.component.ts this.apiService.getProducts() .subscribe(res => { this.productos = res; }, err => { alert(err); // Aquí se emitirá el alerta con el mensaje que `throwError` devuelva. });
Así, ya puedes diferenciar los errores y comunicarle al usuario si algo salió mal de la manera más apropiada, mostrándole un mensaje o una alerta.
Contribución creada por: Kevin Fiorentino.
Steve Silva
Amigos, la forma del suscribe sale deprecado (aunque igual funciona), revisando la documentación se podría hacer así:
Jimmy Buriticá Londoño
Reinaldo Mendoza
Gracias por la aclaracion, muy buen aporte
para angular a 9/9/2023 el service quedaria asi:
getProduct(id: string){ return this.http.get<Product>(`${this.URL_API}/${id}`) .pipe( catchError((err: HttpErrorResponse) => { if(err.status === HttpStatusCode.NotFound){ return throwError(()=>'Product not found'); } if(err.status === HttpStatusCode.Forbidden){ return throwError(()=>'You do not have permission to access this product'); } if(err.status === HttpStatusCode.Unauthorized) { return throwError(()=>'You must be logged in to access this product'); } return throwError(()=>'An error has occurred, try again later'); }) ); }
Otra forma de manipular los errores es:
observable.subcribe({ next: (resp) =>{}, error: (err) =>{}, complete: () =>{} })
Elkin Omar Jiménez García
Y yo creando la interfáz para el error, y para los estados validando el numerito, teniendo tantas cosas en Angular para ello. Que gran clase. Gracias :D
Edgar Benitez
Excelente clase! Poco manejo los errores, por lo que esta clase me encanto y tengo mejor manejo de los mismos. Caso curioso de acuerdo a mi experiencia, pocos manejan los errores, es común que eso lo haga el backend. Ya hoy por hoy porque se la codificación de los errores y los puedo solucionar en menor tiempo, pero ciertamente, como Front-End si estamos dedicados a desarrollar del lado del cliente, debemos manejar estos errores para tener un desarrollo más limpio y una mejor administración de los errores para la pronta solución de los mismos.
Cesar Elías Armendariz Ruano
Jesus Isai Vazquez Valdez
Muchas gracias! :D
Samuel Pérez Bacilio
Alguien mas le aparece throwError como en desuso? (y tambien suscribe cuando le agrego el manejador de error)
Jhon Manuel Angulo Moncada
wilderman.munoz
Andres Silva Vega
El metodo throwError(), según he visto en la documentación, puede ser sustituido por la palabra reservada throw. A mi me funciona:
catchError ((error: HttpErrorResponse) => { switch(error.status) { case 500: throw "Esto es un error 500"; break; case 404: throw "Esto es un error 404"; break; default: throw "Esto es un error default"; } })
Andres Silva Vega
Incluso el uso throwError como muestra el video ya está deprecado (aún que todavia funciona). Lo correcto es:
return throwError(() => new Error('Esto es un error'));
Kengya Moncada
me gusto esta clase !! flujo de errores,
Sara Camila Laura Manzaneda
Por azares de la vida me toca aprender Angular a pesar de que soy team ReactJS y lo que he podido ver es que esta ruta es excelente tiene todo lo necesario para realizar features complejas, aplicando clean code, error handling, SOLID y otros conceptos muy importantes. Me encantaría que la ruta de React fuera así. Concreta, clara, organizada y con testing.
Milton Estrada
Julio Mogollon
Para los que no sepan sobre los codigos HTTP les dejo la siguiente url que brinda un poco de humor con los tipos de codigos que hay.
Kevin Candia
Para el manejo de errores:
return this.http.get<Product>(`${this.apiUrl}/${id}`) .pipe( catchError((response: HttpErrorResponse) => { return { [HttpStatusCode.NotFound] : throwError(() => new Error('No encontrado.')), [HttpStatusCode.BadRequest] : throwError(() => new Error('Petición inválida.')), [HttpStatusCode.InternalServerError] : throwError(() => new Error('El servidor no puede procesar la petición.')) }[response.status] || throwError(() => new Error('No encontrado.')) }) );
Néstor Raúl Guzmán Díaz
Es mejor tener el manejo de errores en el componente? en el servicio? en ambos?
Emilio Nicolás Mendoza Patti
Por lo que veo cuando pongo la parte de manejo de errores despues del subscribe, me figura como que subscribe esta deprecado, alguien me podria indicar porque pasa esto ?
Nicolas Molina
ProfesorEmilio Nicolás Mendoza Patti
Muchas gracias @nicobytes. Puede ser que haya visto que esta forma que utilizas tambien funciona en proyectos de angular 11 por ejemplo?
Milton Estrada
Pariksit Erikrsnan Ortiz Flores
Amigo hice todo cuanto indicaste y no he logrado hacer que funcione las notificaciones en este proyecto. Podrías compartir tu repositorio por favor
Milton Estrada
my-store-angular aquí está mi repositorio completo espero que te sea de ayuda
Kevin Candia
Para los que no les gusta sweet alert 2 como a mi (es molesto), existe Toast, es menos molesto y se ve cool: https://www.npmjs.com/package/toastr
export class MyComponent implements OnInit { constructor( private appService: AppService ) { } ngOnInit(): void { this.appService.toastr.warning('Producto no encontrado.', Constants.WARNING) } } export class AppService { constructor( public toastr : ToastrService ) { }
Página principal: https://codeseven.github.io/toastr/ Pueden ver un demo aquí: https://codeseven.github.io/toastr/demo.html
Pariksit Erikrsnan Ortiz Flores
Amigo hice todo cuanto indicaste y no he logrado hacer que funcione las notificaciones en este proyecto. Podrías compartir tu repositorio por favor
Ruth Lovece
Costo bastante pero creo que así quedó;
onShowDetail(id: number){ this.productsService.getProduct(id) .pipe( catchError((error) => { // Maneja los errores que puedan ocurrir (puedes agregar lógica de manejo de errores aquí) console.error(error.statusText); // Remite error if (error.status === 400){ return throwError(()=>'El producto no existe') } if (error.status === 500){ return throwError(()=>'Error en el servidor') } return throwError(() => 'Ups, algo salió mal') })) .subscribe(data => { this.toggleDetail(); this.productChosen = data;}, ) }
SoulSystem SAS
Company_adminLa siguiente es una forma más actualizada y completa de hacer la función onShowDetail según la documentación de RxJS.
onShowProductDetail(id: string) { this.statusDetail = 'loading'; this.productsService.getProduct(id) .pipe( tap(() => this.toggleProductDetail()), // realiza una acción cuando se emite un valor catchError((error) => { // maneja los errores que puedan ocurrir console.error(error); this.statusDetail = 'error'; return of(null); // retorna un observable con un valor nulo para que el flujo continúe }) ) .subscribe((data) => { if (data) { // verifica que la respuesta sea válida antes de asignarla this.productChosen = data; this.statusDetail = 'success'; } }); }
pipe: Es un método que se utiliza para encadenar múltiples operadores de RxJS y crear una cadena de transformaciones de datos. El método pipe toma como argumento una serie de operadores separados por comas, los cuales se aplican secuencialmente a los datos que fluyen a través del observable. Al utilizar pipe, se crea una nueva instancia del observable con las transformaciones aplicadas.
tap: Es un operador de RxJS que permite realizar una acción secundaria en los datos que fluyen a través del observable, sin modificar los datos. tap es útil para depurar y monitorear el flujo de datos, ya que permite imprimir mensajes en la consola o realizar otras acciones secundarias mientras se procesan los datos.
of: Es una función que se utiliza para crear un observable que emite un valor específico y luego se completa. of es útil para crear observables que emiten valores estáticos o constantes, y es comúnmente utilizado en combinación con otros operadores para crear flujos de datos más complejos.
Les comparto la solución en un solo método y además para mostrar el error uso la librería Sweet alert 2.
onShowDetail(id: string) { this.statusDetail = 'loading'; this.productsService.getProduct(id).subscribe({ next: (data) => { this.toggleProductDetail(); this.productChosen = data; this.statusDetail = 'success'; }, error: (errorMsg) => { this.statusDetail = 'error'; Swal.fire({ title: 'Error!', text: errorMsg, icon: 'error', confirmButtonText: 'Ok', }); } }); }
Con el manejo de observables debemos ver como es el manejo de errores
Se puede obtener un manejo de errores desde el servicio para el manejo de excepciones en el backend
product.service.ts
import { HttpClient, HttpParams, HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { retry, catchError } from 'rxjs/operators'; import { throwError } from 'rxjs'; getProduct(id: number) { return this.http.get<Product>(`${this.apiUrl}/${id}`) .pipe( catchError((error: HttpErrorResponse) => { switch(error.status) { case HttpStatusCode.Conflict: return throwError(() => new Error ('Ups algo esta fallando en el server')); case HttpStatusCode.NotFound: return throwError(() => new Error ('El producto no existe')); default: return throwError(() => new Error ('Ups algo salio mal')); } }) ) }
Tambien se puede hacer manejo de errores desde el front end, utilizano la libreria sweetalert2 sweetalert2 sweetalert2 Github
Lo primero es importar la sweetalert2 en nuestro app.module.ts
app.module.ts
import { SweetAlert2Module } from '@sweetalert2/ngx-sweetalert2'; @NgModule({ imports: [ SweetAlert2Module ], })
despues activamos el manejo de errores con sweetalert2 en nuestro products.component
products.component.ts
import Swal from 'sweetalert2'; export class ProductsComponent implements OnInit { statusDetail: 'loading' | 'success' | 'error' | 'init' = 'init'; } onShowDetail(id: number) { this.statusDetail = 'loading'; this.productsService.getProduct(id) .subscribe({ next: (d) => this.showDetailOk(d), error: (e) => this.showDetailError(e), complete: () => console.info('complete') }); } showDetailOk(data: Product) { this.statusDetail = 'success'; console.log('producto obtenido: ', data); this.toggleProductDetail(); this.productChosen = data; } showDetailError(e: any) { this.statusDetail = 'error'; this.toggleProductDetail(); Swal.fire({ title: 'Error!', text: e, icon: 'error', confirmButtonText: 'Ok', }); }
si en las nuevas versiones la forma en que los implementa el profe fue deprecada.
solución throwError:
if (error.status === 500) { return throwError(() => new Error('Algo esta mal en el servidor')) }
solución subscribe:
this.productsService.getProduct(id) .subscribe({ next: (data) => { this.productChosen = data; this.statusDetail = 'success'; }, error: (response) => { this.statusDetail = 'error'; this.errorMessage = response.message }
Hola, les dejo el código del handlerErrors implementado, tomado de los recursos de esta clase y la solución al deprecated del trhowError.
getProduct(id: string): Observable<Product> { return this.http.get<Product>(`${this.apiUrl}/${id}`) .pipe( catchError((err: HttpErrorResponse) => { return this.handleErrors(err) }) ); } handleErrors(error: HttpErrorResponse): Observable<never> { if (error.status == HttpStatusCode.Forbidden) return throwError(() => new Error ('No tiene permisos para realizar la solicitud.')); if (error.status == HttpStatusCode.NotFound) return throwError(() => new Error ('El producto no existe.')); if (error.status == HttpStatusCode.InternalServerError) return throwError(() => new Error ('Error en el servidor.')); return throwError(() => new Error ('Un error inesperado ha ocurrido.')); }
Han surgido varios cambios, aquí les dejo mi código por si les sirve de guía
// Products.component onShowDetail(id: number) { this.statusDetail = 'loading' this.productsService.getProduct(id) .subscribe({ next: (data) => { this.toggleDetail() this.productDetail = data this.statusDetail = 'success' }, error: (err) => { console.log(err.error.message) this.statusDetail = 'error' } }) }
// Products.services getProductByPage(limit: number, offset: number): Observable<any> { return this.http.get<IProduct[]>(this.url2, { params: { limit, offset } }) .pipe( catchError((error: HttpErrorResponse) => { switch (error.status) { case HttpStatusCode.ServiceUnavailable: return throwError(() => new Error("Service Unavailable")) break; case HttpStatusCode.NotFound: return throwError(() => new Error("Product Not Found")) break; default: return throwError(() => new Error("Error default")) } }) ); }
Wow, eso es porque tú estás trabajando con la versión 13 de Angular que trae una nueva versión de Angular, pero no debes preocuparte, entonces si quieres manejar el error esta sería la nueva forma:
import { of } from 'rxjs'; // recommended of([1,2,3]).subscribe((v) => console.info(v)); // also recommended of([1,2,3]).subscribe({ next: (v) => console.log(v), error: (e) => console.error(e), })
Entonces tu primera función va en next y la segunda función va en error
Sí, quieren usar una librería de notificaciones, una de las más populares es ngx-toastr:
npm install ngx-toastr --save
import { ToastrModule } from 'ngx-toastr'; // .... @NgModule({ imports: [ BrowserModule, FormsModule, ToastrModule.forRoot() ], }) export class AppModule { }
En el componente
import { ToastrService } from 'ngx-toastr'; export class MyComponent { constructor(private toastr: ToastrService) {} showSuccess() { this.toastr.success('Operación exitosa', '¡Genial!'); } }