angular4 表单

1.模板式表单

在html模板上控制

指令对应的实例

 

1.ngForm指令:标记表单,会隐式创建FormGroup类的实例,这个类代表表单数据模型,并存储表单数据;

2.ngModel指令:标记单个字段,ngFrom指令会发现含有ngModel指令的标签,并添加到表单数据模型中,使用该指令必须跟随name属性;

3.ngModelGroup指令:标记多个字段组合,表现形式是一个对象,如{name:"张三",pass:"123"};

例子:

<!--模板变量myForm指向ngForm对象-->
<form #myForm = "ngForm" (ngSubmit) = "onSubmit(myForm.value)">
  <div>用户名:<input ngModel type="text" name="username"/></div>
  <!--ngModelGroup将字段嵌套成一个组-->
  <div ngModelGroup="passwordGroup">
     <div>密码:<input ngModel type="password" name="password"/></div>
     <div>确认密码:<input ngModel type="password" name="confirmPass"/></div>
  </div>
  <div>电话:<input ngModel type="text" name="mobile"/></div>
  <div><input type="submit" value="提交"/></div>
</form>

结果:

 

2.响应式表单

在ts上控制表单数据,可用于动态生成表单

类对应的指令

formGroup:标记组,表单也属于组,属于属性指令,需要加 [ ]

formGroupName:标记组,用于formGroup指令范围内部

formControl:标记字段,属于属性指令,需要加 [ ],用于在formGroup指令范围外部

formControlName:标记字段,用于formGroup指令范围内部

formArrayName:标记数组,用于formGroup指令范围内部

例子:

reactiveForm.html

<form [formGroup]="formModel" (submit)=onSubmit() novalidate>
  <div formGroupName="dateRange">
    起始日期:<input type="date" formControlName="from">
    截止日期:<input type="date" formControlName="to">
  </div>
  <div>
    <ul formArrayName="emails">
      <!-- 循环formArray数组,和下标 -->
      <li *ngFor="let e of this.formModel.get('emails').controls; let i=index;">
        <!-- formArray没有key,所有使用属性绑定[],值为数组的下标 -->
        <input type="text" [formControlName]="i">
      </li>
    </ul>
    <button type="button" (click)="addEmail()">增加Email</button>
  </div>
  <button type="submit">保存</button>
</form>
<input type="text" [formControl]="username">

reactiveForm.ts

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, FormArray } from '@angular/forms/src/model';

@Component({
  selector: 'app-reactive-form',
  templateUrl: './reactive-form.component.html',
  styleUrls: ['./reactive-form.component.css']
})
export class ReactiveFormComponent implements OnInit {

  //定义数据模型
  formModel : FormGroup = new FormGroup({
    dateRange : new FormGroup({
      from:new FormControl(),
      to:new FormControl()
    }),
    emails : new FormArray([
      new FormControl("a@aa.com"),
      new FormControl("b@bb.com"),
    ])
  });
  
 username:FormControl=new FormControl("张三"); 

  constructor() {}
  ngOnInit() {}

  onSubmit(){
    console.log(this.formModel.value);
  }
 
