angular4学习笔记(第七章 表单篇)

1.模板式表单

  • 模板式表单使用ngForm、ngModel、ngModelGroup来完成,它们是FormsModule里的内容,所以在使用模板式表单时需要在app.module.ts中引入FormsModule。
  • ngForm会自动拦截标准的表单处理事件,angular用ngSubmit代替标准的表单提交ngForm隐式的创建一个FormGroup类的实例,代表表单的数据模型,用来存储表单数据,标有这个指令的表单会自动发现标有ngModel指令的表单子元素,并将它们的值添加到数据模型中。
  • ngForm相当于会创建一个实例对象,用于存储需要的表单值
  • ngModel相当于一个标识,表明该元素的值为该表单的一个属性,需要为该元素提供‘name’属性,否则会报错
  • ngModelGroup相当于在表单对象里再创建一个对象,存储表单中一些组合在一起使用的值
  • 使用ngSubmit事件绑定提交事件(用submit也行,目前我还不知道区别在哪,知道再来补充,知道的也可以留言告知一下我,谢谢^-^
  • 因为模板式表单的所有操作都是在界面完成,所以提交时需要传入提交的值(模板本地变量名.value)
  • ngForm、ngModel、ngModelGroup开头字母都要使用小写,否则会报错
  • 模板式表单的使用案列

2.响应式表单

  • 响应式表单指令è¿éåå¾çæè¿°
  • formGroup类用于创建响应式表单,是多个formControl的集合,用于存储表单值
  • formControl类用于声明表单字段
  • formArray类用于声明一个数组字段
  • 响应式表单的使用案列
    • 先在ts文件中创建formGRoup,以及设置表单字段等信息
    • 引入formBuilder可更方便快捷的创建响应式表单
    • FormArray与FormGroup类似,但是它有一个额外的长度属性,FormGroup代表表单的固定子集,而FormArray代表一个可以增长的集合
    • FormArray中的FormControl是没有相关的key,只能通过下标来访问
    • formBulider可以接受一个参数,eg. 第一个参数是formControl的初始值,第二个是校验方法,第三个是异步校验方法
    • formArray增加formControl
    • formArray删除formControl

3.响应式表单验证

  • 内置表单验证
    • 常用required,minlength,maxlength
      • 单个验证用法(数组的第一个值为formControl的默认值,第二个值为formControl的同步校验方法,第三个值为formControl的异步校验方法)
              validRequired: ['', Validators.required],   //必填项
              validMinlength: ['', Validators.minLength(3)],  //长度最少为3
              validMaxlength: ['', Validators.maxLength(6)],  //长度最大为6
      • 多个验证用法(把多个同步验证的方法放到一个数组内,只要有一个不符合,整个formControl就是无效错误的)

        validMix: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(6)]]
  • 自定义验证方法
    • 单个formControl的验证方法
    • formGroup的验证方法

    • 异步验证方法(用于向服务器请求,返回的是一个Observable观察者对象)

      import { FormControl, FormGroup } from '@angular/forms';
      import { Observable } from 'rxjs';
      import 'rxjs/add/observable/of';
      
      
      // 单个formControl的验证方法
      export function mobileValidator(control: FormControl): any {
          const config = /^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1}))+\d{8})$/;
          let res = config.test(control.value);
          //mobile作为一个key作为错误信息的一个关键词来使用,true可以为任意其他值,没有任何影响
          // return res ? null : { mobile: 66 }   true改为66,hdjsah或者其他任意类型的值也是可以的,没有什么影响
          return res ? null : { mobile: true }  
      }
      
      // formGroup的验证方法
      export function equalValidator(group: FormGroup): any {
          const validCombination1 = group.get('validCombination1') as FormControl;
          const validCombination2 = group.get('validCombination2') as FormControl;
          let res = (validCombination1.value === validCombination2.value);
          return res ? null : { equal: true }
      }
      
      // 异步验证方法
      export function mobileAsyncValidator(control: FormControl): Observable<any> {
          const config = /^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1})) + \d{8})$/;
          let res = config.test(control.value);
          return Observable.of(res ? null : { mobileAsync: true });
      }
  • 响应式表单验证的使用案列
    • 声明响应式表单
      formModel: FormGroup;
      
        constructor(formBuilder: FormBuilder) {
          this.formModel = formBuilder.group({
            validRequired: ['', Validators.required],   //必填项
            validMinlength: ['', Validators.minLength(3)],  //长度最少为3
            validMaxlength: ['', Validators.maxLength(6)],  //长度最大为6
            validMix: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(6)]],  //多个验证方法
            validCustom: ['', [Validators.required, mobileValidator]],  //内置验证方法 + 自定义验证方法
            validCustomAsync: ['', Validators.required,mobileAsyncValidator],  //内置验证方法 + 自定义异步验证方法 
            validCombination: formBuilder.group({
              validCombination1: [''],
              validCombination2: ['']
            }, { validator: equalValidator })   //formGroup验证的使用,验证方法为对象,validator : 方法名
          })
        }
      
    • 错误信息hasError
      <!-- 内置验证方法,使用关键字required,minlength,maxlength(小写) -->
      [hidden] = "!formModel.hasError('required', 'username')"
      <!-- 自定义验证方法,使用定义时返回的关键字mobile -->
      [hidden] = "!formModel.hasError('mobile', 'mobile')"
    • 状态字段(touched、untouched、pristine、dirty、pending)

      touched、untouched 判断是否获取过焦点
      pristine(从未被改变过,值为true,反之....)、dirty(被修改过,值为true) 判断字段的值有没有被改变过
      pending 正处于异步校验时这个属性为true
    • 界面提示错误信息案列

      <form [formGroup]="formModel" (ngSubmit)="onSubmit()">
        <div>
          required验证 :<input formControlName="validRequired" type="text">
          <span class="error" 
                [hidden]="!formModel.hasError('required', 'validRequired')
                          ||formModel.get('validRequired').untouched">
            该项为必填项!
          </span>
        </div>
        <div>
          minlength验证 :<input formControlName="validMinlength" type="text">
          <span class="error" 
                [hidden]="!formModel.hasError('minlength', 'validMinlength')||
                          formModel.get('validMinlength').untouched">
            长度至少为3位!
          </span>
        </div>
        <div>
          maxlength验证 :<input formControlName="validMaxlength" type="text">
          <span class="error" 
                [hidden]="!formModel.hasError('maxlength', 'validMaxlength')||
                          formModel.get('validMaxlength').untouched">
            长度最多为6位!
          </span>
        </div>
        <div>
          混合验证 : <input formControlName="validMix" type="text">
          <span [hidden]="formModel.get('validMix').untouched">
            <span class="error" 
                  [hidden]="!formModel.hasError('required', 'validMix')">
              该项为必填项!
            </span>
            <span class="error" 
                  [hidden]="!formModel.hasError('minlength', 'validMix')">
              长度至少为3位!
            </span>
            <span class="error" 
                  [hidden]="!formModel.hasError('maxlength', 'validMix')">
              长度最多为6位!
            </span>
          </span>
        </div>
        <div>
          自定义验证 : <input formControlName="validCustom" type="text">
          <span class="error" 
                [hidden]="!formModel.hasError('required', 'validCustom')||
                          formModel.get('validCustom').untouched">
            该项为必填项!
          </span>
          <span class="error" 
                [hidden]="formModel.hasError('required', 'validCustom')||
                          !formModel.hasError('mobile1', 'validCustom')||
                          formModel.get('validCustom').pristine">
            请填写正确的手机号码!
          </span>
        </div>
        <div>
          自定义异步验证 : <input formControlName="validCustomAsync" type="text">
          <span class="error" 
                [hidden]="!formModel.hasError('required', 'validCustomAsync')
                          ||formModel.get('validCustomAsync').untouched">
            该项为必填项!
          </span>
          <span class="error" 
                [hidden]="formModel.hasError('required', 'validCustomAsync')||
                          !formModel.hasError('mobileAsync', 'validCustomAsync')||
                          formModel.get('validCustomAsync').pristine">
            请填写正确的手机号码!
          </span>
        </div>
        <div formGroupName="validCombination">
          <div>
            组合验证1 :<input formControlName="validCombination1" type="password">
          </div>
          <div>
            组合验证2 :<input formControlName="validCombination2" type="password">
            <span class="error" 
                  [hidden]="!formModel.hasError('equal','validCombination')||
                            formModel.get(['validCombination','validCombination1']).pristine||
                            formModel.get(['validCombination','validCombination2']).pristine">
              两次输入值不相同!
            </span>
          </div>
        </div>
        <div>
          <button type="submit">提交</button>
        </div>
      </form>
      

