Angular学习笔记五之表单

一、模版驱动

表单的控制逻辑写在组件模版中,比较适合简单的表单类型。

(一)创建一个简单的表单

  1. 需要在组件类中引入FormsModule,并且定义一个提交的方法
import {
    
    Component} from '@angular/core';
import {
    
    FormsModule} from "@angular/forms";

@Component({
    
    
    selector: 'app-layout',
    templateUrl: './layout.component.html',
})

export class LayoutComponent {
    
    
    onSubmit(value: any) {
    
    
        console.log(value);
    }
}
  1. 在组件模版中,通过#myForm="ngForm"将这个表单定义为Angular表单,这样就可以使用Angular表单中提供的特性。对这个表单绑定提交的事件。对于表单控件,需要设置name属性作为唯一索引,设置ngModel属性实现与表单对象的双向数据绑定。
<form #myForm="ngForm" (submit)="onSubmit(myForm)">
    <input type="text" name="username" ngModel />
    <button type="submit">Submit</button>
</form>
  1. 在网页中,输入一段文字,点击提交按钮,查看提交事件接收到的value是一个ngForm对象,里面的form.value就是控件双向绑定的数据源。表单控件的name属性就作为数据源的键,value作为值。只有设置了ngModel的控件,输入值才会被记录到form.value
    在这里插入图片描述

(二)表单分组

当需要的表单项比较多时,可以分组对表单项进行管理。

  1. 组件模版中,通过ngModelFroup进行分组,分组的名字会作为直属属性名存储到表单对象中。
    这里将用于分组的元素换为ng-container,就不会渲染成真实的元素
<form #myForm="ngForm" (submit)="onSubmit(myForm)">
    <h3>用户信息</h3>
    <div ngModelGroup="user">
        名字:<input type="text" name="username" ngModel />
        年龄:<input type="number" name="age" ngModel />
    </div>
    <h3>车辆注册信息</h3>
    <div ngModelGroup="car">
        品牌:<input type="text" name="brand" ngModel />
        颜色:<input type="color" name="color" ngModel />
    </div>
    <button type="submit">Submit</button>
</form>
  1. 填写表单,点击提交查看控制台form.value
    在这里插入图片描述

(三)表单验证

Angular表单提供的几个常见的验证规则:

  • required 必填字段
  • minlength 最小长度
  • maxlength 最大长度
  • pattern 正则验证
  1. 组件模版中,在需要验证的表单项上加上需要验证的规则
名字:<input type="text" name="username" ngModel pattern="\d" maxlength="10"/>
年龄:<input type="number" name="age" ngModel required/>
  1. 组件类中,在提交的时候,可以通过form.valid属性(Boolean)查看表单验证是否通过
onSubmit(form: any) {
    
    
   console.log(form.valid); 
}
  1. 对于提交按钮,可以限制当form.valid为false时禁止点击。!myForm.valid可以写成myForm.invalid
<button type="submit" [disabled]="!myForm.valid">Submit</button>
  1. 验证不通过时,可以在HTML中增加提示信息
    需要单个表单项显示自己的提示信息的时候,就必须先拿到单独的表单项,通过#username=’ngModel‘获取该表单项;
    username的类型是NgMdel,根据touched属性获取该表单项是否点击或者输入;
    touched属性当input框有鼠标移入并且移出后会变为true
    invalid属性获取该表单项验证是否不通过;
    errors属性存储所有不通过的验证项的信息。
名字:<input #username='ngModel' type="text" required name="username" pattern="\d*" ngModel maxlength="10"/>
<button type="submit">Submit</button>
<div *ngIf="username.touched && username.invalid && username.errors" style="color: red">
    <div *ngIf="username.errors['required']">用户名必填</div>
    <div *ngIf="username.errors['pattern']">用户名必须是数字</div>
    <div *ngIf="username.errors['maxlength']">用户名最大长度为10</div>
</div>

二、模型驱动

表单的控制逻辑写在组件类中,对于表单的验证等操作更加灵活,比较适合复杂的表单。
模型驱动表单是一个FormGroup类的实例,它可以对表单整体进行验证;其中的每一个表单项都是一个FormControl类的实例,可以对单个表单项进行验证以及可以监测表单值的修改。

(一)创建表单

  1. 在组件所属的模块中,需要引入ReactiveFormsModule模块,并且添加到imports数组中
import {
    
    ReactiveFormsModule} from "@angular/forms";
@NgModule({
    
    
    ...
    imports: [
        ...
        ReactiveFormsModule
    ],
    ...
})
  1. 在组件类中,需要引入需要的模块,并且将当前表单定义为FormGroup类的实例,将每一个表单项定义为FormControl类的实例
...
import {
    
    FormGroup, FormControl} from "@angular/forms";
...
export class LayoutComponent {
    
    
    public form: FormGroup = new FormGroup({
    
    
        username: new FormControl(),
        age: new FormControl(),
    });
    onSubmit() {
    
    
        console.log(this.form);
    }
}
  1. 在组件模版中,当前表单使用formGroup属性绑定组件类中定义的FormGroup实例,表单项使用formControllName绑定每一个FormControl实例;formGroup绑定的是一个对象,所以需要使用中括号;formControllName绑定的是字符串,所以不需要中括号。
<form [formGroup]="form" (submit)="onSubmit()">
    名字:<input type="text" formControlName="username"/>
    年龄:<input type="number" formControlName="age"/>
    <button type="submit">Submit</button>
</form>
  1. 输入表单项,点击提交,看一下输出的this.form的数据结构
    在这里插入图片描述

(二)表单分组

  1. 组件类中,在FormGroup对象内部创建FormGroup对象就可以实现表单分组
public form: FormGroup = new FormGroup({
    
    
    user: new FormGroup({
    
    
        username: new FormControl(),
        age: new FormControl(),
    }),
    car: new FormControl()
});
  1. 组件模版中,分组使用formGroupName绑定分组的名称
<form [formGroup]="form" (submit)="onSubmit()">
    <ng-container formGroupName="user">
       名字:<input type="text" formControlName="username"/>
       年龄:<input type="number" formControlName="age"/>
    </ng-container>
    车辆:<input type="text" formControlName="car"/>
    <button type="submit">Submit</button>
</form>

(三)动态创建表单:FormArray

FormArray用于动态添加一组表单项。整个表单form需要绑定一个FormGroup实例对象,这个实例对象内部可以放置FormControl对象(单个表单项)、FormGroup对象(一组表单项),也可以放置FormArray对象(动态添加的一组表单项)。

  1. 组件类中,在当前表单绑定的FormGroup实例对象里面,创建一个属性,类型为FormArray,里面的每一个元素都是一个FormGroup。初始化放一个元素。
public carForm: FormGroup = new FormGroup({
    
    
    cars: new FormArray([
        new FormGroup({
    
    
            name: new FormControl(),
            power: new FormControl()
        })
    ])
});
  1. 组件模版中
  • 通过formArrayName绑定这个FormArray对象的属性名,就可以实现模版和FormArray对象数据的双向数据绑定。
  • FormArray实例的controls属性,是一个数组,保存动态添加的表单组的数据。由于是一个数组,所以渲染的时候,需要通过*ngFor进行绑定。
  • controls的每一项,都是一个FormGroup实例对象,所以对于每一项,都需要使用FormGroupName绑定当前元素的index属性
  • 每一个FormGroup实例对象,里面的表单项,都是一个FormControl实例对象,所以需要使用FormControlName绑定属性名
<form [formGroup]="carForm" (submit)="onSubmit()">
    <div formArrayName="cars">
        <div *ngFor="let car of cars.controls; let i = index" [formGroupName]="i">
            <input formControlName="name" placeholder="Car name">
            <input formControlName="power" placeholder="Car power">
        </div>
    </div>