  addEmail(){
    let emails=this.formModel.get("emails") as FormArray;
    emails.push(new FormControl());
  }
}

 也可以使用FormBuilder类来创建数据模型,上面代码改成:

 formModel : FormGroup;

  constructor(fb:FormBuilder){  
    //定义数据模型
    this.formModel  = fb.group({
      dateRange : fb.group({
        from:[''],
        to:['']
      }),
      emails : fb.array([
        ["a@aa.com"],
        ["b@bb.com"],
      ])
  });

 

3.响应式表单校验

angular4提供了validator类的预定义校验

  • required - 设置表单控件值是非空的

  • email - 设置表单控件值的格式是 email

  • minlength - 设置表单控件值的最小长度

  • maxlength - 设置表单控件值的最大长度

  • pattern - 设置表单控件的值需匹配 pattern 对应的模式

当然,也可以进行自定义校验

例子:

reactiveForm.html

<form [formGroup]="formModel" (ngSubmit) = "onSubmit()" novalidate>
  <div>用户名:<input formControlName="username"/></div>
  
  <!-- 当验证通过或者未点击该字段时隐藏,状态字段untouched=true表示未点击,touched=true表示已点击 -->
  <div [hidden]="formModel.get('username').valid || formModel.get('username').untouched">
    <!-- 1.在模板上硬编码错误信息 -->
    <!-- [hidden]=false表示显示错误信息,hasError(校验失败返回对象的key,校验字段的name) -->
    <div [hidden]="!formModel.hasError('required','username')">
      用户名是必填项
    </div>
    <div [hidden]="!formModel.hasError('minLength','username')">
      用户名最小长度6
    </div>
</div>

  <div formGroupName="passwordsGroup">
     <div>密码:<input formControlName="password"/></div>
     <!-- 包含在group中的hasError第二个参数为数组[groupname,controlname] -->
     <div [hidden]="!formModel.hasError('minLength',['passwordsGroup','password'])">
        密码最小长度是6
     </div>
     <div>确认密码:<input formControlName="pconfirm"/></div>
     <div [hidden]="!formModel.hasError('equal','passwordsGroup')">
        {{formModel.getError('equal',passwordsGroup)?.passErrDesc}}
     </div>
  </div>
  

  <div>电话:<input formControlName="mobile"/></div>
  <!-- 当验证没有通过或者未改变该字段值时显示 状态字段pristine=ture表示未改变,dirty=ture表示已改变-->
  <div [hidden]="formModel.get('mobile').valid || formModel.get('username').pristine">
    <!-- 2.动态错误提示 -->
    <div [hidden]="!formModel.hasError('mobile','mobile')">
        {{formModel.getError('mobile')?.mobileErrDesc}}
    </div>
  </div>
  <div><input type="submit" value="提交"/></div>
</form>

reactiveForm.ts

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

@Component({
  selector: 'app-reactive-form2',
  templateUrl: './reactive-form2.component.html',
  styleUrls: ['./reactive-form2.component.css']
})
export class ReactiveForm2Component implements OnInit {

  formModel:FormGroup;

  /**
   * 自定义手机号校验
   * 返回值:正确返回 null
   *         错误返回 key:value(value可以是对象错误key:错误信息,也可以是任意值)
   * @param control 
   */
  mobileValidator(control:FormControl):any{
    var myreg = /^((0\d{2,3}-\d{7,8})|(1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}))$/; 
    let valid = myreg.test(control.value);
    console.log("mobile的校验结果是"+valid);
    return valid ? null : {mobile:{mobileErrDesc:"密码和确认密码不匹配"}};
  }

  //自定义密码组校验
  equalValidatir(group:FormGroup):any{
    let password:FormControl= group.get("password") as FormControl;
    let pconfirm:FormControl= group.get("pconfirm") as FormControl;
    let valid:boolean = (password.value === pconfirm.value);
    console.log("密码校验结果:"+valid);
    return valid ? null :{equal:{passErrDesc:"密码和确认密码不匹配"}};
  }

  constructor(fb:FormBuilder) {
    //定义数据模型
    this.formModel = fb.group({
      //预定义校验
      username:['',[Validators.required,Validators.minLength(6)]],
      mobile:['',this.mobileValidator],
      passwordsGroup:fb.group({
        password:[''],
        pconfirm:['']
      },{validator:this.equalValidatir})
    });
  }

  ngOnInit() {}

  onSubmit(){
    //整个表单校验通过
    if(this.formModel.valid){
      console.log(this.formModel.value);
    }
  }
}

当然,有些校验方法时通用的,所以我们可以提取出全局的校验方法。

需要function声明该方法,再通过 export 关键字暴露该方法。

validator.ts

import { FormControl, FormGroup } from "@angular/forms";

//自定义手机号码组校验
export function mobileValidator(control:FormControl):any{
    var myreg = /^((0\d{2,3}-\d{7,8})|(1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}))$/; 
    let valid = myreg.test(control.value);
    console.log("mobile的校验结果是"+valid);
    return valid ? null : {mobile:{mobileErrDesc:"密码和确认密码不匹配"}};
}

//自定义密码组校验
export function equalValidatir(group:FormGroup):any{
    let password:FormControl= group.get("password") as FormControl;
    let pconfirm:FormControl= group.get("pconfirm") as FormControl;
    let valid:boolean = (password.value === pconfirm.value);
    console.log("密码校验结果:"+valid);
    return valid ? null :{equal:{passErrDesc:"密码和确认密码不匹配"}};
  }

