angular学习4-表单

9. angular表单

9.1.1 表单概述

模板式表单

表单的数据模型是通过组件模板中的相关指令来定义的,因为使用这种方式定义表单的数据模型时,我们会受限与HTML的语法,所以模板驱动方式只适用于一些简单的场景

 

响应式表单

使用响应式表单时,你通过编写TypeScript代码而不是Html代码来创建一个底层的数据模型,在这个模型定义好以后,你使用一些特定的指令,将模板上的html元素与底层的数据模型连接起来

 

不管是那种表单,都有一个对应的数据模型来储存表单的数据。在模板式表单中,数据模型是由angular基于你组件模板中的指令隐式创建的。而在响应式表单中,你是通过编码明确的创建数据模型然后将模板上的html元素与底层的数据模型连接起来。

数据模型并不是一个任意的对象,他是一个由angular/forms模块中的而一些特定的类,如FormControl,FormGroup,FormArray等组成的。在模板式表单中,你是不能直接访问到这些类的。

 

9.1.2 html表单

<!-- 这是一个简单的html表单 -->
 <form action="/register">
   <!-- input属性
     1. readonly: 只读
     2. required: 必填。提交表单的时候会进行校验
     3. pattern: 规定输入字段的模式、格式。值为正则表达式
     4. min/max: 规定输入字段的最小值/最大值
     5. maxlength: 规定输入字段的最大长度
     5. formnovalidate:如果有该属性,则表单提交的时候不进行校验
     注意:html自带表单校验的提示语是无法修改的
   -->
   <div><label for="">姓名:</label> <input type="text" readonly /></div>
   <div><label for="">手机号:</label> <input type="text" formnovalidate pattern="[Asd]{1,4}" /></div>
   <div><label for="">密码:</label> <input type="password" /></div>
   <div><label for="">确认密码:</label> <input type="password" /></div>
   <button type="submit">注册</button>
 </form>

缺点:

  • 不能实现数据的双向绑定

  • 数据校验的方式以及校验提示太过简单

  • 不能控制数据提交的格式

 

9.1.3 模板式表单

模板表单的几个重要属性

ngForm

  • angular项目中的每一个form标签都会被自动挂载这个属性

  • 这个属性会拦截form表单的默认提交行为,并且隐式的创建一个formGroup类。同时会自动找到表单中标有ngModel属性的表单元素,并将其(name属性)添加到formGroup类中,记录表单元素的值。

<form #myForm="ngForm" action="/register">
 <div><label for="">姓名:</label> <input type="text" ngModel name="username" /></div>
 <div><label for="">手机号:</label> <input type="text" /></div>
 <div><label for="">密码:</label> <input type="password" /></div>
 <div><label for="">确认密码:</label> <input type="password" /></div>
 <button type="submit">注册</button>
</form>

<div>
{{ myForm.value | json }}
</div>

上面的代码中只有姓名字段带有ngModel标记,所以最终myForm对象中只有userName一个属性(ngModel的具体使用请看下面)

#myForm是我自己定义的模板变量,用来代表ngForm

如果form表单使用ngNoForm,项目中的这个表单就不会被angular接管,保持表单的原有特性。

当显示的声明ngForm属性的时候,form标签可以换成其他标签。

angular学习4-表单

ngModel

ngModel会隐式的创建一个formControl,并存储表单元素的值。

作用一:实现数据的双向绑定

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

作用二: 将表单元素添加到表单数据模型的一部分,此时表单元素必须要有name属性--通过myForm获取表单元素的时候对应的键。

<!--form表单同时有双向绑定数据-->
<div><label for="">姓名:</label> <input type="text" [(ngModel)]="userForm.userName" name="username" /></div>

<!--form表单同时没有双向绑定数据-->
<div><label for="">姓名:</label> <input type="text" ngModel name="username" /></div>

通过#userName="ngModel"定义模板变量替代ngModel。这样就可以在模板中通过userName.value访问姓名输入框的值了。

<div><label for="">姓名:</label> <input type="text" #userName="ngModel" ngModel name="username" /></div>

 

ngModelGroup

功能有点类似于ngForm。会创建一个formGroup对象。表现在ngForm对象中创建一个子对象

<form #myForm="ngForm" action="/register">
 <div><label for="">姓名:</label> <input type="text" [(ngModel)]="userForm.userName" name="username" /></div>
 <!---通过 ngModelGroup="phoneInfo" 创建一个名为phoneInfo的modelGroup-->
 <div #myPhone="ngModelGroup" ngModelGroup="phoneInfo">
   <label for="">手机号:</label> <input type="text" ngModel name="phone" />
 </div>
</form>

