Tempo di lettura: 75 minuti
Forms in Angular: approccio code-driven
Come accennato in precedenza, Angular ci fornisce anche un modo basato sul codice (code-driven)per creare moduli. In questa sezione modificheremo il modulo relativo all’ utente , lo renderemo appunto code-driven.
Per utilizzare questo metodo , devi importare ReactiveFormsModule , invece di FormsModule .
Apri app.module.ts ed assicurati di importarlo :
Trova ed aggiungi:
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
importalo:
imports: [ BrowserModule, AppRoutingModule, HttpClientModule, FormsModule, ReactiveFormsModule ],
In Angular, possiamo usare una classe helper chiamata FormBuilder per creare Form Group e Form Control . Di solito, un gruppo di moduli contiene diversi controlli di modulo. Usiamo i controlli del modulo per controllare i campi di input del modulo.
Per rendere le cose più chiare, costruiamo un form usando la classe FormBuilder !
Apri admin/admin-user-create.component.ts e importa quanto segue:
import {FormBuilder, FormGroup} from '@angular/forms';
Una volta che importiamo FormBuilder e FormGroup , possiamo creare un userForm!
Trova:
export class AdminUserCreateComponent implements OnInit {
aggiungi sotto:
userForm: FormGroup;
ora nel costruttore inietta il FormBuilder:
constructor(private userService: UserService, private router: Router, fb: FormBuilder) { this.userForm = fb.group({ email: fb.control(''), name: fb.control(''), password: fb.control('') }); }
Quindi usiamo fb.group ( FormGroup.group ) per creare un gruppo, che contiene diversi controlli: email, nome utente, ecc.
fb.control (”) significa che il valore del campo dovrebbe essere vuoto.
Nota: fb è un nome personalizzato. Puoi usare qualsiasi nome che ti piace.
Infine, possiamo associare questi controlli ai campi di input nella nostra vista ( admin-user-create.component.ts) come segue:
Apri admin-user-create.component.ts e modifica il form:
admin/admin-user-create.component.ts
<div class="container"> <div class="col-md-12"> <div> <a [routerLink]="['/admin/']" class="btn btn-light"> Back</a> </div> </div> <div class="col-md-12"> <div class="card card-body bg-light"> <form novalidate class="form-horizontal" (ngSubmit)="createUser()" [formGroup]="userForm"> <fieldset> <legend>Aggiungi un nuovo utenter</legend> <div class="form-group"> <label for="email" class="col-lg-2 control-label">Email</label> <div class="col-lg-10"> <input type="email" class="form-control" id="email" name="email" placeholder="Inserisci email" formControlName="email"> </div> </div> <div class="form-group"> <label for="name" class="col-lg-2 control-label">Full name</label> <div class="col-lg-10"> <input type="text" class="form-control" id="name" name="name" placeholder="Inserisci nome e cognome" formControlName="name"> </div> </div> <div class="form-group"> <label for="password" class="col-lg-2 control-label">Password</label> <div class="col-lg-10"> <input type="password" class="form-control" id="password" name="password" placeholder="Password" formControlName="password"> </div> </div> <div class="form-group"> <div class="col-lg-10 col-lg-offset-2"> <a [routerLink]="['/admin/']" class="btn btn-light"> Cancel</a> <button type="submit" class="btn btn-primary">Create</button> </div> </div> </fieldset> </form> </div> </div> </div>
Analiziamo il nuovo codice!
Possiamo usare la direttiva formGroup per associare il nostro form al nostro oggetto userForm :
<form novalidate class="form-horizontal" (ngSubmit)="createUser()" [formGroup]="userForm">
novalidate fa sì che il button di invio sia disabilitato finchètutti i campirichiest non sono stati riempiti.
Usiamo anche ngSubmit qui, quindi quando premiamo il pulsante di invio , verrà eseguito il metodo createUser () .
Dopodiché, possiamo associare un controllo modulo al nostro campo di input utilizzando la direttiva formControlName :
<input type="email" class="form-control" id="email" name="email" placeholder="Inserisci email" formControlName="email">
Modifichiamo un attimo il metodo createUser .
Apri admin/admin-user-create.component.ts e cambia:
createUser(user): void { this.userService.addUser(user) .subscribe(response => { this.user = response; console.log(this.user); this.router.navigate(['/admin/users']); } ); }
a così:
createUser() { console.log(this.userForm.value); }
ora prova ad inserire qualcosa su http://localhost:4200/admin/users/create
vedete la console?
Angular:Convalidare il modulo
Nel modulo basato sul codice(code-driven) , utilizziamo un Validator per convalidare il nostro modulo. Ecco alcune regole di convalida fornite per impostazione predefinita:
- Validators.required : il controllo non deve essere vuoto.
- Validators.minLength (numero) : il controllo deve contenere almeno un numero di caratteri.
- Validators.maxLength (numero) : il controllo deve contenere al massimo il numero di caratteri.
- Validators.pattern (pattern) : il controllo deve corrispondere al pattern dell’espressione regolare .
Per utilizzare Validator , dobbiamo prima importarlo.
Apri admin/admin-user-create.component.ts e trova:
import {FormBuilder, FormGroup} from '@angular/forms';
aggiungi:
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
ora siamo in grado di aggiungere alcune regole di convalida nel costruttore:
constructor(private userService: UserService, private router: Router, fb: FormBuilder) { this.userForm = fb.group({ email: fb.control('', [Validators.required, Validators.minLength(3)]), name: fb.control('', [Validators.required, Validators.minLength(3)]), password: fb.control('', [Validators.required, Validators.minLength(6)]) }); }
Come vedi, tutti i campi devono essere obbligatori. l’email e il nome devono contenere almeno 3 caratteri. Il campo password deve contenere almeno 6 caratteri.
Il modulo basato sul codice contiene anche gli stati del modulo ( validi / non validi ). Utilizzando lo stato non valido(novalidate) , possiamo disabilitare il pulsante di invio se il modulo non è valido (come detto in precedenza) per cui abilitiamolo una volta che i campi sono stati correttamente compilati:
Apri admin/admin-user-create.component.ts e trova:
<button type="submit" class="btn btn-primary">Create</button>
aggiungi:
<button type="submit" class="btn btn-primary" [disabled]="userForm.invalid">Create</button>
Controlla il modulo nel tuo browser, il pulsante Crea è disattivato se il modulo non è valido.
La cosa interessante è che, se non ti andasse di usare il Validator , potresti utilizzare semplicemente l’ attributo request o altri attributi in questo tipo di modulo. Per esempio:
<input type="text" class="form-control" id="email" name="email" placeholder="Email of the user" formControlName="email" required>
Vediamo ora di visualizzare gli alert a video nel momento in cui il modulo no è compilato correttamente ?
Un Validator restituirà una mappa di errori se un controllo non è valido. Possiamo visualizzare messaggi di errore usando ngIf in questo modo:
<div *ngIf="userForm.get('email').dirty && userForm.get('email').hasError('required')" class="alert alert-danger">Email is required</div> <div *ngIf="userForm.get('email').dirty && userForm.get('email').hasError('minlength')" class="alert alert-danger">Email should have at least 3 characters</div>
Fondamentalmente, il codice sopra significa: “Ok, se questo modulo è sporco(dirty) e se ha un errore, visualizza un messaggio di errore”.
Ecco il modulo aggiornato :
admin/admin-user-create.component.ts
<div class="container"> <div class="col-md-12"> <div> <a [routerLink]="['/admin/']" class="btn btn-light"> Back</a> </div> </div> <div class="col-md-12"> <div class="card card-body bg-light"> <form novalidate class="form-horizontal" (ngSubmit)="createUser()" [formGroup]="userForm"> <fieldset> <legend>Aggiungi un nuovo utenter</legend> <div class="form-group"> <label for="email" class="col-lg-2 control-label">Email</label> <div class="col-lg-10"> <input type="email" class="form-control" id="email" name="email" placeholder="Inserisci email" formControlName="email"> <div *ngIf="userForm.get('email').dirty && userForm.get('email').hasError('required')" class="alert alert-danger">Email richiesta</div> <div *ngIf="userForm.get('email').dirty && userForm.get('email').hasError('minlength')" class="alert alert-danger">Devi scrivere almeno 3 caratteri</div> </div> </div> <div class="form-group"> <label for="name" class="col-lg-2 control-label">Full name</label> <div class="col-lg-10"> <input type="text" class="form-control" id="name" name="name" placeholder="Inserisci nome e cognome" formControlName="name"> <div *ngIf="userForm.get('name').dirty && userForm.get('name').hasError('required')" class="alert alert-danger">Nome richiesto</div> <div *ngIf="userForm.get('name').dirty && userForm.get('name').hasError('minlength')" class="alert alert-danger">Il nome deve contenere almeno 3 caratteri</div> </div> </div> <div class="form-group"> <label for="password" class="col-lg-2 control-label">Password</label> <div class="col-lg-10"> <input type="password" class="form-control" id="password" name="password" placeholder="Password" formControlName="password"> <div *ngIf="userForm.get('password').dirty && userForm.get('password').hasError('required')" class="alert alert-danger">Password richiesta</div> <div *ngIf="userForm.get('password').dirty && userForm.get('password').hasError('minlength')" class="alert alert-danger">La password deve contenere almeno 6 caratteri</div> </div> </div> <div class="form-group"> <div class="col-lg-10 col-lg-offset-2"> <a [routerLink]="['/admin/']" class="btn btn-light"> Cancel</a> <button type="submit" class="btn btn-primary" [disabled]="userForm.invalid">Create</button> </div> </div> </fieldset> </form> </div> </div> </div>
ora se fai una prova ed a sbagliare di preposito vedrai una serie di errori a video:
Convalida personalizzata
Con l’ approccio basato sul codice(code-driven) , possiamo creare regole di convalida aggiuntive per convalidare i nostri controlli.
Supponiamo di voler creare una nuova regola di posta elettronica per convalidare il campo email, possiamo farlo seguendo questi passaggi:
Per prima cosa, dobbiamo importare FormControl nel nostro componente. Apri admin/admin-user-create.component.ts e importa FormControl :
import {FormBuilder, FormGroup, Validators, FormControl} from '@angular/forms';
quindi aggiungi il metodo sopra il costruttore:
static isValidEmail(control: FormControl): any { const emailRegexp = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; return emailRegexp.test(control.value) ? null : { invalidEmail: true }; }
Come puoi vedere ho creato un nuovo metodo statico chiamato isValidEmail . Successivamente, utilizzo un’espressione regolare per verificare se il valore di controllo è un’e-mail valida:
const emailRegexp = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
Per verificare il valore, possiamo utilizzare il metodo emailRegexp () . Il metodo restituisce null se il valore di controllo è un’e-mail valida e restituisce {invalidEmail: true} se il valore non è un’e-mail valida.
Successivamente, possiamo applicare la regola al controllo dell’email in questo modo:
Trova:
email: fb.control('', [Validators.required, Validators.minLength(3)]),
sostituisci con:
email: fb.control('', [Validators.required, Validators.minLength(3), AdminUserCreateComponent.isValidEmail]),
quindi aggiungi il controllo nel template, trova:
<input type="email" class="form-control" id="email" name="email" placeholder="Inserisci email" formControlName="email">
aggiungi di seguito:
<div *ngIf="userForm.get('email').dirty && userForm.get('email').hasError('invalidEmail')" class="alert alert-danger">Formato email non valido</div>
provate e vedrete in azione il nuovo controllo
Esistono molti modi per creare validatori personalizzati, ma questo è il modo più semplice. Se lo desideri, puoi creare un validatore personalizzato in un’altra classe e quindi importarlo nel tuo componente quando necessario.
A proposito, c’è un buon pacchetto chiamato ngx-validators che possiamo usare per aggiungere più validatori alla nostra app Angular. Ecco un elenco di validatori:
- Password
- universal
- Carte di credito
In alternativa, puoi utilizzare Ng2 Validation Manager , che si basa sul metodo Laravel Validation.
Assicurati di controllarli!
NOTA:
Stati di forma angolare
Il form di Angular è intelligente. Può essere in grado di dirci quando l’utente ha toccato il controllo, quando il valore è cambiato o quando il valore è diventato non valido. Puoi pensare a questi eventi come a stati del form . Angular inoltre aggiunge e rimuove automaticamente alcune classi CSS su un campo in modo che possiamo facilmente modellare il nostro modulo o messaggi.
Ad esempio, un form sarà valido e avrà la classe ng-valid , quando i validatori avranno successo .
Ecco alcune classi di stato CSS di Angular:
- valid (ng-valid): il modulo è valido.
- invalid (ng-invalid): il modulo non è valido.
- dirty (ng-dirty): il modulo è stato modificato.
- pristine (ng-pristine): il modulo non è stato modificato.
- touched (ng-touched): il form è stato toccato.
- untouched (ng-untouched):il form non è stato toccato.
Angular: Realizzare una classe personalizzata per il controllo della password
In un form che si rispetti nel momento in cui si inserisce la password per una registrazione di un nuovo utente di solito si chiedono caratteri speciali ,numeri, caratteri maiuscoli. Insomma è obbligatorio ,oggi, avere una password sicura!
Bhe noi lo faremo realizzando una classe ad hoc (lo stesso potevamo fare con l’email, ma va bene così 🙂 ).
Aprite il terminale e digitate:
ng g cl classe/custom-validators
ok apritela ed aggiungete:
import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms'; export class CustomValidators { static patternValidator(regex: RegExp, error: ValidationErrors): ValidatorFn { return (control: AbstractControl): { [key: string]: any } => { if (!control.value) { // if control is empty return no error return null; } // test the value of the control against the regexp supplied const valid = regex.test(control.value); // if true, return no error (no error), else return error passed in the second parameter return valid ? null : error; }; } static passwordMatchValidator(control: AbstractControl): any { const password: string = control.get('password').value; // get password from our password form control const confirmPassword: string = control.get('confirmPassword').value; // get password from our confirmPassword form control // compare is the password math if (password !== confirmPassword) { // if they don't match, set an error in our confirmPassword form control control.get('confirmPassword').setErrors({ NoPassswordMatch: true }); } } }
come vedete è simile al metodo creato per l’email, inoltre ho già implementato il metodo che gestirà la conferma della password che vedremo in seguito.
Ora aprite admin/admin-create-user.ts e modificate il costruttore:
constructor(private userService: UserService, private router: Router, fb: FormBuilder) { this.userForm = fb.group({ email: fb.control('', [Validators.required, Validators.minLength(3), AdminUserCreateComponent.isValidEmail]), name: fb.control('', [Validators.required, Validators.minLength(3)]), password: fb.control('', [ Validators.required, Validators.minLength(8), Validators.maxLength(32), CustomValidators.patternValidator(/\d/, { hasNumber: true }), CustomValidators.patternValidator(/[A-Z]/, { hasCapitalCase: true }), CustomValidators.patternValidator(/[a-z]/, { hasSmallCase: true }), CustomValidators.patternValidator(/[@#{}|!()*{}]/, { hasSpecialCharacters: true }), ]) }); }
ricordatevi di importare la classe CustomValidators:
import {CustomValidators} from '../../classe/custom-validators';
nel template trovate :
<input type="password" class="form-control" id="password" name="password" placeholder="Password" formControlName="password">
di seguito aggiungete:
<div *ngIf="userForm.get('password').dirty && userForm.get('password').hasError('hasNumber')" class="alert alert-danger">La password deve contenere anche numeri</div> <div *ngIf="userForm.get('password').dirty && userForm.get('password').hasError('hasCapitalCase')" class="alert alert-danger">La password deve contenere anche caratteri maiuscoli</div> <div *ngIf="userForm.get('password').dirty && userForm.get('password').hasError('hasCamelCase')" class="alert alert-danger">La password deve contenere anche caratteri minuscoli</div> <div *ngIf="userForm.get('password').dirty && userForm.get('password').hasError('hasSpecialCharacters')" class="alert alert-danger">La password deve contenere almeno un carattere speciale: @ # | ! * </div>
questi sono i controlli relativi al corretto formato della password.
Ok ora riaggiorniamo il metodo createUser() su admin/admin-user-create.component.ts in modo tale da aggungere un utente:
da così:
createUser(): void { console.log(this.userForm.value); }
a così:
createUser(): void { this.userService.addUser(this.userForm.value) .subscribe(response => { this.user = response; console.log(this.user); this.router.navigate(['/admin/users']); } ); } }
ed ecco il risultato identico a quando utilizzavamo il template-form: