Skip to content
Daniel Escalante Perez edited this page May 16, 2024 · 1 revision

Paso 4

Este paso contiene el código para crear un autor y una editorial. La creación de un libro se realiza de manera similar.

Los pasos para autor y editorial son muy similares. Vamos a explicar los pasos para autor dado que tiene más elementos, y en particular, un campo fecha.

Ver Video Intro

Para crear un autor tenemos un nuevo componente AuthorCreateComponent que tiene asociado un template, una forma, para crear los datos del nuevo author y un método que se ocupa de invocar el request Http post con el nuevo autor.

Ver Video Componente

Template para crear un autor

Ver Video Template

El template que representa esta forma se encuentra en la carpeta author-create del nuevo componente. El archivo es author-create.component.html. De la manera más básica un formulario en html es un elemento con el tag <form> y dentro debe tener tags para definir los campos de entrada y también debe definir qué se hace cuando el usuario finalice correctamente la creación o definir qué se hace cuando la cancela.

El siguiente fragmento muestra el inicio del formulario.

   <form [formGroup]="authorForm"(ngSubmit)="!authorForm.invalid && createAuthor(authorForm.value)">
...
<form>

El formulario se define como un grupo de elementos, donde cada elemento tiene casi siempre un label, para que el usuario sepa qué información debe ingresar y un input para recibir lo que el usuario digite o seleccione. Cada input puede tener o no un valor inicial y se le puede asignar validaciones. Veamos los ejemplos de la forma de autor:

...
    <div class="form-group mx-sm-3 mb-2">
      <label for="name">Name</label>
      <input
        novalidate
        id="name"
        class="form-control"
        formControlName="name"
        placeholder="Author's name"
      />
      <div
        class="alert alert-danger alert-dismissible fade show"
        *ngIf="
          authorForm.get('name')!.hasError('required') &&
          authorForm.get('name')!.touched
        "
      >
        Name required
      </div>
      <div
        class="alert alert-danger alert-dismissible fade show"
        *ngIf="authorForm.get('name')!.hasError('minlength')"
      >
        Name too short
      </div>
    </div>
...

Información Adicional

"form-group" El estilo bootstrap "form-group" es lo que permite que el label y el campo input vayan juntos.
"invalid-feedback" el texto de mensaje de error va a ir debajo de estos campos y el estilo "invalid-feedback" es lo que hará que se vea rojo y con una explicación que le definimos The author's name is missing.
label Tag para la etiqueta for="authorName"
input Lo que se va recibir de entrada. Especifica:
type="text" tipo del campo.
class="form-control" el estilo de la forma.
[(ngModel)]="author.name" establece una relación bidireccional (si se actualiza un lado se actualiza simultáneamente el otro) entre este campo y el atributo author.name del modelo (definido en el componente).
#authorName="ngModel"
[ngClass]="{ 'is-invalid': f.submitted && authorName.invalid }" Estilos CSS asociados con el formulario para indicar que todo está Ok o que hay información inválida.
required El campo es obligatorio. El usuario debe digitar información en el campo.

POST en el servicio de autor

En la clase AuthorService debemos agregar un servicio para crear en el back el nuevo autor. Es decir, invocar HTTP POST enviando el json que representa al autor.

@Injectable()
export class AuthorService {
    
    /**
    * Constructor of the service
    * @param http The HttpClient - This is necessary in order to perform requests
    */
    constructor(private http: HttpClient) { }
    
    ...
    
    /**
    * Creates an author
    * @param author The new author
    * @returns The new author with the new id
    */
    createAuthor(author): Observable<Author> {
        return this.http.post<Author>(this.apiUrl, author);
    }
}

La función createAuthor recibe de parámetro un objeto de la clase Author y retorna un Observable de la clase Author. Lo que hace la función es invocar http.post.

Componente de creación

El componente de creación del autor AuthorCreateComponent debe estar declarado en el módulo de autor. Este componente tiene por responsabilidad enlazar el autor que se crea en la forma con el que se va a enviar de argumento al servicio de crear el autor que se explicó en la sección anterior.

Declaración del componente y el constructor

Como los demás componentes, el componente de creación AuthorCreateComponent está anotado con @Component tiene el selector, el templateUrl y los estilos. Aquí hemos agregado un provider que se llama DatePipe para formatear las fechas como explicaremos más adelante.

El constructor recibe (inyección de dependencias) tres servicios, que son debidamente importados al comienzo del archivo:

  1. El servicio para formatear las fechas
  2. AuthorService que tiene el método de creación, el cual hace el llamado a http
  3. Un servicio ToastrService que sirve para desplegar mensajes en la pantalla del usuario y que lo usaremos para indicar que el "author fue creado satisfactoriamente".