4.模板式表单验证

  • 模板式表单因为只能在html界面操作,所以需要创建指令来进行表单验证(使用‘ ng g d 指令名 ’ 创建指令)
  • 编辑指令(以mobile验证方法为例子)
    import { Directive } from '@angular/core';
    import { NG_VALIDATORS } from '@angular/forms';
    import { mobileValidator } from './my-validators/my-validator';
    
    @Directive({
      selector: '[mobile]',  //属性名
      providers: [{
        provide: NG_VALIDATORS,  //验证指令必须需要的服务
        useValue: mobileValidator,  //验证方法
        multi: true
        // 是否允许同一个元素拥有多个属性,true为允许,false为不允许。如 : ngfor与ngif指令,该指令的multi为false,
        // 所以在一个html元素中,不允许同时出现ngif和ngfor,如<div *ngIf="a" *ngFor="let a in b"></div>是会报错的
      }]
    })
    export class MobileValidatorDirective {
    
      constructor() { }
    
    }
    
  • 在html中作为属性来使用(在form标签中使用novalidate来阻止默认的验证方法,防止出现冲突错误)
    <form #myForm="ngForm" (submit)="onSubmit(myForm.value)" autocomplete="off" novalidate>
      <div>
        用户名:<input required minlength="4" ngModel name="username" type="text" 
                      (input)="onUserNameInput(myForm)">
        <span [hidden]="userNameValid || userNametouched">
          <span class="error" 
                [hidden]="!myForm.form.hasError('required', 'username')">
            该项为必填项!
          </span>
          <span class="error" 
                [hidden]="!myForm.form.hasError('minlength', 'username')">
            用户名长度至少4位!
          </span>
        </span>
    
      </div>
    
      <div>
        <!-- mobile属性就是自定义的验证指令 -->
        手机号:<input ngModel required mobile name="mobile" type="tel">
        <span class="error" 
              [hidden]="!myForm.form.hasError('required', 'mobile')">
          该项为必填项!
        </span>
        <span class="error" 
              [hidden]="myForm.form.hasError('required', 'mobile')||
                        !myForm.form.hasError('mobile', 'mobile')">
          请填写正确手机号!
        </span>
      </div>
    
      <div ngModelGroup='passwordGroup' equal>
        <div>
          密码:<input ngModel required name="password" type="password">
        </div>
        <div>
          确认密码:<input ngModel required name="pconfirm" type="password">
        </div>
        <span class="error" 
              [hidden]="!myForm.form.hasError('equal', 'passwordGroup')">
          密码不一致!
        </span>
      </div>
      <button type="submit">提交</button>
    </form>
    
  • 错误信息(与响应式差别在于响应式使用的是formModel.hasError,模板式使用的是myForm.form.hasError)
    <span class="error" [hidden]="!myForm.form.hasError('mobile', 'mobile')">
        请填写正确的手机号!
    </span>
  • 状态字段(模板式表单只能设置相对应的方法来设置状态,所以不适合做复杂场景的表单)
      <div>
        用户名:<input required minlength="4" ngModel name="username" type="text"
     (input)="onUserNameInput(myForm)">
        <span [hidden]="userNameValid || userNametouched">
          <span class="error" [hidden]="!myForm.form.hasError('required', 'username')">
            该项为必填项!
          </span>
          <span class="error" [hidden]="!myForm.form.hasError('minlength', 'username')">
            用户名长度至少4位!
          </span>
        </span>
    
      </div>
      userNameValid: boolean = true;
      userNameUntouched: boolean = true;
      onUserNameInput(form: NgForm) {
        if (form) {
          this.userNameValid = form.form.get('username').valid;
          this.userNameUntouched = form.form.get('username').untouched;
        }
      }
  • formGroup的验证方法与响应式的验证方法有些不同
    
    // 响应式formGroup的验证方法
    export function equalValidator(group: FormGroup): any {
        const validCombination1 = group.get('validCombination1') as FormControl;
        const validCombination2 = group.get('validCombination2') as FormControl;
        let res = (validCombination1.value === validCombination2.value);
        return res ? null : { equal: true }
    }
    
    // 模板式formGroup的验证方法
    export function equalTempValidator(group: FormGroup): any {
        const password = group.value.password as FormControl;
        const pconfirm = group.value.pconfirm as FormControl;
        let res = (password === pconfirm);
        return res ? null : { equal: true }
    }