异步校验器

用来调用远程的服务来校验表单的值。异步校验器跟普通的校验器一样,只不过返回的是一个可观测的流

例子:

validator.ts

import { FormControl, FormGroup } from "@angular/forms";

//自定义手机号码组校验
export function mobileAsyncValidator(control:FormControl):any{
    var myreg = /^((0\d{2,3}-\d{7,8})|(1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}))$/; 
    let valid = myreg.test(control.value);
    console.log("mobile的校验结果是"+valid);
    //Observable.of返回一个流,.delay(5000)表示延迟5秒,用来模拟服务器响应时间
    return Observable.of(valid ? null : {mobile:{mobileErrDesc:"密码和确认密码不匹配"}}).delay(5000);
}

reactiveForm.ts

constructor(fb:FormBuilder) {
    //定义数据模型
    this.formModel = fb.group({
      //预定义校验
      username:['',[Validators.required,Validators.minLength(6)]],
      //异步校验作为第3个构造函数
      mobile:['',this.mobileValidator,this.mobileAsyncValidator],
      passwordsGroup:fb.group({
        password:[''],
        pconfirm:['']
      },{validator:this.equalValidatir})
    });
  }
<!--状态字段pending=true表示正在进行校验-->
<div [hidden]="!formModel.get('mobile').pending">
   正在校验手机合法性
</div>

 

4.模板式表单校验 

终端输入命令$ ng g directive directive/mobileValidator  创建指令文件

将校验方法定义成指令,如下

mobileValidator.directive.ts

import { Directive } from '@angular/core';
import { NG_VALIDATORS } from '@angular/forms';
import { mobileValidator } from '../validator/validator';

//指令装饰器
@Directive({
  selector: '[mobile]',//指令名称
  //useValue:校验方法名称 ,multi:true:表示token为NG_VALIDATORS的提供器可对应多个useValue
  providers:[{provide:NG_VALIDATORS,useValue:mobileValidator,multi:true}]
})
export class MobileValidatorDirective {
  constructor() { }
}

在模板中使用指令来校验模板表单

templateForm.component.html

<!-- novalidate指令:不启用浏览器默认的表单校验,让angualr的校验指令不受干扰 -->
<form #myForm = "ngForm" (ngSubmit) = "onSubmit(myForm.value,myForm.valid)" novalidate>
  <!-- 使用angular预定义的指令required,minlength等来校验表单 -->
  <div>用户名:<input ngModel required minlength="6" name="username" type="text"/></div>
  
  <div ngModelGroup="passwordGroup" equal>
     <div>密码:<input ngModel type="password" name="password"/></div>
     <div>确认密码:<input ngModel type="password" name="confirmPass"/></div>
  </div>

  <!-- 定义input事件来控制是否显示错误提示 -->
  <div>电话:<input ngModel mobile name="mobile" type="text" (input)="onMobileInput(myForm)"/></div>
  <!-- 接收模板的值判断是否显示错误提示 -->
  <div [hidden]="mobileValid || mobileUntouched">
    <!-- 使用myForm.form获取表单对象 -->
    <div [hidden]="!myForm.form.hasError('mobile','mobile')">
      电话号码不正确
    </div>
    <div>
  <div><input type="submit" value="提交"/></div>
</form>

templateForm.component.ts

import { Component, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms/src/directives/ng_form';

@Component({
  selector: 'app-template-form',
  templateUrl: './template-form.component.html',
  styleUrls: ['./template-form.component.css']
})
export class TemplateFormComponent implements OnInit {
  constructor() { }
  ngOnInit() {}

  mobileValid:boolean=true;
  mobileUntouched:boolean=true;

  //点击电话输入框时,判断mobile是否合法
  onMobileInput(form:NgForm){
    if(form){
      this.mobileValid = form.form.get("mobile").valid;
      this.mobileUntouched = form.form.get("mobile").untouched;
    }
  }

  onSubmit(value:any,valid:boolean){
    console.log(valid);
    console.log(value);
  }
}

 

posted @ 2018-03-27 23:48  龙哥迷恋上  阅读(782)  评论(0编辑  收藏  举报