1

Angular5 Reactive Form

根据最近的使用, 总结一下在ngx中使用reactive form

1. 创建表单

需求: 创建一个带验证的表单, 如果表单验证不通过则提交按钮disabled=true

<!-- app.component.html -->

<form [formGroup]="form">

  <div class="form-group">
    <label for="">name: </label>
    <input type="text" formControlName="name">
  </div>
  <div class="form-group">
    <label for="">password: </label>
    <input type="password" formControlName="password">
  </div>

  <div class="form-group">
    <button type="submit" (click)="submit()" [disabled]="form.invalid">submit</button>
    <button type="button" (click)="reset()">reset</button>
  </div>
    
</form>
// app.component.ts

import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  name = 'app';

  form: FormGroup;

  constructor(
    private fb: FormBuilder
  ){}

  ngOnInit(){
    this.form = this.fb.group({
      name: ['', Validators.required],
      password: ['', [Validators.required, Validators.minLength(6)]]
    });
  }

  submit(){
    if(this.form.valid){
      console.log('submiting...')
    }else{
      console.log('form is not valid')
    }
  }

  reset(){
    this.form.reset();
  }
}

2. 自定义验证器

需求: 密码需要格式为数字字母下划线6-12位

参考: Custom validators

// app.component.ts

...

// 自定义密码验证
function ValidPwd(control: AbstractControl):any {
  const reg = /^\w{6,12}$/;
  if(reg.test(control.value)){
    // 通过验证时需要返回 null
    return null;
  }
  return {status: 'error', message: '密码格式为数字字母下划线6-12位'}
}

...
export class AppComponent implements OnInit {
...
  ngOnInit(){
    this.form = this.fb.group({
      name: ['', Validators.required],
      password: ['', [Validators.required, ValidPwd]]
    });
  }
...
}

3. 动态创建表单

需求: 表单增加朋友选项, 默认显示一个, 可以增加/删除

<!-- app.component.html -->

<form [formGroup]="form">

  <div class="form-group">
    <label for="">name: </label>
    <input type="text" formControlName="name">
  </div>
    
  <div class="form-group">
    <label for="">password: </label>
    <input type="password" formControlName="password">
  </div>

  <ng-container *ngFor="let friend of friends.controls; let i=index">
    <div class="form-group">
      <label for="">friend(s): </label>
      <input type="text" [formControl]="friend">
      <span class="cursor-pointer hover-red" (click)="addFriend()" *ngIf="i===0">+</span>
      <span class="cursor-pointer hover-red" *ngIf="i!==0" (click)="removeFriend(i)">-</span>
    </div>
  </ng-container>

  <div class="form-group">
    <button type="submit" (click)="submit()" [disabled]="form.invalid">submit</button>
    <button type="button" (click)="reset()">reset</button>
  </div>

</form>
// app.component.ts

...

export class AppComponent implements OnInit {
  name = 'app';

  form: FormGroup;
  friends;

  constructor(
    private fb: FormBuilder
  ) {}

  ngOnInit() {
    this.form = this.fb.group({
      name: ['', Validators.required],
      password: ['', [Validators.required, ValidPwd]],
      friends: this.fb.array([this.createFriend()])
    });

    this.friends = this.form.get('friends') as FormArray;
  }

  /**
   * 动态创建表单
   * @returns {FormControl}
   */
  createFriend() {
    return this.fb.control('', Validators.required);
  }

  /**
   * 增加输入框
   */
  addFriend(): void {
    this.friends.push(this.createFriend());

  }

  /**
   * 移除输入框
   * @param {number} i
   */
  removeFriend(i: number): void {
    this.friends.removeAt(i);
  }

...

}

图片描述

4. standalone

需求: 增加单选框控制表单

在Reactive表单中, 使用ngModel时, 会出现报错

Error: 
      ngModel cannot be used to register form controls with a parent formGroup directive.

报错中也提示了, 应该在input中增加[ngModelOptions]="{standalone: true}"

文档中是这么介绍的:

standalone: Defaults to false. If this is set to true, the ngModel will not register itself with its parent form, and will act as if it's not in the form. This can be handy if you have form meta-controls, a.k.a. form elements nested in the <form> tag that control the display of the form, but don't contain form data.

现在表单变成这样:

<!-- app.component.html -->

<form [formGroup]="form">

  <div class="form-group">
    <label for="">name: </label>
    <input type="text" formControlName="name">
  </div>
  <div class="form-group">
    <label for="">password: </label>
    <input type="password" formControlName="password">
  </div>

  <ng-container
    *ngFor="let friend of friends.controls; let i=index">
    <div class="form-group">
      <label for="">friend(s): </label>
      <input type="text" [formControl]="friend">
      <span class="cursor-pointer hover-red" (click)="addFriend()" *ngIf="i===0">+</span>
      <span class="cursor-pointer hover-red" *ngIf="i!==0" (click)="removeFriend(i)">-</span>
    </div>
  </ng-container>

  <div class="form-group">
    <label for="">contactType: </label>
    mobile<input type="radio" value="0" [(ngModel)]="contactType" [ngModelOptions]="{standalone:true}">&emsp;
    landLine<input type="radio" value="1" [(ngModel)]="contactType" [ngModelOptions]="{standalone:true}">
  </div>

  <div class="form-group">
    <label for="">{{+contactType === 1?"landLine":"mobile"}}: </label>
    <input type="text" formControlName="contact">
  </div>

  <div class="form-group">
    <button type="submit" (click)="submit()" [disabled]="form.invalid">submit</button>
    <button type="button" (click)="reset()">reset</button>
  </div>

</form>

app.componen.ts中增加contactType变量, 表单实例中增加contact:

// app.component.ts

...

export class AppComponent implements OnInit {
  name = 'app';

  form: FormGroup;
  friends;
  contactType:number = 0;

  constructor(
    private fb: FormBuilder
  ) {}

  ngOnInit() {
    this.form = this.fb.group({
      name: ['', Validators.required],
      password: ['', [Validators.required, ValidPwd]],
      friends: this.fb.array([this.createFriend()]),
      contact: ['', Validators.required]
    });

    this.friends = this.form.get('friends') as FormArray;
  }

  /**
   * 动态创建表单
   * @returns {FormControl}
   */
  createFriend() {
    return this.fb.control('', Validators.required);
  }

  /**
   * 增加输入框
   */
  addFriend(): void {
    this.friends.push(this.createFriend());

  }

  /**
   * 移除输入框
   * @param {number} i
   */
  removeFriend(i: number): void {
    this.friends.removeAt(i);
  }

  submit() {
    if (this.form.valid) {
      console.log('submitting...');
    } else {
      console.log('form is not valid');
    }
  }

  reset() {
    this.form.reset();
  }
}

michael
25 声望1 粉丝