Angular响应式表单

Angular响应式表单

Angular响应式表单是建立在RxJS基础上,有很多接口是使用RxJs接口实现

一.响应式表单基础元件

1.项目引入

  • @angular/forms中引入ReactiveFormsModule
  • 响应式表单基础元件: FormControl [来自@angular/forms]

2.项目构建

  • 创建FormControl对象

    import { Component } from '@angular/core';
    import { FormControl } from '@angular/forms';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      user = new FormControl();
    }
    
  • 使用定义的对象

    <input type="text" [formControl]="user" />
    <div>
        {{user.value | json}}
    </div>
    
  • 效果:当在输入框输入内容时,会直接在页面中进行显示

3.组件中获取和更新控件的值

  • 可以通过监听FormControl的valueChanges方法获取对应值,也可以通过setValue方法设置对应值

    import { Component, OnInit } from '@angular/core';
    import { FormControl } from '@angular/forms';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements OnInit{
      user = new FormControl();
      ngOnInit() {
        // 由于FormControl的valueChanges方法返回的是Observable对象
        // 需要使用subscribe的方式进行监听
        this.user.valueChanges.subscribe((val)=>{
          console.log(val);
        });
      }
      changeUserName() {
        this.user.setValue('Jack'); //通过FormControl的setValue方法可以设置对应的值
      }
    }
    
  • 在模板中进行显示

    <input type="text" [formControl]="user"/>
    <button (click)="changeUserName()">Click</button>
    <div>
      {{user.value | json}}
    </div>
    

二.使用FormGroup创建表单

1.项目引入

  • @angular/forms中引入FormGroup和FormControl

2.实例代码

  • ts中定义FormGroup对象,并包含多个FormControl对象;声明onSubmit方法用于打印FormGroup中的数据信息

    import { Component, OnInit } from '@angular/core';
    import { FormGroup, FormControl } from '@angular/forms';
    @Component({
      selector: 'app-FormGroupTest',
      templateUrl: './FormGroupTest.component.html',
      styleUrls: ['./FormGroupTest.component.css']
    })
    export class FormGroupTestComponent {
      userInfo = new FormGroup({
        userName: new FormControl(),
        phone: new FormControl(),
        sex: new FormControl()
      });
      onSubmit() {
        console.log(this.userInfo.value);
      }
    }
    
  • html中使用[formGroup]绑定ts中的对象,通过angular自带的form标签的提交事件ngSubmit设置点击submit按钮时的触发事件

    <form [formGroup]="userInfo" (ngSubmit)="onSubmit()">
      <input type="text" formControlName="userName"/>
      <input type="text" formControlName="phone"/>
      
      <input type="radio" formControlName="sex" value="1"/><input type="radio" formControlName="sex" value="0"/><button type="submit">提交</button>
    </form>
    
  • 此时点击提交后打印的内容就是表单中实际值

3.对表单字段进行分组

  • 如果想声明嵌套表单,则可以通过FormGroup中嵌套FormGroup的方式进行实现

  • TypeScript中实现内容

    import { Component, OnInit } from '@angular/core';
    import { FormGroup, FormControl } from '@angular/forms';
    
    @Component({
      selector: 'app-FormGroupTest',
      templateUrl: './FormGroupTest.component.html',
      styleUrls: ['./FormGroupTest.component.css']
    })
    export class FormGroupTestComponent {
      userInfo = new FormGroup({
        userName: new FormControl(),
        phone: new FormControl(),
        sex: new FormControl(),
        pwd: new FormGroup({
          password: new FormControl(),
          confirmPassword: new FormControl()
        })
      });
      onSubmit() {
        console.log(this.userInfo.value);
      }
    }
    
  • Html中在form中添加form并设置属性名为formGroupName

    <form [formGroup]="userInfo" (ngSubmit)="onSubmit()">
      <input type="text" formControlName="userName"/>
      <input type="text" formControlName="phone"/>
      
      <input type="radio" formControlName="sex" value="1"/><input type="radio" formControlName="sex" value="0"/><form formGroupName="pwd">
        <input type="text" formControlName="password"/>
        <input type="text" formControlName="confirmPassword"/>
      </form>
    
      <button type="submit">提交</button>
    </form>
    
  • 输出内容为嵌套对象的形式

    {
        phone: "180411",
        pwd: {password: "123456", confirmPassword: "123456"},
        sex: "1",
        userName: "jack"
    }
    

