Formularze w Angularze V2

2.5 sposobu

Created by Dariusz Biedrzycki / kontakt@3wm.pl / Mobiem.pl

Stara szkoła

[()]

Two way data binding


    <input
        type="text"
        [(ngModel)]="user.login"
        name="login"
    />
                    
            

    initData(){
       this.user = { login:"" };
    }
            

    <button
        (click)="saveData()"
    >
    Register
    </button>
                    
            

    saveData(){
       if(this.validateData(this.user)){
          this.registerApi.save(this.user);
       }
    }
            

    <form
            (ngSubmit)="saveData()"
    />
       <input
         type="submit"
         value="Register"
       />
    </form>
                    
            

    saveData(){
       if(this.validateData(this.user)){
          this.registerApi.save(this.user);
       }
    }
            

    <input
       type="text"
    />
    <p
       *ngIf="loginError"
    >{{loginError}} </p>
                    
            

    saveData(){
       if(this.validateData(this.user)){
          this.registerApi.save(this.user);
       } else {
          this.showErrors();
       }
    }
            

    <input
       type="text"
       (ngChange)="validateLogin()"
    />
    <p
       *ngIf="loginError"
    >{{loginError}} </p>
                    
            

    validateLogin(){
    }
            

Old way

Za

Przeciw

Dowoloność implementacji

Two way data binding

Zmiana modelu w wielu miejscach

Two way data binding

FormsModule

src/app/app.module.ts


    import { FormsModule } from '@angular/forms';
    @NgModule({
        imports: [
           ...
           FormsModule,
           ...
        ],
        ...
    })
    export class AppModule { }
            

src/app/register.component.html


    <form  
       (onSubmit)="onSubmit(f)" 
       #f
    >
       <input
          type="text"
          ngModel
          name="userLogin"
       />
       <button type="submit"> Register </button>
    </form>
            

src/app/register.component.ts


    ...           
    onSubmit(form) {
        console.log(form);
    }
    ...
            

src/app/register.component.html


    <form  
       (onSubmit)="onSubmit(f)" 
       #f
    >
            

src/app/register.component.ts


    ...           
    onSubmit(form:HTMLFormElement) {
        console.log(form);
    }
    ...
            

src/app/register.component.html


    <form  
       (onSubmit)="onSubmit(f)" 
       #f="ngForm"
    >
       <input
          type="text"
          ngModel
          name="userLogin"
       />
       <button type="submit"> Register </button>
    </form>
            

src/app/register.component.ts


    import {NgForm} from "@angular/forms";
    ...           
    onSubmit(form: NgForm) {
        console.log(form);
    }
    ...
            

    {
        controls: {
            userLogin: {
                dirty: false,
                pristine: true,
                invalid: false
                valid: true
                touched: false
                untouched: true
            }
        }
        value: {
            userLogin: "to co wpisaliśmy"
        },
        dirty: false...
    }
            

src/app/register.component.html


    <form  
       (onSubmit)="onSubmit()" 
       #f="ngForm"
    >
            

src/app/register.component.ts


    import {Component, ViewChild} from '@angular/core';
    ...
    @ViewChild('f') registerForm: NgForm;
    ...           
    onSubmit() {
        console.log(this.registerForm);
        if(this.registerForm.valid){
            this.registerApi.save(this.registerForm.value);
        }
    }
    ...
            

state

true

false

touched

ng-touched

ng-untouched

dirty

ng-dirty

ng-pristine

valid

ng-valid

ng-invalid

src/app/register.component.html


    <form (onSubmit)="onSubmit(f)" #f="ngForm">
       <input
          type="text"
          ngModel
          name="userLogin"
          required
          forbiddenName="bob"
       />
       <button type="submit"> Register </button>
    </form>
            

Wbudowane walidatory


class Validators {
  static min(min: number): ValidatorFn
  static max(max: number): ValidatorFn
  static required(control: AbstractControl): ValidationErrors|null
  static requiredTrue(control: AbstractControl): ValidationErrors|null
  static email(control: AbstractControl): ValidationErrors|null
  static minLength(minLength: number): ValidatorFn
  static maxLength(maxLength: number): ValidatorFn
  static pattern(pattern: string|RegExp): ValidatorFn
  static nullValidator(c: AbstractControl): ValidationErrors|null
  static compose(validators: (ValidatorFn|null|undefined)[]|null): ValidatorFn|null
  static composeAsync(validators: (AsyncValidatorFn|null)[]): AsyncValidatorFn|null
}
            

src/app/forbidden-name.directive.ts


export function 
forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} => {
    const forbidden = nameRe.test(control.value);
    return forbidden ? {
        'forbiddenName': {value: control.value}} : null;
  };
}
            

https://angular.io/generated/live-examples/form-validation/eplnkr.html

src/app/register.comonent.css


.ng-valid[required], .ng-valid.required  {
  border-left: 5px solid #42A948; /* green */
}

.ng-invalid:not(form)  {
  border-left: 5px solid #a94442; /* red */
}
            

Template driven

Za

Przeciw

Prostota

Może pisać kto inny niż programista angular

Więcej walidatorów, spada czytelność

Brak możliwości testowania przez unit testy

Reactive forms

src/app/app.module.ts


    import { ReactiveFormsModule } from '@angular/forms';
    @NgModule({
        imports: [
           ...
           ReactiveFormsModule,
           ...
        ],
        ...
    })
    export class AppModule { }
            