</form>
  1. FormArray适用于动态添加表单组,所以,我们需要一个按钮,触发添加表单组的操作。以及,向FormArray对象里添加一个元素的方法。FormArray对象的push方法可以实现向FormArray对象中添加一个元素。
<button (click)="addCar()">增加一组表单</button>
addCar() {
    
    
    const cars = this.carForm.get('cars') as FormArray;
    cars.push(new FormGroup({
    
    
        name: new FormControl(),
        power: new FormControl()
    }));
}
  1. 对于cars这个变量,每次添加表单的时候,都需要获取一遍,所以可以将cars,定义为一个get形式的变量。这样就可以直接通过this.cars访问cars这个FromArray实例对象。
get cars(): FormArray {
    
    
    return this.carForm.get('cars') as FormArray;
}
  1. 当需要删除动态添加进来的表单组时,在组件模版中,每一个动态添加的FormGroup对象都需要一个删除按钮,触发删除操作,并将当前FormGroup对象的index传递过去;在组件类中,需要调用FromArray对象的removeAt方法,删除指定索引的formGroup对象。
<div *ngFor="let car of cars.controls; let i = index" [formGroupName]="i">
    <input formControlName="name" placeholder="Car name">
    <input formControlName="power" placeholder="Car power">
    <button (click)="removeCar(i)">Remove</button>
</div>
removeCar(i: number) {
    
    
    // 从cars中删除第i个元素。比数组删除元素方便
    this.cars.removeAt(i);
}
  1. 增加submit事件,提交时打印当前表单的value
    在这里插入图片描述

(三)表单验证

  1. 内置验证器
    在组件类中需要引入内置验证器;使用new关键字创建FormControl对象时,第二个参数传递一个验证规则组成的数组
    并且将username定义为get类型的变量方便获取。
import {
    
    FormGroup, FormControl,Validators} from "@angular/forms";
public carForm: FormGroup = new FormGroup({
    
    
    username: new FormControl('', [
        Validators.required,
        Validators.minLength(4)])
})
get username() {
    
    
    return this.carForm.get('username') as FormControl;
}

组件模版中,可以通过username的几个属性,判断这几个验证规则是否通过;可以通过carForm.valid判断表单中所有的验证规则是否都通过了。

<form [formGroup]="carForm" (submit)="onSubmit()">
    <input type="text" formControlName="username">
    <div *ngIf="username.touched && username.invalid && username.errors">
        <div *ngIf="username.errors['required']">Username is required</div>
        <div *ngIf="username.errors['minlength']">Username must be at least 3 characters long</div>
    </div>
    <button type="submit" [disabled]="carForm.invalid">Submit</button>
</form>

(四)自定义表单验证器

自定义验证器有如下几条规则

  1. 自定义验证器是一个TypeScript
  2. 类中包含具体的验证方法,验证方法必须为静态方式,使用static修饰
  3. 验证方法接受一个参数control,类型为AbstractControl,就是FormControl类的实例对象的类型。
  4. 如果验证成功,返回null
  5. 如果验证失败,返回一个对象,key为验证标识,valuetrue,表示该项验证失败
(1)自定义同步表单验证器
1. 定义同步验证器
  • 验证器是一个ts类,所以要定义到一个.ts文件里面
  • 接收的参数类型是AbstractControl
  • 返回的参数类型是ValidationErrors | null
  • 方法名和标识名保持一致
  • 这个类需要被组件引入,所以需要使用export导出
import {
    
    AbstractControl, ValidationErrors} from "@angular/forms";

export class MyValidators {
    
    
    static connotContainSpace(
        control: AbstractControl
    ): ValidationErrors | null {
    
    
        if (/\s/.test(control.value)) {
    
    
            return {
    
    connotContainSpace: true}
        }
        return null;
    }
}
2. 使用同步验证器
  • 在组件类中引入自定义验证器
import {
    
    MyValidators} from "./MyValidators";
  • 在表单项new的时候,将需要该表单项需要使用的验证规则传递进去。直接写定义,不需要调用。