4.更新表单中字段

  • 注意:如果使用FormGroup的方式声明表单,再使用form.setValue()方法时需要设置所有表单值,如设置不全则会进行报错

  • 更新表单指定字段方式一:使用form.patchValue(键值对象);方法设置表单中指定字段的值

  • 更新表单指定字段方式二:先使用form.get(键)获取formControl对象,再调用setValue方法:,如this.userInfo.get('userName').setValue('Jerry');

  • 实例代码

    • TypeScript

      import { Component, OnInit } from '@angular/core';
      import { FormGroup, FormControl } from '@angular/forms';
      @Component({
        selector: 'app-FormGroupTest',
        templateUrl: './FormGroupTest.component.html',
        styleUrls: ['./FormGroupTest.component.css']
      })
      export class FormGroupTestComponent {
        userInfo = new FormGroup({
          userName: new FormControl(),
          phone: new FormControl(),
          sex: new FormControl(),
          pwd: new FormGroup({
            password: new FormControl(),
            confirmPassword: new FormControl()
          })
        });
        onSubmit() {
          console.log(this.userInfo.value);
        }
        updateFormData() {
          // this.userInfo.patchValue({'userName':'Jack'});
          this.userInfo.get('userName').setValue('Jerry');
        }
      }
      
    • HTML

      <form [formGroup]="userInfo" (ngSubmit)="onSubmit()">
        <input type="text" formControlName="userName"/>
        <input type="text" formControlName="phone"/>
        
        <input type="radio" formControlName="sex" value="1"/><input type="radio" formControlName="sex" value="0"/><form formGroupName="pwd">
          <input type="text" formControlName="password"/>
          <input type="text" formControlName="confirmPassword"/>
        </form>
      
        <button type="submit">提交</button>
        <button (click)="updateFormData()">更新表单</button>
      </form>
      

5.使用内置验证器验证表单字段

  • 可以通过给FormControl构造函数的第一个参数设置表单字段的默认值,第二个参数设置验证器类型如required、最小最大长等

  • 通过声明get方法的方式指定对应get的属性值,方便在html中直接调用

  • 通过FromControl的invalid的方法可以判断表单字段是否通过,touched和dirty方法指定当触发之后再回执行判断,hasError方法设置判断对应类型的错误

  • TypeScript代码

    import { Component, OnInit } from '@angular/core';
    import { FormGroup, FormControl, Validators, AbstractControl } from '@angular/forms';
    
    @Component({
      selector: 'app-FormGroupTest',
      templateUrl: './FormGroupTest.component.html',
      styleUrls: ['./FormGroupTest.component.css']
    })
    export class FormGroupTestComponent {
      userInfo = new FormGroup({
        // 参数1: 设置默认值; 参数2:设置校验器
        userName: new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(5)]),
        phone: new FormControl('', [Validators.required, Validators.pattern(/^1[34578]\d{9}$/)]),
        sex: new FormControl('1', [Validators.required]),
        pwd: new FormGroup({
          password: new FormControl(),
          confirmPassword: new FormControl()
        })
      });
      onSubmit() {
        console.log(this.userInfo.value);
      }
      updateFormData() {
        // this.userInfo.patchValue({'userName':'Jack'});
        this.userInfo.get('userName').setValue('Jerry');
      }
      // 定义get方法直接获取属性值
      get userName(): AbstractControl {
        return this.userInfo.get('userName');
      }
      get phone(): AbstractControl {
        return this.userInfo.get('phone');
      }
    }
    
  • HTML代码

    <form [formGroup]="userInfo" (ngSubmit)="onSubmit()">
      <fieldset>
        <label>账号:
          <input type="text" formControlName="userName"/>
        </label>
        <!-- 判断如果用户名的表单无效且是在被点击或dirty状态时显示错误内容 -->
        <span *ngIf="userName.invalid && (userName.touched || userName.dirty)">
          <span *ngIf="userName.hasError('required')">请输入用户名</span>
          <span *ngIf="userName.hasError('minlength') || userName.hasError('maxlength')">请输入3-5位用户名</span>
        </span>
      </fieldset>
      <fieldset>
        <label>手机号:
          <input type="text" formControlName="phone"/>
        </label>
        <span *ngIf="phone.invalid && (phone.touched || phone.dirty)">
          请输入手机号
        </span>
      </fieldset>
      <fieldset>
        <label>性别:
          <input type="radio" formControlName="sex" value="1"/><input type="radio" formControlName="sex" value="0"/></label>
      </fieldset>
      <fieldset>
        <form formGroupName="pwd">
          <label>密码:
            <input type="text" formControlName="password"/>
          </label>
          <label>确认密码:
            <input type="text" formControlName="confirmPassword"/>
          </label>
        </form>
      </fieldset>
      <button type="submit">提交</button>
      <button (click)="updateFormData()">更新表单</button>
    </form>
    