src/app/register.component.html


    <form  
       [formGroup]="registerForm" (submit)="onSubmit()"
    >
       <input
          type="text"
          formControlName="userLogin"
       />
       <button type="submit"> Register </button>
    </form>
            

src/app/register.component.ts



    import { FormGroup, FormControl} from '@angular/forms';
    ...
    registerForm:FormGroup;
    ...
    initForm(){
        this.registerForm = new FormGroup({
            userLogin: new FormControl(null)
        });
    }
    ...
            

src/app/register.component.html


    <form  [formGroup]="registerForm" (submit)="onSubmit()">
       <input
          type="text"
          formControlName="userLogin"
       />
       <button type="submit"> Register </button>
    </form>
            

src/app/register.component.ts


    ...
    initForm(){
        this.registerForm = new FormGroup({
            userLogin: new FormControl(null)
        });
    }
    ...
    onSubmit() {
        console.log(this.registerForm);
    }
            

    {
        controls: {
            userLogin: {
                dirty: false,
                pristine: true,
                invalid: false
                valid: true
                touched: false
                untouched: true
            }
        }
        value: {
            userLogin: "to co wpisaliśmy"
        },
        dirty: false...
    }
            

src/app/register.component.html


    <form  [formGroup]="registerForm" (submit)="onSubmit()">
       <input type="text"
          formControlName="userLogin"
       />
       <button type="submit"> Register </button>
    </form>
            

src/app/register.component.ts


    import { FormGroup, FormControl, Validators} 
    from '@angular/forms';
    ...
    registerForm:FormGroup;
    initForm(){
      this.registerForm = new FormGroup({
        userLogin: new FormControl('Janusz', [
          Validators.required,
          this.properName.bind(this)
        ])
      });
    }
    ...
            

src/app/register.component.ts


  properName(control: FormControl): {[s:string]: boolean}{
    if (control.value == 'JanuszBiznesu') {
      return null;
    } else {
      return {'properName': true}
    }
  }
            

    {
        controls: {
            userLogin: {
                errors: {
                    properName: true
                }
            }
        }
        value: {
            userLogin: "to co wpisaliśmy"
        },
        dirty: false...
    }
            

src/app/register.component.html


<form  [formGroup]="registerForm" (submit)="onSubmit()">
  <input type="text" formControlName="userLogin" />
  <div
    *ngIf="
        registerForm.get('userLogin').errors && 
        registerForm.get('userLogin').touched"
  >
    <span *ngIf="registerForm.get('userLogin').errors.properName">
    Prawidłowa nazwa użytkownika to JanuszBiznesu
    </span>
  </div>
  <button type="submit"> Register </button>
</form>
            

src/app/register.component.ts


    import { FormGroup, FormControl, Validators} 
    from '@angular/forms';
    ...
    registerForm:FormGroup;
    initForm(){
      this.registerForm = new FormGroup({
        adress: new FormGroup({
            home: new FormControl(null, Validators.required),
            work: new FormControl(null, Validators.required)
        })
      });
    }
    ...
            

src/app/register.component.ts


    import { FormGroup, FormControl, Validators, FormArray} 
    from '@angular/forms';
    initForm(){
      this.registerForm = new FormGroup({
        hobby: new FormArray([])
      });
    }
    addHobby(){
      (<FormArray>registerForm.get('hobby'))
      .push(
        new FormControl(null, Validators.required)
      )
    }
            

src/app/register.component.ts


    initForm(){
      this.registerForm = new FormGroup({
        userLogin: new FormControl(null)
      });
      this.registerForm.valueChanges.subscribe((value)=>{
        //{
        //    userLogin: "to co wpisaliśmy"
        //}
      });
      this.registerForm.statusChanges.subscribe((value)=>{
        //INVALID i VALID
      });
    }
            

src/app/register.component.ts


  this.registerForm = new FormGroup({
    userLogin: new FormControl(null),
    hobby: new FormArray([]),
    adress: new FormGroup({
        home: new FormControl(null, Validators.required),
        work: new FormControl(null, Validators.required)
    })
  });
  this.registerForm.setValue({
    'userLogin': "Jan"
    'hobby': ['szachy'],
    'addres': {'work':"Warszawa", 'home':"Płock"}
  });
            

src/app/register.component.ts


  this.registerForm = new FormGroup({
    userLogin: new FormControl(null),
    hobby: new FormArray([]),
    adress: new FormGroup({
        home: new FormControl(null, Validators.required),
        work: new FormControl(null, Validators.required)
    })
  });
  this.registerForm.patchValue({
    'userLogin': "Roman"
  });
            

Reactive Way

Za

Przeciw

Możliwe unit testy logiki validacji

Szablony bez logiki

Pełna kontrola nad modelem fomularza

Więcej pisania

src/app/register.component.ts


  this.registerForm.valueChanges
        .map((value) => {
            value.userLogin = value.userLogin.toUpperCase();
            return value;
        })
        .filter((value) => this.form.valid)
        .subscribe((value) => {
           console.log("Model Driven Form valid value: vm = ",
                       JSON.stringify(value));
        });
            

Dziękuję

Dariusz Biedrzycki

kontakt@3wm.pl

Mobiem.pl