-
Notifications
You must be signed in to change notification settings - Fork 1
paso4
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 |
---|
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. |
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
.
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.
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:
- El servicio para formatear las fechas
-
AuthorService
que tiene el método de creación, el cual hace el llamado ahttp
- 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
) { }
...
}
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;
...
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;
}
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');
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.