<div>
{{ myForm.value | json }} <br>
{{ myPhone.value | json }}
</div>

angular学习4-表单

 

每个表单元素的#模板变量名="ngModel"是互不影响的

模板式表单是不可以直接操作FormControl这些类的,而是通过ngForm这些属性来操控的。

 

9.1.4 响应式表单

通过以下三个核心类创建表单数据模型;然后通过指令将数据模型和模板中的表单元素对应起来。

angular学习4-表单

angular学习4-表单

 

  1. 在组件中导入表单类,创建数据模型

    import { Component, OnInit } from '@angular/core';
    import { FormControl, FormGroup, FormArray } from '@angular/forms'
    ...
    formModel: FormGroup = new FormGroup({ // FormGroup代表整个表单数据模板
     startTime1: new FormControl(''),
     endTime1: new FormControl(),
       
     timeRang: new FormGroup({ // FormGroup代表表单的一部分数据模板
       startTime: new FormControl(''),
       endTime: new FormControl()
    }),
    })

    // 添加邮件
    addEmail() {
     // 通过这种方式添加电子邮件个数,a
     this.formModel.get('emails').push(new FormControl('aaa@xxx.com.cn'))
    }

    页面里面连接表单元素

    <form action="/register" [formGroup]="formModel">
    <div><label for="">初始时间: </label><input type="date" formControlName="startTime1" /></div>
    <div><label for="">结束时间: </label><input type="date" formControlName="endTime1"/></div>
    <div formGroupName="timeRang"> <!--用对应的FormGroup的名称将这部分内容包裹-->
     <div><label for="">初始时间: </label><input type="date" formControlName="startTime" /></div>
     <div><label for="">结束时间: </label><input type="date" formControlName="endTime"/></div>
    </div>

    <ul formArrayName="emails"><!--带有formArray属性的元素要包裹对应动态集合中所有formControl对应的元素-->
     <li *ngFor="let email of formModel.get('emails').controls;let i=index">
       <label for="">邮件{{i+1}}: </label><input type="text" [formControlName]="i" /> </li>
    </ul>

    <button type="submit" (click)="addEmail()">add email</button>
    <button type="submit" (click)="onSubmit()">提 交</button>
    </form>

     

    FormGroup: 多个FormControl的集合。可以是整个表单,也可以是表单里面的一个集合。分别对应上面代码中的formModeltimeRang

    FormArray: 可以动态增加或者减少的多个FormControl集合。

    通过formModel.value.emails可以获取邮件列表的值。通过formModel.get('emails')方法可以获取邮件的FormArray对象。

    FormControl: 表单数据模型的基本结构。用于存放表单元素的值、校验信息以及是否被修改过。

  2. 通过指令将html元素连接到数据模型上

    formGroup: 绑定对应的集合名称(属性绑定的形式).指定当前表单与哪个数据模型绑定

    formGroupName: 绑定对应的集合名称(非属性绑定的形式)。

    formArrayName:绑定对应的动态可增减集合名称。(非属性绑定的形式)

    formControlName: 将对应的formControl与单个表单元素对应起来(非属性绑定的形式)。执行表单元素绑定的control字段

    formControl: 将对应的formControl与单个表单元素对应起来。只能绑定单个零散的FormControl,而不能绑定某个form集合中的FormControl(属性绑定的形式)

     

     

    • 构造方法传参:FormGroup传参为对象;FormArray: 传参是数据项为FormControl类型对象的数组;FormControl传参是字符串或不传参。

    • js修改和查看表单各元素值的方法:查看值:this.formModel.value;获取formGroup集合中的某个对象:this.formModel.get();更改值:具体请查看接angular官方文档对应上面三个类的属性描述。

    • 响应式表单不可以通过模板变量访问值,只能通过模板数据及其方法访问;模板式表单可以通过模板变量访问和操作,但是不能直接通过formGroup这些接口操作。

     

     

    FormBuilder:

    FormBuilder 提供了一个语法糖,以简化 FormControlFormGroupFormArray 实例的创建过程。 它会减少构建复杂表单时所需的样板代码的数量。

    使用FormBuilder简化上面的数据模板的创建过程:

    ...
    formModel:any

     // 声明一个fb变量,为FormBuilder类型
     constructor(public fb: FormBuilder) {
       // 构建一个新的 FormGroup 实例
       this.formModel = fb.group({
         timeRang: fb.group({
           startTime: fb.control(''),
           endTime: fb.control('')
        }),
         emails: fb.array([
           fb.control('aaa@xx.com.cn')
        ])
      })
    }
     // 添加邮件
     addEmail() {
       this.formModel.get('emails').push(this.fb.control('aaa@xx.com.cn'))
    }
    ...

    FormBuilder的control、group、array方法除了可以分别创建出对应的 FormControlFormGroupFormArray实例外,还可以接收其它参数提供更多功能。具体请看官网。

 

 

