Formularios, tablas y modelos de datos en Angular

Curso online de introducción a Angular. Cupón descuento: 💸-25€

Curso online avanzado con Angular. Cupón descuento: 💸-30€

formularios-tablas-y-modelos-de-datos-en-angular

Las aplicaciones Angular 9 son excelentes para el tratamiento de datos en el navegador. Su razón de ser fue la recogida de información mediante formularios y la presentación de páginas dinámicas de forma sencilla.

Vamos a ver cómo la librería @angular/forms enlaza las vistas, los controladores y los modelos; y cómo se hace la presentación de datos en listas y tablas.

Partiendo de la aplicación tal como quedó en Páginas y rutas Angular SPA, al finalizar tendrás una aplicación que recoge y presenta datos.

Código asociado a este tutorial en GitHub: AcademiaBinaria/angular-basic

1. Binding

1.0 Base

Los formularios son el punto de entrada de información a nuestros sistemas. Llevan con nosotros desde el inicio de la propia informática y se han comido una buena parte del tiempo de programación. En Angular han prestado una especial atención a ellos facilitando su desarrollo, desde pantallas simples hasta complejos procesos.

Creamos una nueva ruta funcional para la gestión de contactos. Requiere ruta, enlace, módulo y componente.

1
ng g m contacts --routing true --route contacts --module app-routing.module

En app-routing y en contacts-routing:

1
2
3
4
5
6
7
8
9
10
// app-routing
{
path: 'contacts',
loadChildren: () => import('./contacts/contacts.module').then(m => m.ContactsModule)
},
// contacts-routing
{
path: '',
component: ContactsComponent
}

En main.component.html

1
<li><a routerLink="contacts">Forms</a></li>

1.1 Directivas

Para empezar agregamos algunas propiedades. En contacts.component.ts:

1
2
3
4
5
6
header = 'Contacts';
description = 'Manage your contact list';
numberOfContacts = 0;
counterStyleColor = 'green';
counterClass = 'warning';
formHidden = false;

1.1.1 Enlace del modelo hacia la vista

En contacts.component.html mostramos cabeceras con estilo

1
2
3
4
<h3>
{{ header }}: {{ description | uppercase }}
</h3>
<p [style.color]="'green'">You have <mark class="{{ counterClass }}">{{ numberOfContacts }}</mark> contacts right now.</p>

1.1.2 Enlace de la vista hacia el modelo

En contacts.component.html también actuamos sobre la vista

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<input
value="Show Form"
class="primary"
type="button"
(click)="formHidden=false"
/>
<input
value="Hide Form"
class="inverse"
type="button"
(click)="formHidden=true"
/>
<form [ngClass]="{'hidden':formHidden}">
<fieldset>
<legend>Contact Form</legend>
</fieldset>
</form>

2. Doble Binding

NgModel

Form

Enlace del modelo hacia la vista

Enlace de la vista hacia el modelo


2.1 NgModel

La directiva ngModel viene en el FormsModule

Hay que importarlo antes de usarlo. Por ejemplo en contacts.module.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ContactsRoutingModule } from './contacts-routing.module';

@NgModule({
imports: [
CommonModule,
ContactsRoutingModule,
FormsModule
]
})
export class ContactsModule { }

Banana in a box [()]

Hace referencia al paréntesis dentro del corchete

1
[(ngModel)]="model.property"

Usa la comunicación en ambos sentidos

  • (banana) : de la vista al modelo
  • [box] : del modelo a la vista

La directiva se asocia con una propiedad del controlador…
o mejor aún, con una propiedad del modelo del controlador


Modelo

1
public contact = { name: '' };

Directiva

1
2
3
4
5
6
7
8
9
<section>
<label for="name">Name</label>
<input
name="name"
type="text"
[(ngModel)]="contact.name"
placeholder="Contact name"
/>
</section>

Espía

1
<pre>{{ contact | json }}</pre>

2.2 Form

Hay más usos de las directivas en los formularios

Por ejemplo, dado el siguiente modelo:

1
public contact = { name: '', isVIP: false, gender: '' };

2.2.1 CheckBox

1
2
3
4
<section>
<label for="isVIP">Is V.I.P.</label>
<input name="isVIP" type="checkbox" [(ngModel)]="contact.isVIP" />
</section>

2.2.2 Radio Buttons

1
2
3
4
5
6
7
<section>
<label for="gender">Gender</label>
<input name="gender" value="m" type="radio" [(ngModel)]="contact.gender" />
<i>Male</i>
<input name="gender" value="f" type="radio" [(ngModel)]="contact.gender" />
<i>Female</i>
</section>

3. Estructuras

*ngFor

*ngIf


3.1 *ngFor

Directiva estructural repetitiva

Dado el siguiente modelo:

