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