9.1.5 表单校验

angular的表单验证包括内置表单验证)和自定义表单验证。

 

  1. 每当表单控件中的值发生变化时,Angular 就会进行验证。只会校验被修改的控件。

  2. angular的内置校验器都在Validators类中,通过Validators.checkName的形式访问。

    1. 与表单校验有关的关键属性:validinValiderrorsstatus

  3. angular的内置验证其可以适合一些简单的验证,复杂的表单验证建议使用自定义表单验证-还可以自定义提示信息。

  4. 自定义验证器的传参类型可以是:AbstructControlFormControlFormArray三种类型。某个控件或者子集合的校验不通过,那他的包裹集合的验证(valid)属性也是不通过的

 

9.1.5.1 响应式表单验证

<h2 style="color: brown">响应式表单校验</h2>
<form [formGroup]="formModel">
<div><label for="">姓名:</label><input type="text" required formControlName="userName" /></div>
<div><label for="">电话号码:</label><input type="text" formControlName="phone" /></div>

<div formGroupName="passwordGroup">
  <div><label for="">密码:</label><input type="password" formControlName="password"/></div>
  <div><label for="">确认密码:</label><input type="password" formControlName="confirmPass" /></div>
</div>

<button (click)="onSubmit()">提 交</button>
</form>
...
public formModel

 // 验证单个表单空间的自定义验证器
 // 参数类型
 checkPhone(control: AbstractControl): any {
   const phoneReg = /^1[3589]{1}\d{9}/
   const valid = phoneReg.test(control.value)
   console.log('phone', control)
   return valid ? null : { tip: '电话号码格式有误' }
}

 checkoutPassGroup(group: FormGroup): any {
   console.log('确认密码校验', group)
   // 直接获取值
   // var valid = group.value.password === group.value.confirmPass ? null : { tip: '两次密码输入不一致'}
   // 获取group中的control对象
   var valid = group.get('password').value === group.get('confirmPass').value ? null : { tip: '两次密码输入不一致'}
   return valid
}

 constructor(public fb:FormBuilder) {
   this.formModel = fb.group({
     // 内置验证器,作为control的第二个参数,多个验证其的话第二个参数就是个数组
     // 疑问:什么时候是函数,什么时候是属性;还有官网上这些方法传参类型怎么看不懂
     userName: ['',[Validators.required, Validators.min(2)]],
     // 自定义验证器
     phone: ['', this.checkPhone],
     passwordGroup: fb.group({
       password: [''],
       confirmPass: [''],
    }, {validator: this.checkoutPassGroup})
  })
}
...
  1. 上面姓名的校验是通过angular的内置校验器进行校验的。

    angular学习4-表单

 

  1. 通常类的实例化函数的第二个参数是要传入的构造器函数。如果内置校验器传参是类的实例化本身,可以写成简写形式,如上面电话号码的验证方法this.checkPhone确认密码的方法{validator: this.checkoutPassGroup} 对于自定义校验器,如果想传入除去类实例本身以外的参数要怎么传

  2. 一个类同时使用多个验证器的时候,用数组。如上面的姓名验证。

  3. 在响应式表单中,事实之源是其组件类。不应该通过模板上的属性来添加验证器,而应该在组件类中直接把验证器函数添加到表单控件模型上(FormControl)。然后,一旦控件发生了变化,Angular 就会调用这些函数

 

9.1.5.1.1 获取校验结果

通过状态字段

  • touched: 该表单元素是否已经获取过焦点。对于集合,只要其中一个元素获取过焦点,整个集合都是touched状态

  • untouched: 与touched相对的一个字段

  • pristine: 表单元素没有被修改过值的时候为true. 对于集合,只要其中一个元素获被修改过值,整个集合都是dirty状态。

  • dirty: 表单元素被修改过值。

  • pending: 字段处于校验过程中

  • hasError(): 可以判断某个表单元素的某个校验器有没有通过。返回值为布尔类型。

    // 第一个参数是校验器名称(对于自定义校验器也适合),第二个参数是字段名称。
    formModel.hasError('required', 'userName')
  • 自己获取字段的value,进行手动校验

    • conrol.setValue('value') : 设置表单元素的值

    • formModel.get('controlName'): 获取某个表单元素或者某个子集合

    •  

上一篇:重新学习angularjs--第一篇(入门)


下一篇:pytorh dataloader 迭代类型数据链式处理分析