1
2
3
4
5
6
7
public workStatuses = [
{ id: 0, description: 'unknow' },
{ id: 1, description: 'student' },
{ id: 2, description: 'unemployed' },
{ id: 3, description: 'employed' }
];
public contact = { name: '', isVIP: false, gender: '', workStatus: 0 };

*ngFor

1
2
3
4
5
6
7
8
<section>
<label for="workStatus">Work Status</label>
<select name="workStatus" [(ngModel)]="contact.workStatus">
<option *ngFor="let status of workStatuses" [value]="status.id">
<span>{{ status.description }}</span>
</option>
</select>
</section>

let iterador of iterable

uso avanzado de trackBy


3.2 *ngIf

Directiva estructural condicional

Dado el siguiente modelo

1
2
3
4
5
6
7
8
public contact = {
name: '',
isVIP: false,
gender: '',
workStatus: '0',
company: '',
education: ''
};

*ngIf

1
2
3
4
5
6
7
8
9
10
11
12
13
<section *ngIf="contact.workStatus=='3'; else education">
<label for="company">Company Name</label>
<input name="company" type="text" [(ngModel)]="contact.company" />
</section>
<ng-template #education>
<section>
<label for="education">Education</label>
<input name="education"
type="text"
[(ngModel)]="contact.education"
placeholder="Education" />
</section>
</ng-template>

if condition else template

también hay *ngSwitch


4. Modelo y controlador

Interfaces y modelos

ViewModel en el controlador


4.1 Interfaces y modelos

Mejor interface que clase

1
2
3
4
5
6
7
8
9
10
11
12
13
export interface Option {
id: number;
description: string;
}

export interface Contact {
name: string;
isVIP: boolean;
gender: string;
workStatus: number | string;
company: string;
education: string;
}

tipos compuestos number | string


Se usan para tipificar las propiedades

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public workStatuses: Option[] = [
{ id: 0, description: 'unknow' },
{ id: 1, description: 'student' },
{ id: 2, description: 'unemployed' },
{ id: 3, description: 'employed' }
];
public contact: Contact = {
name: '',
isVIP: false,
gender: '',
workStatus: 0,
company: '',
education: ''
};
public contacts: Contact[] = [];

4.2 ViewModel en el controlador

No solo propiedades, también métodos

1
2
3
4
5
6
7
8
9
saveContact() {
this.contacts.push({ ...this.contact });
this.updateCounter();
}

private updateCounter() {
this.numberOfContacts = this.contacts.length;
this.counterClass = this.numberOfContacts === 0 ? 'warning' : 'success';
}
1
<input value="Save" type="submit" (click)="saveContact()" />

OnInit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public workStatuses: Option[];
public contact: Contact;
public contacts: Contact[];
constructor() {}
public ngOnInit() {
this.workStatuses = [
{ id: 0, description: 'unknow' },
{ id: 1, description: 'student' },
{ id: 2, description: 'unemployed' },
{ id: 3, description: 'employed' }
];
this.contact = {
name: '',
isVIP: false,
gender: '',
workStatus: 0,
company: '',
education: ''
};
this.contacts = [];
}

Repasamos

1
2
3
4
5
6
7
<ul *ngIf="contacts.length>0; else empty">
<li *ngFor="let contact of contacts">
<span>{{ contact.name }}</span>
<input value="Delete" type="button" (click)="deleteContact(contact)" />
</li>
</ul>
<ng-template #empty> <i>No contacts yet</i> </ng-template>
1
2
3
4
deleteContact(contact: Contact) {
this.contacts = this.contacts.filter(c => c.name !== contact.name);
this.updateCounter();
}

Mira el código completo de la clase ContactsComponenten el fichero contacts.component.ts para tener una visión completa del componente. Como ves, las propiedades header, numContacas, formHidden, contacts ... se corresponden con las utilizadas en las directivas de enlace en la vista. Los métodos saveContact(), deleteContact() son invocados desde eventos de elementos del html.

Juntos, la vista y su clase controladora, resuelven un problema de interacción con el usuario creando un componente. Todas las páginas que diseñes serán variaciones y composiciones de estos componentes.

Y esto es sólo el comienzo. La idea de componente será fundamental en la web del mañana para la creación de páginas mediante web components. Pero eso ya se verá más adelante…

Ahora tienes una aplicación en Angular 9 que recoge y muestra datos. Sigue esta serie para añadirle Flujo de datos entre componentes Angular mientras aprendes a programar con Angular8. Todos esos detalles se tratan en el curso básico online que imparto con TrainingIT o a medida para tu empresa.

Aprender, programar, disfrutar, repetir.
Saludos, Alberto Basalo

Compartir

Código descuento: Curso online de introducción a Angular 💸-25€


Código descuento: Curso online avanzado con Angular 💸-30€

Angular.Builders