Rolando Moya
Danna Hernández
Buen aporte! Gracias
Germán González
el mio
Los endpoints GET se utilizan para la obtención de datos, los endpoints POST para crearlos. Es momento de actualizarlos con PUT y PATCH.
PUT vs. PATCH, ¿cuál es la diferencia?
Técnicamente, los endpoints PUT deberían recibir todos los datos del registro a actualizar. Por ejemplo, el título, la descripción, el precio, la categoría, etc. En cambio, PATCH debería solo recibir un campo individual a actualizar como solo el título, o solo la categoría.
De todos modos, también puedes utilizar endpoints del tipo PUT que reciban un solo dato a actualizar. Ten en cuenta que PUT es mucho más utilizado que PATCH, pero si quieres refinar y ser estricto con tu backend y seguir a raja tabla las buenas prácticas, PATCH es ideal para este tipo de actualizaciones de tus datos.
Como generar una actualización de registros
Para las solicitudes PUT y PATCH y generar una actualización de registros sigue los siguientes pasos.
1. Crea las interfaces necesarias
Crea las interfaces necesarias para actualizar los datos. Recuerda que la interfaz CreateProducto extendía de Product y a su vez omita los campos que no necesita utilizar.
// interfaces/producto.interface.ts export interface Product { id: number; name: string; precio: number; description: string; image: string; category?: Category; } export interface CreateProducto extends Omit<Product, 'id' | 'category'> { idCategory: string; }
A partir de aquí, crea la interfaz UpdateProducto que extiende de CreateProducto y a su vez utiliza una nueva característica de TypeScript llamada Partial<> que coloca como opcionales todos los campos. Al ser todos los campos opcionales, puedes utilizar esta interfaz para solicitudes PUT o PATCH según tengas la necesidad.
// interfaces/producto.interface.ts export interface UpdateProducto extends Partial<CreateProducto> { }
2. Maneja interfaces para HTTP
Utiliza estas nuevas interfaces en el servicio para realizar peticiones HTTP
// services/api.service.ts @Injectable({ providedIn: 'root' }) export class ApiService { constructor( private http: HttpClient, ) { } // ... updateProductPUT(idProduct: number, body: UpdateProducto): Observable<Product> { return this.http.put<Product>(`https://example.com/api/productos`, body); } updateProductPATCH(idProduct: number, body: UpdateProducto): Observable<Product> { return this.http.patch<Product>(`https://example.com/api/productos`, body); } }
Ya sea que los endpoints del backend sean PUT o PACH, podrás realizar la solicitud y mantener tus datos tipados y tu aplicación más segura de errores.
3. Haz la solicitud para actualizar el producto
Finalmente, desde tu componente, realiza la solicitud para actualizar el producto. Cuando recibas el producto actualizado, deberás reemplazarlo por el producto viejo en tu lista de productos.
// components/catalogo/catalogo.component.ts updateProduct(idProduct: number): void { const body: UpdateProducto = { name: 'Nuevo nombre del producto', }; this.apiService.updateProductPATCH(idProduct, body) .subscribe(p => { // Reemplazamos el producto actualizado en el Array de productos const index = this.productos.findIndex(product => product.id === p.id); this.productos[index] = p; }); }
Si no utilizas PATCH y todos tus endpoints son PUT, eso está bien. No tiene que preocuparte.
Contribución creada por: Kevin Fiorentino.
Yo hice esta solución que se actualice tanto en la sección de show cart como en pantalla generar <updateProduct(){ const changes: UpdateProduct = { title: 'Nuevo title', description: 'Esta es un prueba', } const id = this.productChosen.id; this.productsService.update(id,changes).subscribe(data =>{ const productIndex = this.products.findIndex( item =>item.id ==this.productChosen.id ); this.products[productIndex] = data; this.productChosen = data; }) }>
más fácil usando map, yo actualizo el 'productChosen' y a continuación toda la data de productos 'this.products'.
Evidentemente cuando se tenga un backend con base de datos, quizás habría que una vez actualizado un producto, enviar a todos los usuarios esos datos modificados, desde el frontend podría haber habilitado un sistema de sockets o mientras tanto, hacer una petición de nuevo, como la que hicimos en ngOnInit(), para adquirir los datos de nuevo...
updateOneProduct() { const changes: UpdateProductDTO = { price: 500, title: 'Nuevo título para este producto' } const id = this.productChosen.id; this.productsService.update(id, changes).subscribe(updatedProduct =>{ console.log('Producto actualizado: ', updatedProduct); this.productChosen = updatedProduct; this.products = this.products.map((item)=>{ if (item.id === updatedProduct.id) { return updatedProduct; } return item; }); }); }
Rolando Moya
Danna Hernández
Buen aporte! Gracias
Germán González
el mio
Santiago Velandia Gallo
Intenté hacerlo con patch y no funciona, esto es porque en el backend no hay un metodo con el llamado patch(por si a alguien mas le pasa)
Jimmy Buriticá Londoño
Me ha gustado la forma en que se definen las interfaces con el uso de la palabra Partial y de esta manera todos los campos del objeto son opcionales.
Dionel Cristian Martinez
Otra forma de crear el DTO para actualizar seria:
interface UpdateProductDTO extends Partial<Omit<Product, 'id', 'category'>> { categoryId: number; }
Alejandro Mejia Escobar
Christian Michelle Torres Martínez
A mi parecer es una mala práctica lo que implementas, ya que product-detail al ser un componente hijo no debería realizar peticiones a una API, para eso tendría que delegársela al componente padre que es products, que considero es donde se tendría que hacer la petición a la API y ahí realizar el update. Pero como te digo sería ver que opina al respecto Nico u otros compañeros con el conocimiento, saludos
Carlos Gómez
Puedes separar el detalle, pero conservar el clic en el padre.
Patrick Feeney
Para el error HTTP 500:
Por alguna razon el backend explota si no le mandamos un array de imagenes con al menos un elemento. Asi que para que el PUT funcione, solamente tenemos que asegurarnos de incluir un el valor de imagenes. Ejemplo para actualizar el titulo:
const changes: UpdateProductDTO = { title: `nuevo tituloa123123`, images: this.productChosen.images }
Saludos
edit: para que no les salga el error de que el 'slug' ya existe, agreguen esto al titulo para que sea distinto cada vez `tu titulo ${new Date}`
Marcel Marin
A los servicios que nos suscribimos para recibir informacion del API luego no hay que hacer un unsuscribe?
Daniel Meza
Buenísima pregunta Marcel.
Cuando te has suscrito a un observable de tipo http o route no es necesario. Ya que en algún tiempo esos se completan. Cuando tu recibes una respuesta del servidor y se recibe con los datos que ocupabas o se retorna un error, el observable se completa.
¿Cuándo debes preocuparte por un unsubscribe correcto. Es cuando te suscribes a un observable que has creado. Debes indicar en qué momento quieres eliminar esas suscripción.
Dejo acá más info de eso: Cómo manejar Memory Leaks ? - 3 formas de hacer unsubscribe de tus observables 😏😏
Juan Sáez García
La API no funciona. ¿Saben como puedo solucionarlo y si hay alguna otra API parecida para continuar el curso?
Daniel Meza
Ya anda jalando al 100
Julian Franco
Hay otra manera de usar el Partial sin crear una nueva interfaz y es directamente en la funcion de update del product service.
update(dto: Partial <CreateProductDTO>){ return this.http.patch<Product>(this.apiUrl, dto); }
BlackSip .
Para que el cambio se vea reflejado en la vista Detalles solo es asignarle a this.productChosen = data;
Ahorra Seguros
No se que rayos hice pero no tuve la necesidad de hacer nada más, el productChosen y el array se actualizaron solos :V
David Castro
Jajaja Qué?? y al día de hoy ya sabes cómo fue que hiciste eso? Cuentalo amigo.
Néstor Raúl Guzmán Díaz
Estoy obteniendo e lsiguiente error cuando hago el update, si mal no estoy es problema de la api.
Patrick Feeney
Confirmo
Carlos Brizuela
No me quedó clara la diferencia entre put y patch para este caso. Porque en teoría put es para actualizar todos los campos, pero utilizamos put y solo le enviamos un campo e igualmente funcionó sin problemas, esto es por la programación interna de la api?
Andrey Martin
PUT (Actualización completa):
PUT (Actualización completa):
ELKIN YESID MORENO VELEZ
Muy interesante el manejo de DTO, el concepto de Model a través de Interface.
La pregunta que tengo es como manejar GENERIC en ANGULAR para crear CRUDs.
Saludos y Exitos.
Daniel Meza
Es muy similar a como lo usas en C#, miModelo<T>. Revisa el curso de Typescript que tiene platzi. Ahí se toca el tema.
Curso de TypeScript: Programación Orientada a Objetos y Asincronismo
Daniel Meza
Nico. ¿está bien dejar todas estas interfaces en el archivo product.model.ts?
Nicolas Molina
ProfesorHola, cuando el proyecto no es muy grande es totalmente válido, pero cuando se empieza a volver complejo puedes iniciar a dividir una interfaz por archivo, en teoría trata de seguir el patrón de LIFT (Locate code quickly, Identify the code at a glance, keep the Flattest structure you can, and Try to be DRY. )
Brandon Lee Aguero Fernandez
Genial el partial, con respecto a PUT y PATCH yo elegiría PATCH, ya que la mayoría de las veces solo que queremos actualizar un atributo o propiedad en específico, aunque como soy un poco purista jejeje si fue un proyecto personal me daría el tiempo para tener los dos métodos en mi backend 🙂. Para solucionar la actualización del detalle del producto lo mejor sería no realizar un get para ver lo detalles sino compartir los datos de la primera llamada en nuestro componente
Mauricio Vargas García
Brandon Lee Aguero Fernandez
En este también te fue mal
Mauricio Vargas García
Creo que la forma más fácil de actualizar también el producto seleccionado en el slide, es la siguiente:
updateProduct() { const change: UpdateProductDTO = { title: 'Cambio titulo', } const id = this.productChossen.id; this.productService.update(id, change) .subscribe(data => { const productIndex = this.products.findIndex(item => item.id === id); this.products[productIndex] = data; this.productChossen = data; //Se asigna el nuevo valor de data al producto seleccionado }); }
Cristian Felipe Morera Beltran
Hola, yo solo le agregue esa línea de código al subcribe
Me funciono pero no se si está bien jaja.
Pasa solucionar el problema de actualizar el producto en detalle solo hay que actualizar la variable productChosen con data.
updateProduct(){ const changes = { title: 'Nuevo titulo klk', } const id = this.productChosen.id; this.productService.update(id,changes) .subscribe(data => { console.log('Updated:', data); const productIndex = this.products.findIndex(item => item.id === this.productChosen.id); this.products[productIndex] = data; this.productChosen = data; }) }
Si siguieron mi consejo de separar el detalle de producto en un componente nuevo, puedes hacer las siguientes modificaciones para que la información se actualice cuando actualices el producto:
product-detail.component.ts
@Output() productUpdated = new EventEmitter<Product>(); updateProduct(id: string){ const changes: UpdateProductDTO = { title: 'Producto Editado v2' }; this.productsService.update(id, changes) .subscribe(data=>{ this.product = data; this.productUpdated.emit(this.product); }) }
products.component.html
<app-product-detail *ngIf="productChosen" [product]="productChosen" (productUpdated)="onUpdatedProduct($event)" ></app-product-detail>
products.component.ts
onUpdatedProduct(data:Product){ const productIndex = this.products.findIndex(item => item.id === data.id) this.products[productIndex] = data; }
El comentario anterior se me fue mal, aquí dejo el bloque de código
updateProduct() { const change: UpdateProductDTO = { title: 'Cambio titulo', } const id = this.productChossen.id; this.productService.update(id, change) .subscribe(data => { const productIndex = this.products.findIndex(item => item.id === id); this.products[productIndex] = data; this.productChossen = data; //Se asigna el nuevo valor de data al producto seleccionado }); } ``` updateProduct() { const change: UpdateProductDTO = { title: 'Cambio titulo', } const id = this.productChossen.id; this.productService.update(id, change) .subscribe(data => { const productIndex = this.products.findIndex(item => item.id === id); this.products\[productIndex] = data; this.productChossen = data; //Se asigna el nuevo valor de data al producto seleccionado }); }