public carForm: FormGroup = new FormGroup({
    
    
    username: new FormControl('', [
        Validators.required,
        Validators.minLength(4),
        MyValidators.connotContainSpace])
})
  • 在组件模版中提示
<div *ngIf="username.touched && username.invalid && username.errors">
    <div *ngIf="username.errors['required']">Username is required</div>
    <div *ngIf="username.errors['minlength']">Username must be at least 3 characters long</div>
    <div *ngIf="username.errors['connotContainSpace']">用户名不能包含空格</div>
</div>
(2)自定义异步表单验证器
1. 定义异步验证器
  • 异步验证器返回值的类型是PromiseObservable,这个Promise resolve的结果是Validators | null
  • 只有所有的同步验证都通过后才会执行异步验证,如果同步验证存在不通过的情况,则不会执行异步验证
// 异步验证器
static shouldBeUnique(
    control: AbstractControl
): Promise<ValidationErrors | null> {
    
    
    return new Promise((resolve, reject) => {
    
    
        setTimeout(() => {
    
    
            if (control.value === 'mosh') {
    
    
                resolve({
    
    shouldBeUnique: true})
            } else {
    
    
                resolve(null)
            }
        }, 2000)
    })
}
2. 使用异步验证器
  • 异步验证器要放在formControl对象创建时的第三个参数的位置
  • 在模版中的使用与内置验证器相同
public carForm: FormGroup = new FormGroup({
    
    
    username: new FormControl('', [
        Validators.required,
        Validators.minLength(5),
        MyValidators.connotContainSpace],
        MyValidators.shouldBeUnique)
})
3. 显示验证中状态
  • formControl对象有一个属性pending,异步验证执行过程中,该属性为true,否则为false
<div *ngIf="username.pending">正在验证...</div>

(五)FormBuilder

  • FormBuilder是一个类,这个类的实例对象可以帮助我们快速创建表单
  • 创建FormGroup对象可以使用formBuilder.group()方法,这个方法接收两个参数,第一个参数是controls,即表单项组成的对象,第二个参数是options,即对于formGroup对象的校验规则;写在这里的校验规则通常是自定义校验规则,接收formGroup作为参数,可以定义对于整个表单里面的所有表单项的验证规则。比如两个表单项不能相同等等。
  • 创建FormControl对象可以使用formBuilder.control()方法,这个方法可以接受三个参数,第一个参数是表单项的默认值,第二个参数是同步表单验证器组成的数组,第三个参数是异步表单验证器组成的数组
  • 创建FormControl对象还可以直接将一个数组作为表单项的value,数组元素列表同上述formBuilder.control()方法的参数列表
constructor(private formBuilder: FormBuilder) {
    
    
}

public form:FormGroup = this.formBuilder.group({
    
    
    username: this.formBuilder.control(''),
    password: ['', [Validators.required, Validators.minLength(6)]],
})

(六)模型驱动表单常用方法

(1)patchValue
  • 设置表单控件的值,可以设置全部,也可以设置一个
onPatchValue() {
    
    
    this.form.patchValue({
    
    
        username: 'mosh'
    })
}
(2)setValue
  • 设置全部表单控件的值。不设置全部就不会起作用。
onSetValue() {
    
    
    this.form.setValue({
    
    
        username: 'meggie',
        password: '123456'
    })
}
(3)valueChanges
  • 当表单控件的值发生变化时触发的事件。
  • 需要获取到某一个想要监听变化的表单控件,然后注册监听函数
  • 订阅函数subscribe中获取到的参数就是修改后的值
ngOnInit() {
    
    
    this.form.get('username')?.valueChanges.subscribe(value => {
    
    
        console.log(value);
    })
}
(4)reset
  • 表单内容置空
onReset() {
    
    
   	this.form.reset();
}

猜你喜欢

转载自blog.csdn.net/weixin_45855469/article/details/130515587
今日推荐