|Aquí vale la pena recordar que cuando el método HTTP devuelve un código de error, ya sea porque alguna regla de negocio no fue válida, o porque falló algo en el back, esto se atrapa y se procesa en un interceptor global para toda la aplicación. Allí también se usa el mismo servicio ToastrService para desplegar los mensajes.

|:----|

import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import { DatePipe } from '@angular/common';
import { ToastrService } from 'ngx-toastr';
import { AuthorService } from '../author.service';
import { Author } from '../author';

@Component({
    selector: 'app-author-create',
    templateUrl: './author-create.component.html',
    styleUrls: ['./author-create.component.css'],
    providers : [DatePipe]
})
export class AuthorCreateComponent implements OnInit {
    constructor(
        private dp : DatePipe,
        private authorService: AuthorService,
        private toastrService: ToastrService
    ) { }
...
}

Declaración atributo para el nuevo autor

El componente declara un atributo author que es el que hará el enlace entre el template y el componente. Si revisamos de nuevo el Template para crear un autor podemos darnos cuenta que el atributo que se utiliza allí llamado author es el que estamos declarando aquí en el componente:

...
    /**
    * The new author
    */
    author: Author;
...

Función que llama al servicio de creación

La función que llama al servicio de creación, si no hay error durante la invocación, actualiza el atributo author con el valor que devuelve el servicio.

Como el servicio this.authorService.createAuthor es un Observable, esta función se suscribe al observable para que se haga la invocación http.post y para que retorne el resultado cuando haya terminado. Recordemos que las invocaciones http se hacen de manera asíncrona, es decir, en el momento de la suscripción se hace el llamado pero la ejecución en el front sigue su curso. Cuando regresa, se "activa" la suscripción para que ejecute lo que está dentro de la función. En este caso las líneas:

...
                this.author = author;
                this.create.emit();
                this.toastrService.success("The author was created", "Author creation");    
...

Estas líneas actualizan el atributo author, emiten un evento de creación exitosa e invocan el servicio toastrService para desplegar el mensaje.

La función completa se muestra en el siguiente fragmento. Nos falta entender lo del manejo de la fecha y los eventos emit y cancel:

...
    createAuthor(): Author {
       let dateB : Date = new Date(this.author.birthDate.year, this.author.birthDate.month, this.author.birthDate.day);
       this.author.birthDate = this.dp.transform(dateB, 'yyyy-MM-dd');

        this.authorService.createAuthor(this.author)
            .subscribe((author) => {
                this.author = author;
                this.create.emit();
                this.toastrService.success("The author was created", "Author creation");                
            });
        return this.author;
    }

Manejo de las fechas tipo Date

Para ver el uso del componente del calendario que estamos usando puede ir a:

Example usage of the datepicker widget from ng-bootstrap datepicker

En nuestra decisión de diseño las fechas van en formato YYYY-mm-dd. Es decir que el json que se recibe en el back tiene un atributo de tipo String con ese formato. El API REST convierte ese formato a un atributo de tipo Date (java.util).

En el front, los atributos de fechas los declaramos de tipo any. Tenemos que asegurarnos de que estos objetos que representan las fechas, viajan en el formato que se espera. Para esto necesitamos primero convertir en un Date la fecha que devuelve el componente del calendario (que fue declarado el campo como any) y luego convertir ese objeto Date a String.

Nota: El tipo Date de javascript el número que representa el month sigue la siguiente convención: 0 es enero, 1 es febrero, 2 es marzo, y así sucesivamente.

Teniendo lo anterior en cuenta, la instrucción para convertir el valor que devuelve el calendario a Date es:

let dateB: Date = new Date(this.author.birthDate.year, this.author.birthDate.month - 1, this.author.birthDate.day);

Para convertir el objeto Date a string usamos el servicio declarado en providers que se llama DatePipe. Esto implica que se debe incluir esta librería: import { DatePipe } from '@angular/common'.

Lo que hace DatePipe es convertir en string una fecha Date con el formato que reciba de argumento.

this.author.birthDate = this.dp.transform(dateB, 'yyyy-MM-dd');

Eventos para confirmar o cancelar la creación

Es importante también manejar los eventos de cancelación al momento de crear. Es decir, cuando el usuario decide cancelar el formulario. A continuación se muestra el código html al momento de realizar la cancelación del formulario:

...
                <button
                   type="button"
                   class="btn btn-danger ml-3"
                   (click)="cancelCreation()"
                >
                  Cancel
                </button>
...

Como se puede observar se llama al método cancelCreation() que este se debe encontrar en el componente. A continuación se muestra el fragmento de código de ese método en el componente:

  cancelCreation() {
    this.toastr.warning("The author wasn't created", 'Author creation');
    this.authorForm.reset();
  }

Allí se muestra un mensaje de alerta a través de Toastr y finalmente se reinician los campos del formulario.