Enhance Web Development
Angular 2
Forms
Les formulaires
- Kezako
- Template driven forms
- Model driven forms
- Validations
Links
Kezako
- Les formulaires sont la première forme d'intéraction avec l'utilisateur pour mettre à jour des données
- Les formulaires angular 2 (model driven forms) remplacent ceux de angular 1 (template driven forms)
Configuration
import { FormsModule } from '@angular/forms'
@NgModule({
imports: [
...,
FormsModule
],
declarations: [
...,
],
bootstrap: [
...,
]
})
export class AppModule { }
Processus
component
ng2 controls
Component view
- pristine
- dirty
- touched
- untouched
- errors
- valid
Template driven forms
ngModel
- Utilise la notation du two-way bindings : [(ngModel)]
[(ngModel)] - Une des seules directives à utiliser le two-way binding
- Il permet de lier un champs input avec un model
- Si on le bind à un type primitif => Erreur
Besoin de préciser [ngModelOptions]="{standalone:true}"
@Component({
selector: "my-form",
template: `<input type="text" [(ngModel)]="user.name">`
})
class MyForm {
user: User = new User('name')
}
ngModel
Validations
- Comme Angular 1, ngModel nous donne accès à l'état du formulaire et du champs
@Component({
selector: "my-form",
template: `
<input type="text" [(ngModel)]="user.name" #field="ngModel">
<pre>
{{ field.valid }}
</pre>
`
})
class MyForm {
user: User = new User('name')
}
ngModel
Model driven forms
Formulaires simples
- Utilisation de FormBuilder et de Validators dans le composant
- Utilisation des directives ngFormModel et ngControl dans la vue
<form [formGroup]="loginForm">
<p>Login <input formControlName="login"></p>
<div formGroupName="passwordRetry">
<p>Password <input type="password" formControlName="password"></p>
<p>Confirm password <input type="password" formControlName="passwordConfirmation"></p>
</div>
</form>
Formulaire simple : la vue
import { Component } from '@angular/core'
import { REACTIVE_FORM_DIRECTIVES, FormBuilder, FormGroup, Validators } from '@angular/forms'
@Component({
selector: 'nat-login',
template: require('./login.component.html'),
directives: [ REACTIVE_FORM_DIRECTIVES ]
})
export class LoginComponent {
loginForm: FormGroup
constructor(private _formBuilder: FormBuilder) {
this.loginForm = _formBuilder.group({
email: ["", Validators.required],
passwordRetry: _formBuilder.group({
password: ["", Validators.required],
passwordConfirmation: ["", Validators.required, asyncValidator]
})
})
}
}
Formulaire simple : le composant
FormBuilder
-
group
Créer un group de formulaire -
array
Créer un tableau (exemple: liste d'adresse) -
control
Créer un validateur/Contrôle
Directive form
- Plus aucun ngModel
- Le bind se fait au travers du formGroup ou du formControlName
function containsTroll(c: Control) {
if(c.value.indexOf('troll') >= 0) {
return {
noTroll: true
}
}
return null
}
this.loginForm = _formBuilder.group({
email: ['', containsTroll]
password: ['', Validators.required],
});
Custom validator
Validations
Validateurs natifs
- Angular 2 propose 5 validations natives :
- Validators.required
- Validators.minLength
- Validators.maxLength
- Validators.pattern
- Validators.nullValidator
// Component
this.name = new Control('', Validators.minLength(4));
// View
<input required type="text" formControlName="name" />
<div [hidden]="name.dirty && !name.valid">
<p [hidden]="name.errors.minlength">
Your name needs to be at least 4 characters.
</p>
</div>
Validateurs natifs
Validateurs personnalisés
- Nos validateurs personnalisées doivent respecter :
- retourner null si valide (ok ok ... bizarre)
- Respecter l'interface suivante
interface ValidationResult {
[key:string]:boolean;
}
// Component
const containsTroll: ValidatorFn = (c: AbstractControl) => {
if(c.value.indexOf('troll') >= 0) {
return {
noTroll: true
}
}
return null
}
// ...
name: ["", Validators.required, containsTroll],
// ...
// View
<input required type="text" formControlName="name" />
<div [hidden]="name.dirty && !name.valid">
<p [hidden]="name.errors.noTroll">
There is "troll" in your name ...
</p>
</div>
Validateurs personnalisés
Validateurs asynchrone
- Fonctionne de la même façon qu'un validateur personnalisé, mais retourne une Promise
- S'initialise avec le troisième paramètre de Control(name, validators, asyncValidators)
// Component
function exists(c: Control): Promise<ValidationResult> {
let q = new Promise((resolve, reject) => {
setTimeout(() => {
if (c.value === 'George') {
resolve({"usernameExists": true})
} else {
resolve(null)
}
}, 1000)
})
return q
}
email: ["", Validators.required, asyncName],
// View
<input required type="text" formControlName="name" />
<p [show]="name.pending">
Fetching data from server!
</p>
<div [hidden]="name.dirty && !name.valid && !name.pending">
<p [hidden]="name.errors.required">
Your name is required!
</p>
<p [hidden]="name.errors.usernameExists">
This username already exists! sad ;-(
</p>
</div>
Validateurs asynchrones
let validators = Validators.compose(Validators.required, containsTroll)
let asyncValidators = Validators.composeAsync(exists, exists1)
Validateurs combinés
Demo time !
Défi
- Créer un nouveau composant widget-detail qui prend en input un widget
- Dans ce composant, afficher un formulaire pour éditer ses valeurs
- Créer un formulaire pour créer un widget
linalis - 14 - Forms
By Benjamin LONGEARET
linalis - 14 - Forms
- 1,172