LUIS DAVID MEDINA GAVIRIA
febrero 24, 2022 las endpoints no funcionan.
Nicolas Molina
ProfesorHola, sentimos los problemas causados y la demora en responder, ya la API está al aire de nuevo.
!api
¿Cómo mejorar el formulario de productos con las nuevas validaciones requeridas?
Las actualizaciones constantes en nuestras API demandan una atención al detalle en nuestras aplicaciones frontend. Un ejemplo claro es la necesidad de reflejar cambios en nuestro formulario de productos para que se comunique eficazmente con el backend. Con este enfoque, no solo cumplimos con los requisitos técnicos sino que también mejoramos la experiencia del usuario al prevenir errores comunes antes de que ocurran.
¿Qué cambios hemos hecho en el formulario de productos?
Esencialmente, hemos realizado varias modificaciones clave en el formulario de creación de productos para alinearlo con las actualizaciones de API:
¿Cómo gestionamos el Category ID?
Un desafío importante es el manejo del category ID, ya que nuestro backend ahora requiere que cada producto esté asociado a una categoría específica. Por el momento, hemos añadido un campo de input para manejar este valor. Sin embargo, próximamente lo reemplazaremos por un select dinámico que presente categorías reales, eliminando la posibilidad de errores y mejorando la usabilidad.
¿Qué validaciones implementamos?
Para asegurar la integridad de los datos enviados al backend, implementamos las siguientes validaciones:
¿Qué ocurre cuando los campos no tienen las validaciones correctas?
Si intentamos enviar el formulario sin cumplir estas validaciones, el backend automáticamente devuelve mensajes de error claros que indican qué campos requieren atención. Esto refuerza la importancia de tener validaciones tanto en el frontend como en el backend para una experiencia de usuario coherente y libre de frustraciones.
¿Cómo vamos a mejorar más el formulario?
Nuestra próxima mejora será reemplazar el campo de texto de category ID por un select dinámico. Este cambio permitirá que los usuarios elijan categorías predefinidas, asegurando exactitud en los datos y simplificando el proceso de creación de productos.
Para aquellos que desean ir un paso más allá, les reto a explorar cómo implementar este select dinámico por ustedes mismos. Esta práctica no solo mejorará sus habilidades, sino que también los preparará para futuras tareas de desarrollo.
¿Cómo asegurar la integridad y usabilidad del formulario?
Además de implementar select dinámicos, es crucial validar que los datos proporcionados por el usuario no sean arbitrarios. El backend debería rechazar inputs inválidos, como IDs de categoría inexistentes, manteniendo la integridad del sistema. Mientras tanto, desde el frontend, podemos mejorar la experiencia del usuario:
La clave para un desarrollo exitoso yace en la constante integración de feedback y en admitir, analizar y solucionar cada pequeño error que surge durante el proceso. ¡Continúen perfeccionando sus habilidades de desarrollo y sigan adelante con entusiasmo y curiosidad!
LUIS DAVID MEDINA GAVIRIA
febrero 24, 2022 las endpoints no funcionan.
Nicolas Molina
ProfesorHola, sentimos los problemas causados y la demora en responder, ya la API está al aire de nuevo.
!api
Alejandra Carvajal Campo
no funciona la api
Oscar Bravo
Jorge Quijano
Enero 27 - 2023 la API sigue sin funcionar...
Heger Arias Santos
He estado usando la nueva API de platzi, me funciona a la perfección. Debes cambiarla en cada archivo de enviroments.ts -> https://api.escuelajs.co/api/v1
url_api: 'https://api.escuelajs.co/api/v1'
Patrick Feeney
Muchas muchas cosas tienen que cambiar para que esto funcione con el nuevo backend.
Solo para comenzar el form deberia tener 'title' y no 'name'. Ademas, la 'image' ya no es singular si no plural: 'images'.
Con este codigo se puede convertir la imagen en un array. Ademas que en el file input permiti que se pudieran elegir varias imagenes...
task.snapshotChanges() .pipe( finalize(() => { this.images$ = fileRef.getDownloadURL(); this.images$.subscribe(newUrl => { const currentUrls = this.form.get('image')?.value || []; const mergedUrls = [...currentUrls, newUrl] this.form.get('images')?.setValue(mergedUrls); }); }) ) .subscribe();
Mi request se ve asi en la dev console:
{ "title":"asdada9999", "price":12, "images":["https://firebasestorage.googleapis.com/v0/b/p... etcetera"], "categoryId":"12", "description":"dfghjkl,;./'" }
Patrick Feeney
Me da 500 el backend....
Ruben Ariel Villalobos
El api ahora te pide que la imagen sea un array asi que lo solucione asi: primero en el formGroup ponerlo como nulo, images: [null, [Validators.required]], en el input html poner el atributo multiple y luego en el metodo uploadFile modificar el codigo del task por esto task .snapshotChanges() .pipe( finalize(() => { this.image$ = fileRef.getDownloadURL(); this.image$.subscribe((url) => { const currentValue = this.form.get('images').value || []; const newValue = [...currentValue, url]; this.form.patchValue({ images: newValue }); }); }) ) .subscribe();
Sabad Josue Perez Rodriguez
Si estas viendo el video en 2023 recuerda usar la nueva version de la API:
https://api.escuelajs.co/api/v1
. Para esta clase es necesario en el HTML de products modificar los siguientes datos: . anterior -> nuevo
{{product._id}} -> {{product.id}}, {{product.name}} -> {{product.title}}, [routerLink]="['edit', product._id] -> [routerLink]="['edit', product.id]
Emilio Nicolás Mendoza Patti
Emilio Nicolás Mendoza Patti
Utilizando la nueva API (https://api.escuelajs.co/api/v1) me muesta los siguiente mensajes
Logre seguir la clase pero haciendo unas modificaciones por lo que cambio la API, comparto resultado y el codigo
codigo .html
<form [formGroup]="form" (ngSubmit)="saveProduct($event)"> <mat-card> <mat-card-header> <mat-card-title>Producto</mat-card-title> </mat-card-header> <mat-card-content> <div class="row"> <div class="col-xs"> <mat-form-field> <input placeholder="Title" formControlName="title" matInput type="text"> <div class="messages" *ngIf="titleField.touched && titleField.invalid"> <mat-error *ngIf="titleField.hasError('required')">Este campo es rerquerido</mat-error> <mat-error *ngIf="titleField.hasError('minlength')">Debe tener mas de 4 caracteres</mat-error> </div> </mat-form-field> </div> </div> <div class="row"> <div class="col-xs"> <mat-form-field> <input placeholder="Price" formControlName="price" matInput type="number"> </mat-form-field> <div class="messages" *ngIf="priceField.errors && priceField.dirty"> <mat-error *ngIf="priceField.hasError('price_invalid')">No te debes pasar de 10000</mat-error> <mat-error *ngIf="priceField.hasError('required')">El campo es requerido</mat-error> </div> </div> </div> <div class="row"> <div class="col-xs"> <mat-form-field> <textarea placeholder="Text" formControlName="description" matInput></textarea> </mat-form-field> </div> </div> <div class="row"> <div class="col-xs"> <div *ngFor="let imageField of imagesField.value"> <img *ngIf="imageField" [src]="imageField" style="width: 200px;"> </div> <input type="file" (change)="uploadFile($event)" multiple="multiple"> </div> </div> <div class="row"> <div class="col-xs"> <mat-form-field> <input placeholder="Category" formControlName="categoryId" type="number" matInput/> </mat-form-field> </div> </div> </mat-card-content> <mat-card-actions> <button [disabled]="form.invalid" mat-raised-button type="submit">Guardar</button> </mat-card-actions> </mat-card> </form>
Codigo .ts
import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { AngularFireStorage } from '@angular/fire/storage'; import { finalize } from 'rxjs/operators'; import { MyValidators } from './../../../../utils/validators'; import { ProductsService } from './../../../../core/services/products/products.service'; import { FileModel } from './../../../../core/models/file.model' import { Observable } from 'rxjs'; @Component({ selector: 'app-product-create', templateUrl: './product-create.component.html', styleUrls: ['./product-create.component.scss'] }) export class ProductCreateComponent implements OnInit { form: FormGroup; image$: Observable<any>; constructor( private formBuilder: FormBuilder, private productsService: ProductsService, private router: Router, private storage: AngularFireStorage ) { this.buildForm(); } ngOnInit() { } saveProduct(event: Event) { event.preventDefault(); if (this.form.valid) { const product = this.form.value; this.productsService.createProduct(product) .subscribe((newProduct) => { this.router.navigate(['./admin/products']); }); } } uploadFile(event) { const files = [...event.target.files] as FileModel[]; files.forEach(item => { const name = `${item.name}`; const fileRef = this.storage.ref(name); const task = this.storage.upload(name, item); task.snapshotChanges() .pipe( finalize(() => { this.image$ = fileRef.getDownloadURL(); this.image$.subscribe(url => { const fileList = this.imagesField.value as string[] this.imagesField.setValue([...fileList, url ]) }); }) ) .subscribe(); }) if (files) { this.imagesField.setValue([] as string[]) } } private buildForm() { this.form = this.formBuilder.group({ title: ['', [Validators.required, Validators.minLength(4)]], price: ['', [Validators.required, MyValidators.isPriceValid]], images: [Array<string>(), Validators.required], categoryId: ['', Validators.required], description: ['', [Validators.required, Validators.minLength(10)]], }); } get titleField() { return this.form.get('title') } get priceField() { return this.form.get('price'); } get imagesField() { return this.form.get('images'); } get descriptionField() { return this.form.get('description'); } }