6.提示信息的优化处理

  • 可以使用表单字段的error对象获取对应的错误信息内容。当校验都通过时返回null,required条件没通过时返回{'required':true},当length条件没通过时返回{'maxlength':{'requiredLength':5,'actualLength':实际字段长度}}

  • 可以通过修改FormControl的第二个参数值为AbstractControlOptions类型定义对应的校验规则,在对象中可以通过validators设置校验器类型,通过updateOn设置什么时间触发校验

  • TypeScript代码

    import { Component, OnInit } from '@angular/core';
    import { FormGroup, FormControl, Validators, AbstractControl } from '@angular/forms';
    
    @Component({
      selector: 'app-FormGroupTest',
      templateUrl: './FormGroupTest.component.html',
      styleUrls: ['./FormGroupTest.component.css']
    })
    export class FormGroupTestComponent {
      userInfo = new FormGroup({
        userName: new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(5)]),
        // 可以设置第二个参数为一个AbstractControlOptions类型
        // 在该对象类型中可以传递validators / asyncValidators / updateOn
        // 其中updateOn表示当什么时间触发校验
        phone: new FormControl('', {validators: [Validators.required, Validators.pattern(/^1[34578]\d{9}$/)], updateOn:'blur'}),
        sex: new FormControl('1', [Validators.required]),
        pwd: new FormGroup({
          password: new FormControl(),
          confirmPassword: new FormControl()
        })
      });
      onSubmit() {
        console.log(this.userInfo.value);
      }
      updateFormData() {
        // this.userInfo.patchValue({'userName':'Jack'});
        this.userInfo.get('userName').setValue('Jerry');
      }
      get userName(): AbstractControl {
        return this.userInfo.get('userName');
      }
      get phone(): AbstractControl {
        return this.userInfo.get('phone');
      }
    }
    
  • HTML代码

    <form [formGroup]="userInfo" (ngSubmit)="onSubmit()">
      <fieldset>
        <label>账号:
          <input type="text" formControlName="userName"/>
        </label>
        <!-- 判断如果用户名的表单无效且是在被点击或dirty状态时显示错误内容 -->
        <span *ngIf="userName.invalid && (userName.touched || userName.dirty)">
          <!-- 使用errors对象可以获取到对应的错误内容 -->
          <span *ngIf="userName.errors.required">请输入用户名</span>
          <span *ngIf="userName.errors.minlength">用户名的长度不能小于{{userName.errors.minlength.requiredLength}}个字符</span>
          <span *ngIf="userName.errors.maxlength">用户名的长度不能超过{{userName.errors.maxlength.requiredLength}}个字符</span>
          <!--
            检验都通过返回null
            当required条件没通过时返回的是: {'required': true} 
            当length条件没通过时返回的是: {'maxlength': {'requiredLength':5, 'actualLength':6}}
          -->
        </span>
      </fieldset> 
      <fieldset>
        <label>手机号:
          <input type="text" formControlName="phone"/>
        </label>
        <span *ngIf="phone.invalid && (phone.touched || phone.dirty)">
          请输入手机号
        </span>
      </fieldset>
      <fieldset>
        <label>性别:
          <input type="radio" formControlName="sex" value="1"/><input type="radio" formControlName="sex" value="0"/></label>
      </fieldset>
      <fieldset>
        <form formGroupName="pwd">
          <label>密码:
            <input type="text" formControlName="password"/>
          </label>
          <label>确认密码:
            <input type="text" formControlName="confirmPassword"/>
          </label>
        </form>
      </fieldset>
      <button type="submit">提交</button>
      <button (click)="updateFormData()">更新表单</button>
    </form>
    