5.更新在线竞拍搜索表单

  • 创建响应式表单
    import { Component, OnInit } from '@angular/core';
    import { ProductService } from 'src/shared/product.service';
    import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
    
    @Component({
      selector: 'app-search',
      templateUrl: './search.component.html',
      styleUrls: ['./search.component.css']
    })
    export class SearchComponent implements OnInit {
    
      private types: string[];
    
      formModel: FormGroup;
    
      constructor(private _ProductService: ProductService, FormBuilder: FormBuilder) {
        this.formModel = FormBuilder.group({
          productName: ['', Validators.minLength(2)],
          productPrice: [null, priceNumberValidator],
          productType: [-1]
        })
      }
    
      ngOnInit() {
        this.types = this._ProductService.getAllType();
      }
    
      onSubmit() {
        console.log(this.formModel.value);
      }
    
    }
    
    
  • 创建自定义校验方法(本次放在search组件的ts中,也可以定义一个文件夹存放自定义验证方法)
    export function priceNumberValidator(control: FormControl): any {
      let res = (control.value > 0);
      return res ? null : { number: true };
    }
  • 修改html代码,添加错误提示信息
    <form [formGroup]="formModel" (ngSubmit)="onSubmit()">
    
      <div class="form-group">
        <label for="">商品名称</label>
        <input formControlName="productName" type="text" class="form-control" placeholder="商品名称">
        <span [hidden]="formModel.get('productName').untouched">
          <span class="text-danger" style="font-size:smaller" 
                [hidden]="!formModel.hasError('minlength','productName')">
            *商品名称长度至少为2位!
          </span>
        </span>
      </div>
    
      <div class="form-group">
        <label for="">商品价格</label>
        <input formControlName="productPrice" type="text" class="form-control" placeholder="商品价格">
        <span [hidden]="formModel.get('productPrice').untouched">
          <span class="text-danger" style="font-size:smaller" 
                [hidden]="!formModel.hasError('number','productPrice')">
            *商品价格为正数!
          </span>
        </span>
      </div>
    
      <div class="form-group">
        <label for="">商品类别</label>
        <select formControlName="productType" class="form-control">
          <option value="-1" selected>全部</option>
          <option *ngFor="let type of types" value="{{type}}">{{type}}</option>
        </select>
      </div>
    
      <button type="submit" class="btn btn-primary btn-block">搜索</button>
    
    </form>
    

6.结果展示(对界面的数据做了某些修改)

猜你喜欢

转载自blog.csdn.net/cxzlp12345/article/details/87435953