Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Formulare und Validierung in Angular

Formulare und Validierung in Angular

Slides from my talk at Angular Days, Oct 2017 in Berlin

15934fa2aa7b2ce21f091e9b7cffa856?s=128

Manfred Steyer
PRO

October 11, 2017
Tweet

Transcript

  1. Formulare und Validierung: Deep Dive Manfred Steyer SOFTWAREarchitekt.at ManfredSteyer ManfredSteyer

  2. Über mich … • Manfred Steyer • SOFTWAREarchitekt.at • Trainer

    & Consultant • Focus: Angular • Google Developer Expert (GDE) Page ▪ 2 Manfred Steyer
  3. Ansätze in Angular • ngModel im Template • Angular erzeugt

    Objektgraph für Formular • FormsModule Template- getrieben • Anwendung erzeugt Objektgraph • Mehr Kontrolle • ReactiveFormsModule Reaktiv • Angular generiert Formular für Datenmodell • An Community übergeben Daten- getrieben
  4. Template- getriebene Formulare

  5. Template-getriebene Formulare Page ▪ 7 export class FlugSuchenComponent { von:

    string; nach: string; constructor(flugService: FlugService) { von = 'Graz'; nach = 'Hamburg'; } }
  6. View Page ▪ 8 <form> <input type="text" name="von" [(ngModel)]="von" required

    minlength="3"> […] </form> NgForm valid, dirty, … Control valid, dirty, … controls { } von
  7. View Page ▪ 9 <form #f="ngForm"> <input type="text" name="von" [(ngModel)]="von"

    required minlength="3"> […] </form> NgForm valid, dirty, … Control valid, dirty, … controls { } von
  8. View Page ▪ 10 <form #f="ngForm"> <input type="text" name="von" [(ngModel)]="von"

    required minlength="3"> <div *ngIf="!f.controls['von'].valid"> …Error… </div> </form> NgForm valid, dirty, … Control valid, dirty, … controls { } von
  9. View Page ▪ 11 <form #f="ngForm"> <input type="text" name="von" [(ngModel)]="von"

    required minlength="3"> <div *ngIf="!f?.controls['von']?.valid"> …Error… </div> </form> NgForm valid, dirty, … Control valid, dirty, … controls { } von
  10. View Page ▪ 12 <form #f="ngForm"> <input type="text" name="von" [(ngModel)]="von"

    required minlength="3"> <div *ngIf="!f?.controls['von']?.valid"> …Error… </div> <div *ngIf="f?.controls['von'].hasError('required')"> …Error… </div> </form> NgForm valid, dirty, … FormControl valid, dirty, … controls { } von
  11. DEMO Page ▪ 13

  12. Eigene Validierungs- Regeln Page ▪ 14

  13. Direktiven • Fügen Verhalten zur Seite hinzu • Beispiel: ngModel,

    ngClass, ngIf, ngFor • Kein Template im Gegensatz zu Komponenten Page ▪ 15
  14. Validierungs-Direktive <input [(ngModel)]="von" name="von" ort> Page ▪ 16

  15. Validierungs-Direktive Page ▪ 17 @Directive({ selector: 'input[ort]' }) export class

    OrtValidatorDirective implements Validator { validate(c: AbstractControl): any { let value = c.value; […] if (…) return { ort: true }; return {}; // Kein Fehler } }
  16. Validierungs-Direktive Page ▪ 18 @Directive({ selector: 'input[ort]', providers: [{ provide:

    NG_VALIDATORS, useExisting: OrtValidatorDirective, multi: true}] }) export class OrtValidatorDirective implements Validator { validate(c: AbstractControl): any { let value = c.value; […] if (…) return { ort: true }; return {}; // Kein Fehler } } .hasError(‘ort’)
  17. Attribute berücksichtigen <input [(ngModel)]="von" name="von" [ort]="['Graz', 'Hamburg', 'Zürich']"> Page ▪

    19
  18. Attribute berücksichtigen <input [(ngModel)]="von" name="von" [ort]="'Graz, Hamburg, Zürich'"> Page ▪

    20
  19. Attribute berücksichtigen Page ▪ 22 @Directive({ selector: 'input[ort]', providers: [{

    provide: NG_VALIDATORS, useExisting: OrtValidatorDirective, multi: true }] }) export class OrtValidatorDirective implements Validator { @Input() ort: string; validate(c: AbstractControl): any { […] } }
  20. Attribute berücksichtigen Page ▪ 23 @Directive({ selector: 'input[ort]', providers: [{

    provide: NG_VALIDATORS, useExisting: OrtValidatorDirective, multi: true }] }) export class OrtValidatorDirective implements Validator { @Input() ort: string; @Input() strategy: string; validate(c: AbstractControl): any { […] } }
  21. Attribute berücksichtigen <input [(ngModel)]="von" name="von" [ort]="'Graz, Hamburg, Zürich'" [strategy]="'strict'"> Page

    ▪ 24
  22. Attribute berücksichtigen <input [(ngModel)]="von" name="von" ort="Graz, Hamburg, Zürich" strategy="strict"> Page

    ▪ 25
  23. DEMO

  24. Multi-Field-Validatoren @Directive({ selector: 'form[roundTrip]', providers: [ … ] }) export

    class RoundTripValidatorDirective implements Validator { validate(control: AbstractControl): any { […] } }
  25. Multi-Field-Validatoren export class RoundTripValidatorDirective implements Validator { validate(control: AbstractControl): any

    { let group = control as FormGroup; let from = group.controls['from']; let to = group.controls['to']; if (!from || !to) return { }; […] }
  26. Multi-Field-Validatoren export class RoundTripValidatorDirective implements Validator { validate(control: AbstractControl): any

    { let group = control as FormGroup; let from = group.controls['from']; let to = group.controls['to']; if (!from || !to) return { }; if (from.value === to.value) return { roundTrip: true }; return { }; } }
  27. Asynchrone Validierungs-Direktiven Page ▪ 30 @Directive({ selector: 'input[asyncCity]', providers: [

    … ] }) export class AsyncCityValidatorDirective { validate(control: AbstractControl): Observable<any> { […] } }
  28. Asynchrone Validierungs-Direktiven Token: NG_ASYNC_VALIDATORS Page ▪ 31

  29. DEMO Page ▪ 32

  30. Reaktive Formulare

  31. ReactiveFormsModule @NgModule({ imports: [ ReactiveFormsModule, CommonModule, SharedModule, [...] ], [...]

    }) export class FlightBookingModule { }
  32. Reaktive Formulare export class FlugSuchenComponent { form: FormGroup; […] }

  33. Reaktive Formulare export class FlugSuchenComponent { form: FormGroup; constructor(…) {

    let fromControl = new FormControl('Graz'); let toControl = new FormControl('Hamburg'); this.form = new FormGroup({ from: fromControl, to: toControl}); […] } }
  34. Reaktive Formulare export class FlugSuchenComponent { form: FormGroup; constructor(…) {

    let fromControl = new FormControl('Graz'); let toControl = new FormControl('Hamburg'); this.form = new FormGroup({ from: fromControl, to: toControl}); fromControl.validator = Validators.require; […] } }
  35. Reaktive Formulare export class FlugSuchenComponent { form: FormGroup; constructor(…) {

    let fromControl = new FormControl('Graz'); let toControl = new FormControl('Hamburg'); this.form = new FormGroup({ from: fromControl, to: toControl}); fromControl.validator = Validators.require; fromControl.asyncValidator = […]; […] } }
  36. Reaktive Formulare export class FlugSuchenComponent { form: FormGroup; constructor(…) {

    let fromControl = new FormControl('Graz'); let toControl = new FormControl('Hamburg'); this.form = new FormGroup({ from: fromControl, to: toControl}); fromControl.validator = Validators.compose([Validators.require, Validators.minLength(3)]); fromControl.asyncValidator = Validators.composeAsync([ … ]); […] } }
  37. FormBuilder export class FlugSuchenComponent { form: FormGroup; constructor(fb: FormBuilder, …)

    { this.form = fb.group({ von: ['Graz', Validators.required], nach: ['Hamburg', Validators.required] }); […] } }
  38. FormBuilder export class FlugSuchenComponent { form: FormGroup; constructor(fb: FormBuilder, …)

    { this.form = fb.group({ von: ['Graz', [Validators.required, Validators.minLength(3)] ], nach: ['Hamburg', Validators.required] }); […] } }
  39. FormBuilder export class FlugSuchenComponent { form: FormGroup; constructor(fb: FormBuilder, …)

    { this.form = fb.group({ von: ['Graz', [Validators.required, Validators.minLength(3)], [ /* asyncValidator */ ] ], nach: ['Hamburg', Validators.required] }); […] } }
  40. API this.form.valueChanges.subscribe(change => { console.debug('formular hat sich geändert', change); });

    this.form.controls['from'].valueChanges.subscribe(change => { console.debug('from hat sich geändert', change); }); let fromValue = this.form.controls['from'].value; let toValue = this.form.controls['to'].value; let formValue = this.form.value;
  41. Reaktive Formulare Page ▪ 44 <form [formGroup]="form"> <input id="from" formControlName="von"

    type="text"> […] </form>
  42. Reaktive Formulare Page ▪ 45 <form [formGroup]="form"> <input id="from" formControlName="von"

    type="text"> <div *ngIf="!form.controls.von.valid">…Error…</div> […] </form>
  43. DEMO

  44. Validatoren für reaktive Formulare

  45. Reaktive Validatoren == Funktionen

  46. Ein einfacher Validator export class CityValidators { static validate (c:

    AbstractControl): any { if (c.value == 'Graz' || c.value == 'Hamburg') { return { }; } return { city: true }; } […] }
  47. Parametrisierte Validatoren export class CityValidators { static validateWithParams(allowedCities: string[]) {

    […] } […] }
  48. Parametrisierte Validatoren export class CityValidators { static validateWithParams(allowedCities: string[]) {

    return (c: AbstractControl): any => { […] }; } […] }
  49. Parametrisierte Validatoren export class CityValidators { static validateWithParams(allowedCities: string[]) {

    return (c: AbstractControl): any => { if (allowedCities.indexOf(c.value) > -1) { return { } } return { city: true }; }; } […] }
  50. Validatoren anwenden this.form = fb.group({ von: [ 'Graz', [ CityValidators.validate,

    CityValidators.validateWithParams(['Graz', 'Hamburg']) ], [ /* asyncValidator */ ] ], nach: ['Hamburg', Validators.required] });
  51. DEMO

  52. Kontakt [mail] manfred.steyer@SOFTWAREarchitekt.at [blog] SOFTWAREarchitekt.at [twitter] ManfredSteyer