7.自定义表单规则

  • 查看源码可以发现定义表单规则函数参数是AbstractControl类型对象,返回是ValidationErrors类型对象,即具体的错误提示对象

  • 使用时可以直接使用对应的表单校验规则函数

  • 如果是FormGroup嵌套FormGroup的类型,则可以在get方法中参数使用子FormGroup.字段类型的方式获取对应的属性值

  • 错误内容显示在页面即可以使用errors类型对象的自定义错误键获取错误提示内容

  • 校验器定义代码

    import { AbstractControl, ValidationErrors } from "@angular/forms";
    
    export function passwordValidator (control: AbstractControl) : ValidationErrors | null {
        const reg = /^[A-Z][\w\_\-]{2,5}$/;
        return reg.test(control.value) ? null : {passwordError: '密码必须以大写字母开头,只能包含数字、字母、下划线、中划线、长度在3-6之间'};
    }
    
  • TypeScript代码

    import { Component, OnInit } from '@angular/core';
    import { FormGroup, FormControl, Validators, AbstractControl } from '@angular/forms';
    import { passwordValidator } from './Validators';
    
    @Component({
      selector: 'app-FormGroupTest',
      templateUrl: './FormGroupTest.component.html',
      styleUrls: ['./FormGroupTest.component.css']
    })
    export class FormGroupTestComponent {
      userInfo = new FormGroup({
        userName: new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(5)]),
        // 可以设置第二个参数为一个AbstractControlOptions类型
        // 在该对象类型中可以传递validators / asyncValidators / updateOn
        // 其中updateOn表示当什么时间触发校验
        phone: new FormControl('', {validators: [Validators.required, Validators.pattern(/^1[34578]\d{9}$/)], updateOn:'blur'}),
        sex: new FormControl('1', [Validators.required]),
        pwd: new FormGroup({
          password: new FormControl('', passwordValidator),
          confirmPassword: new FormControl('', passwordValidator)
        })
      });
      onSubmit() {
        console.log(this.userInfo.value);
      }
      updateFormData() {
        // this.userInfo.patchValue({'userName':'Jack'});
        this.userInfo.get('userName').setValue('Jerry');
      }
      get userName(): AbstractControl {
        return this.userInfo.get('userName');
      }
      get phone(): AbstractControl {
        return this.userInfo.get('phone');
      }
      get password(): AbstractControl {
        return this.userInfo.get('pwd.password');//通过formGroup.字段名的方式获取具体表单字段
      }
      get confirmPassword(): AbstractControl {
        return this.userInfo.get('pwd.confirmPassword');
      }
    }
    
  • HTML代码

    <form [formGroup]="userInfo" (ngSubmit)="onSubmit()">
      <fieldset>
        <label>账号:
          <input type="text" formControlName="userName"/>
        </label>
        <!-- 判断如果用户名的表单无效且是在被点击或dirty状态时显示错误内容 -->
        <span *ngIf="userName.invalid && (userName.touched || userName.dirty)">
          <!-- 使用errors对象可以获取到对应的错误内容 -->
          <span *ngIf="userName.errors.required">请输入用户名</span>
          <span *ngIf="userName.errors.minlength">用户名的长度不能小于{{userName.errors.minlength.requiredLength}}个字符</span>
          <span *ngIf="userName.errors.maxlength">用户名的长度不能超过{{userName.errors.maxlength.requiredLength}}个字符</span>
          <!--
            检验都通过返回null
            当required条件没通过时返回的是: {'required': true} 
            当length条件没通过时返回的是: {'maxlength': {'requiredLength':5, 'actualLength':6}}
          -->
        </span>
      </fieldset> 
      <fieldset>
        <label>手机号:
          <input type="text" formControlName="phone"/>
        </label>
        <span *ngIf="phone.invalid && (phone.touched || phone.dirty)">
          请输入手机号
        </span>
      </fieldset>
      <fieldset>
        <label>性别:
          <input type="radio" formControlName="sex" value="1"/><input type="radio" formControlName="sex" value="0"/></label>
      </fieldset>
      <fieldset>
        <form formGroupName="pwd">
          <label>密码:
            <input type="text" formControlName="password"/>
          </label>
          <!-- 直接通过返回的errors中自己定义的错误键获取即可 -->
          <span *ngIf="password.invalid && (password.touched || password.dirty)">
            {{password.errors.passwordError}}
          </span>
          <label>确认密码:
            <input type="text" formControlName="confirmPassword"/>
          </label>
          <span *ngIf="confirmPassword.invalid && (confirmPassword.touched || confirmPassword.dirty)">
            {{password.errors.passwordError}}
          </span>
        </form>
      </fieldset>
      <button type="submit">提交</button>
      <button (click)="updateFormData()">更新表单</button>
    </form>
    

