8 forms e direttiva nGModel

Nella lezione precedente eravamo rimasti che al click sul pulsante modifica, tramite la direttiva *ngIf , visualizzavamo il form . Ora dobbiamo fare in modo che il form venga popolato dei campi relativi all’utente selezionato, quindi l’app.component passi l’oggetto user al componente user-detail tramite una variabile che andiamo subito a dichiarare nella classe di user-detail.component

@Input() user: User;

quindi sarà una variabile che riceve dati (@Input) la chiamiamo user e sarà di tipo User. Automaticamente si importa sia l’interfaccia che l’ Input in user-detail

import {Component, Input, OnInit} from '@angular/core';
import {User} from '../interfaces/user';

a questo punto siamo in grado di ricevere i dati iniettandoli nell’app.component tramite la variabile user in ingresso, quindi con parentesi quadre, che prenderà i valori di userSelected

<app-users-detail [user]="userSelected"></app-users-detail>

adesso in cima al template user-detail, mostriamo la variabile user. Useremo la sintassi col pipe di tipo json che affronteremo più avanti

{{user | json}}

Notiamo che sopra al form ci saranno i dati dell’utente scelto, ora però dobbiamo fare in modo che i campi del form vengano popolati con i relativi campi presi dall’oggetto user, ovvero dobbiamo effettuare il binding tra proprietà dell’oggetto e campi del form.

ngModel

Per creare questa relazione di binding tra elementi, possiamo utilizzare la direttiva di angular ngModel.

Per utilizzare ngModule bisogna che venga importato FormsModule nell’ app.module.ts (a seconda dell’ide potrebbe venire importato automaticamente)

import {FormsModule} from '@angular/forms';

FormsModule contiene diverse direttive, tra cui quella per i form, per i formgroup, per ngModel, per ngGroup.

Usiamo nel form la direttiva ngModel

<input [(ngModel)]="user.nome" name="firstname" id="firstname">

Noteremo che adesso il campo Nome del form riceverà il relativo dato selezionato.

Da notare le doppie parentesi, le quadre significano che il dato è in input, le tonde in output, in quanto il valore che andremo a modificare ricevuto in input nel campo, dovrà essere salvato in output nell’array.

Se avessimo usato solo le parentesi quadre, riceveremmo solo il valore nel campo, ma non verrebbe applicata nessuna modifica. Il sistema di corrispondenza in un solo senso prende il nome di One-way data binding.

Nel modo che abbiamo usato, al cambio di valore nel campo, viene automaticamente cambiato nei dati della tabella, in quanto il valore ricevuto è per riferimento, mentre noi vogliamo salvare solo alla fine e aggiornare i records.

Dobbiamo quindi creare una copia del valore, la quale sovrascriverà il valore stesso solo alla fine e non potrà agire direttamente sul valore cambiando il dato in tempo reale nella tabella e nell’array.

Per creare una copia, lavoriamo sulla funzione onSelectUser() in users.component sfruttando il metodo assign() dell’oggetto Object, al quale passeremo un oggetto vuoto { } e poi l’oggetto da cui copiare (per noi user)

onSelectUsers(user: User) {
 const userCopy = Object.assign({}, user);
 this.updateUser.emit(userCopy);
}

assign() prende e crea una copia di un oggetto passato come secondo parametro ( user ), prendendone i valori e mettendoli nell’oggetto passato come primo parametro ( { } ). Per dirla in breve, fa una copia del secondo oggetto sul primo.

Aggiungiamo un metodo saveUser() sul click del pulsante salva

<button class="btn-save" (click)="saveUser()">SALVA</button>

al metodo saveUser() non dobbiamo passare nessun parametro, perchè nella classe di user-detail abbiamo già l’oggetto user che contiene i dati dell’ array.

Aggiungiamo il campo id nell’interface e nell’array, per simulare meglio i dati passati da un database e completiamo gli ngModel del vari campi input del form

<form>
<div class="form-group">
<input type="hidden" name="id" [(ngModel)]="user.id" >
<label for="firstname">Nome</label>
<input [(ngModel)]="user.nome" name="firstname" id="firstname">
</div>
<div class="form-group">
<label for="lastname">Cognome</label>
<input [(ngModel)]="user.cognome" name="lasttname" id="lasttname">
</div>
<div class="form-group">
<label for="fiscalcode">Codice fiscale</label>
<input [(ngModel)]="user.cf" name="fiscalcode" id="fiscalcode">
</div>
<div class="form-group">
<label for="phone">Telefono</label>
<input [(ngModel)]="user.tel" name="phone" id="phone">
</div>
<div class="form-group">
<label for="email">Email</label>
<input [(ngModel)]="user.mail" type="email" name="email" id="email">
</div>
<div class="form-group">
<label for="age">Età</label>
<input [(ngModel)]="user.eta" name="age" id="age">
</div>
<div class="form-group form-footer">
<button class="btn-save" (click)="saveUser()">SALVA</button>
</div>
</form>

Come si può vedere abbiamo aggiunto un input hidden per l’id che ci permette di passarlo e sapere quale id stiamo modificando, facciamo un alert col metodo saveUser()

saveUser() {
alert(this.user.id);
}

L’id ci è utile anche per capire se l’utente esiste già e nel caso aggiungerlo per creare un nuovo utente, quindi dobbiamo implementare l’ interface, che andiamo a rinominare UserInterface, in una nuova classe User dentro una nuova cartella classes

import {UserInterface} from '../interfaces/user';

export class User implements UserInterface {
 id: number;
 nome: string;
 cognome: string;
 cf: string;
 mail: string;
 tel: string;
 prov: string;
 eta: number;
}

andiamo a modificare i riferimenti sostituendo il percorso di import di interfaces con classes nei vari moduli.

L’utente User di app.component lo inizializiamo ora come oggetto della classe User

userSelected: User = new User();

Così facendo adesso il form compare subito senza cliccare su modifica e se andiamo a cliccare su salva, l’alert id ci risponde con undefined, quindi inizializziamo in un costruttore i valori di default

constructor() {
 this.id = 0;
 this.nome = '';
 this.cognome = '';
 this.cf = '';
 this.mail = '';
 this.tel = '';
 this.prov = '';
 this.eta = 18;
}

ora aggiungiamo una variabile nel app.component showform inizializzata a false e che cambia a true nel metodo updateUser()

export class AppComponent {
 showForm = false;
 userSelected: User = new User();
 updateUser(user: User) {
 this.showForm = true;
 this.userSelected = user;
}

e nel template dell’app.component basiamo *ngIf su showForm anzichè userSelected per non mostrare il form fin da subito, ma solo quando clicchiamo sui bottoni