8.给FormGroup添加校验规则

  • 完成功能:校验密码与确认密码值是否相同,且定义在一个FormGroup中

  • 定义参数是FormGroup的校验规则作为新的校验器

    // 用于校验表单中密码重复输入是否相同
    export function passwordEqualValidator(group: FormGroup): ValidationErrors | null {
        const password = group.get('password').value;
        const confirm = group.get('confirmPassword').value;
        return password === confirm ? null : {notEqual: '两次输入的密码不一致.'}
    }
    
  • 将passwordEqualValidator作为FormGroup第二个参数进行赋值,并定义对应FormGroup的get方法,方便在html中进行获取

    import { Component, OnInit } from '@angular/core';
    import { FormGroup, FormControl, Validators, AbstractControl } from '@angular/forms';
    import { passwordValidator, passwordEqualValidator } from './Validators';
    
    @Component({
      selector: 'app-FormGroupTest',
      templateUrl: './FormGroupTest.component.html',
      styleUrls: ['./FormGroupTest.component.css']
    })
    export class FormGroupTestComponent {
      userInfo = new FormGroup({
        userName: new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(5)]),
        // 可以设置第二个参数为一个AbstractControlOptions类型
        // 在该对象类型中可以传递validators / asyncValidators / updateOn
        // 其中updateOn表示当什么时间触发校验
        phone: new FormControl('', {validators: [Validators.required, Validators.pattern(/^1[34578]\d{9}$/)], updateOn:'blur'}),
        sex: new FormControl('1', [Validators.required]),
        pwd: new FormGroup({
          password: new FormControl('', passwordValidator),
          confirmPassword: new FormControl('', passwordValidator)
        }, passwordEqualValidator)
      });
      onSubmit() {
        console.log(this.userInfo.value);
      }
      updateFormData() {
        // this.userInfo.patchValue({'userName':'Jack'});
        this.userInfo.get('userName').setValue('Jerry');
      }
      get userName(): AbstractControl {
        return this.userInfo.get('userName');
      }
      get phone(): AbstractControl {
        return this.userInfo.get('phone');
      }
      get password(): AbstractControl {
        return this.userInfo.get('pwd.password');//通过formGroup.字段名的方式获取具体表单字段
      }
      get confirmPassword(): AbstractControl {
        return this.userInfo.get('pwd.confirmPassword');
      }
      get pwd() {
        return this.userInfo.get('pwd');
      }
    }
    
  • 页面中应添加针对FormGroup的规则invalid判断,并通过errors对象获取对应的错误信息

    <form [formGroup]="userInfo" (ngSubmit)="onSubmit()">
      <fieldset>
        <label>账号:
          <input type="text" formControlName="userName"/>
        </label>
        <!-- 判断如果用户名的表单无效且是在被点击或dirty状态时显示错误内容 -->
        <span *ngIf="userName.invalid && (userName.touched || userName.dirty)">
          <!-- 使用errors对象可以获取到对应的错误内容 -->
          <span *ngIf="userName.errors.required">请输入用户名</span>
          <span *ngIf="userName.errors.minlength">用户名的长度不能小于{{userName.errors.minlength.requiredLength}}个字符</span>
          <span *ngIf="userName.errors.maxlength">用户名的长度不能超过{{userName.errors.maxlength.requiredLength}}个字符</span>
          <!--
            检验都通过返回null
            当required条件没通过时返回的是: {'required': true} 
            当length条件没通过时返回的是: {'maxlength': {'requiredLength':5, 'actualLength':6}}
          -->
        </span>
      </fieldset> 
      <fieldset>
        <label>手机号:
          <input type="text" formControlName="phone"/>
        </label>
        <span *ngIf="phone.invalid && (phone.touched || phone.dirty)">
          请输入手机号
        </span>
      </fieldset>
      <fieldset>
        <label>性别:
          <input type="radio" formControlName="sex" value="1"/><input type="radio" formControlName="sex" value="0"/></label>
      </fieldset>
      <fieldset>
        <form formGroupName="pwd">
          <label>密码:
            <input type="text" formControlName="password"/>
          </label>
          <!-- 直接通过返回的errors中自己定义的错误键获取即可 -->
          <span *ngIf="password.invalid && (password.touched || password.dirty)">
            {{password.errors.passwordError}}
          </span>
          <label>确认密码:
            <input type="text" formControlName="confirmPassword"/>
          </label>
          <span *ngIf="confirmPassword.invalid && (confirmPassword.touched || confirmPassword.dirty)">
            {{confirmPassword.errors.passwordError}}
          </span>
          <span *ngIf="pwd.invalid && !password.invalid && !confirmPassword.invalid">
            {{pwd.errors.notEqual}}
          </span>
        </form>
      </fieldset>
      <button type="submit">提交</button>
      <button (click)="updateFormData()">更新表单</button>
    </form>
    

9.动态获得表单中FormControl的值

  • 动态获得FormControl的值

    this.formGroupName.get('FormControlName').valueChanges.subscribe(value=>{
        ...
    })
    
  • 通过FormControl的setValidators方法设置Validators

    this.formGroupName.get('FormControlName').setValidators([...]);
    
  • 通过FormControl的clearValidators方法设置情况所有校验器

    this.formGroupName.get('FormControlName').clearValidators();
    
  • 通过调用FormControl的updateValueAndValidity方法更新数据值和校验器

    this.formGroupName.get('FormControlName').updateValueAndValidity();
    

10.使用FormBuilder服务简化表单创建过程

  • 在实际开发中,如果使用new FormGroup() new FormControl()会使代码比较笨重,可以使用Angular内置的服务FromBuilder来简化表单创建

  • FormBuilder的使用

    • 在constructor中注入FormBuilderconstructor(private fb: FormBuilder)

    • 可以通过this.fb.group({}创建FormGroup,this.fb.control()创建FormControl,this.fb.array()创建FormArray

    • 通过FormBuilder可以将代码简化成如下

      userInfo = this.fb.group({
          userName: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(5)]],
          phone: ['', {validators: [Validators.required, Validators.pattern(/^1[34578]\d{9}$/)], updateOn:'blur'}],
          ...
      });
      
发布了258 篇原创文章 · 获赞 332 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/qq_34829447/article/details/96909834
今日推荐