Angualr响应式表单

这篇具有很好参考价值的文章主要介绍了Angualr响应式表单。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

由于最近公司框架升级,抛弃了原来手动检验表单的方式,将所有的表单改为响应式,由于之前没用过,在一开始我以为只有我没有用过,了解了小组里的其他同事得知基本都不是很熟悉

后面时间比较紧,没办法只能边做边学边改了,所以难免踩了一些坑,当然也花了一些时间学习,虽然对于熟悉的人来说可能很简单,但是还是将学习的过程和小结以及解决的问题的方法总结一下,也算是一种提炼。在这里更多的是理论结合实际业务需求来说,而不是一味的按照官方文档的方式写API介绍,如果那样就是学习笔记,而不是总结了。

为什么主要介绍响应式表单呢?因为响应式表单提供对底层表单对象模型直接、显式的访问。它们与模板驱动表单相比,更加健壮:它们的可扩展性、可复用性和可测试性都更高。适用于比较复杂的表单,其实最重要的是其他的我也不会呀。

一、响应式表单基本概念

1.FormControl 、FormArray 、FormGroup

1.FormControl: 用于追踪单个表单控件的值和验证状态,例如一个栏位绑定

//初始化一个栏位的值为测试名字,并且不可用
const Name:FormControl = new FormControl({value:'测试名字', disabled: true });

2.FormArray:用于追踪表单控件数组的值和状态,例如几个栏位一起,常用的表格或者在表单中嵌入表格

//定义表单对象的属性为aliases的FormArray 
this.validateForm = this.fb.group({aliases: this.fb.array([]),});

//获取FormArray 
get aliases() {return this.validateForm.get('aliases') as FormArray;}

//给FormArray 添加item
this.aliases.push(
  this.fb.group({Id: 0,Name: [null],})
);

3.FormGroup:用于追踪单个表单控件的值和验证状态,它可以包含单个或多个FormControl 和 FormArray ,一般一个表单对应一个FormGroup实例,而表单的各个栏位对应FormControl 和FormArray ,当然他们可以互相嵌套,例如FormArray 中可以嵌套FormGroup,它的灵活性就是如此。

validateForm =  new FormGroup({Name: new FormControl({value:'测试名字', disabled: true }),});
validateForm = this.fb.group({});

4.FormBuilder:是一个可注入的服务提供者,手动创建多个表单控件实例会非常繁琐,FormBuilder 服务提供了一些便捷方法来生成表单控件,以前每一个创建要先生成FormGroup 然后生成FormControl,而使用FormBuilder的group方法可以减少重复代码,说白了就是帮助方便生成表单

validateForm!: FormGroup;
//手动创建
validateForm = new FormGroup({
    Name: new FormControl('测试名字'),
  });
  
//FormBuilder表单构建器
validateForm = this.fb.group({
  Name:[ { value:'测试名字',disabled:true}],
});



2.Validator 表单验证

表单验证用于确保用户的输入是完整和正确的。如何把单个验证器添加到表单控件中,以及如何显示表单的整体状态,通常验证器返回null表示所有的验证通过。


1.同步验证器:同步验证器函数接受一个控件实例,然后返回一组验证错误或 null,在实例化FormControl 时可以将他作为第二个参数传入

 //formControlName的值必须和ts代码中FormControl 的实例一致
 <input type="text" id="name" class="form-control" formControlName="name" required>
 
 //判断对应的FormControl 是否没通过校验 而有错误信息
 <div *ngIf="name.errors?.['required']">
    Name is required.
 </div>
//初始化一个栏位并且加入必填校验验证器
const name:FormControl = new FormControl({'测试名字', disabled: true },Validators.required,);

//获取这个FormControl
get name() { return this.heroForm.get('name'); }

2.异步验证器:异步函数接受一个控件实例并返回一个 Promise 或 Observable ,只有在所有同步验证器都通过之后,Angular 才会运行异步验证器,在实例化FormControl 时可以将他作为第三个参数传入

3.内置验证器:例如验证一些长度,不能为空可以使用已经提供的Validator 类来实现


4.自定义验证器:系统内部提供的验证器不能满足现有需求,可以使用自定义验证器做一些个性化的校验,自定义校验器必须返回ValidationErrors类型或者空

 //formControlName的值必须和ts代码中FormControl 的实例一致
 <input type="text" id="name" class="form-control" formControlName="name" required>
 
 //判断对应的FormControl 是否没通过校验 而有错误信息
 <div *ngIf="name.hasError('Invalid')">
    名字也太长了吧....
 </div>
//初始化一个栏位并且加入必填校验验证器
const name:FormControl = new FormControl({'测试名字', disabled: true },this.CustomValidators());

CustomValidators() {
 return (control: AbstractControl): ValidationErrors | null => {
    if(control.value.length!=10)
      {
        return {Invalid:true}
      }
      return null;
    };
}



3.表单及元素的基本方法和属性

  • 方法
方法 使用效果
setValue() 使用setVlue可以设置控件FormControl 的值,但是使用时必须FormGroup所有的属性一起赋值,不能单个赋值,常用在修改加载赋值。
patchValue() 使用patchValue也可以设置FormControl的值,可以根据需要设置指定的FormControl,而不需要全部设置,常用在更新某个字段值
reset () FormControl 中使用重置当前控件所有状态,FormGroup中使用就是重置表单对象里的内容,例如控件被设为不可用disabled,control.reset({ value: 'Drew', disabled: true });
markAsPristine() 是将表单控件值标记为未改变,这个方法主要用在表单重置时,此时它的状态pristine为true
markAsDirty() 是将表单FormControl 控件值标记为已改变,此时它的状态Dirty为true
updateValueAndValidity() 重新计算表单FormControl 控件的值和验证状态等
setValidators() 给表单FormControl 控件设置验证器,如果设置多个就用数组"setValidators([v1,v2,v3])" ,串行设置是覆盖的关系,不是追加
disable() 给表单FormControl 控件设置不可用,注意当FormControl 是disabled时,表单的常规取值getValue()对应值会为空,可用getRawValue()取原始值对象得到对应FormControl 的值
enable() 给表单FormControl 控件设置启用

  • 属性
属性 使用方法说明
touched 当表单FormControl 控件 的touched为true表示控件已经被获取焦点,反之同理
untouched 当untouched 为true表示控件未被获取焦点,反之同理
pristine 表示表单元素是纯净的,用户未操作过,可以使用markAsPristine方法设为true
dirty 表示表单元素是已被用户操作过,可以使用markAsDirty方法设为true
status 获取表单FormControl 控件上的的状态
Errors 获取当前控件的错误信息



二.实例分析及应用

1. 简单的表单实现
需求1

我们主要用到的框架版本是Angular 12 + NG-ZORRO, 所以在下面很多实现和示例代码将与他们有关,虽然可能代码不一样,但也只是在UI层面的区别稍微大一点点,但对于TS代码,只是换汤不换药,稍微注意一下就好了,其实下面实例中的需求,基本就是我在工作时需要做的的一些基本内容和遇到的问题,经过查阅资料后解决的思路和过程,甚至截图都一模一样。

实现最基本的表单新增功能并且校验员工ID为必填以及长度不能超过50,要实现的效果图如下

Angualr响应式表单

分析

1.首先需求未提出有特殊注意点,基本都是简单的输入框赋值然后保存,只要基本的概念搞清楚实现这种最简单

2.我们用一个FormGroup和6个FormControl 完成和界面绑定即可

3.绑定验证器用于校验长度和必填

实现步骤

1.定义html 表单结构

<!-- formGroup 属性绑定表单对象 -->
<form nz-form [formGroup]="validateForm" nzLayout="vertical">
  <nz-form-label nzRequired>Employee ID
  </nz-form-label>
  
   <!-- Employee_ErrorTrip为验证不通过弹出的提示信息 -->
   <!-- formControlName绑定表单元素FormControl -->
  <nz-form-control [nzErrorTip]="Employee_ErrorTrip">
    <input nz-input formControlName="EmployeeID"  placeholder="" />
  </nz-form-control>

  <ng-template #Employee_ErrorTrip let-control>
    <ng-container *ngIf="control.hasError('required')">
      员工编号为必填项目
    </ng-container>
  </ng-template>
</form>

2.在TypeScript代码中声明表单对象,在构造函数中注入FormBuilder,并且在ngOnInit中进行表单初始化

//定义表单对象
validateForm:FormGroup;

//构造函数注入FormBuilder
constructor(private fb: FormBuilder){}

//在声明周期钩子函数中初始化表单
ngOnInit() {
  //初始化并且绑定必填验证器和长度验证器

    this.validateForm = this.fb.group({
      EmployeeID: ['', [Validators.required, Validators.maxLength(50)]],  
    })
}

2.在表格中应用表单
需求2

需要实现表格的表单新增和提交以及个性化定制需求,要实现的效果图和需求描述如下

1.点击Add 添加一行表格 ,编辑完毕,点击Save保存数据,点击Revoke取消编辑

2.默认开始时间和结束时间禁止使用

3.当选择Contract Type为 “短期合同” Contract start date 和Contract end date可用,当选择Contract Type为 “长期合同”不可用

4.如果Contract start date 和Contract end date可用,需要验证开始结束时间合法性,例如开始事件不能超过结束时间

Angualr响应式表单

分析

1.在表格中使用表单,虽然表单在表格中,但是他的每一列同样都是一个个FormControl

2.一共4列需要输入值,就说明有4个FormControl 然后最后一列就是2个按钮

3.我们根据上面的基础知识知道,FormControl 不能单独使用,所以需要被FormGroup包裹,此时说明一行对应一个FormGroup

4.由一行对应一个FormGroup知道,我们的表格时多行的,也就是有多个FormGroup,我们可以使用FormArray来存储,因为他代表一组表单组

5.根据需求第2点默认开始时间和结束时间禁止使用,我们知道在一开始初始化时,设置开始结束时间对应的FormControl 为disabled就行了

6.第3点需求需要涉及联动,也就是当Contract Type对应的FormControl 的值为“短期合同”时,需要将 “开始结束时间”对应的FormControl设置为可用,这个需要自定义验证器来完成

实现步骤

1.首先定义Html表单结构

<nz-table [nzData]="CONTRACTS" nzTableLayout="fixed" [nzShowQuickJumper]="true">
  <thead>
    <tr>
      <th>Contract type</th>
      <th>Contract start date</th>
      <th>Contract end date</th>
      <th>Agreement item</th>
      <th>Operation</th>
    </tr>
  </thead>

  <tbody>
    <!-- 绑定表单组属性aliases -->
    <ng-container formArrayName="aliases">
      <!-- 将表单组中当前行的索引与formGroup绑定 -->
      <tr [formGroupName]="i" *ngFor="let data of aliases.controls;index as i">
        <td>
          <nz-form-item>
            <nz-form-control nzSpan="1-24">
              <!-- AccountName绑定FormControl  -->
              <nz-select nzShowSearch nzAllowClear nzPlaceHolder="" formControlName="Type">
                <nz-option *ngFor="let option of Type" [nzValue]="option.Code" [nzLabel]="option.Value">
                </nz-option>
              </nz-select>
            </nz-form-control>
          </nz-form-item>
          <nz-form-item>
            <nz-form-control nzSpan="1-24" [nzErrorTip]="StartDate">
              <nz-date-picker id="StartDate" formControlName="StartDate" nzPlaceHolder="">
              </nz-date-picker>
              <!-- 校验提示模板用于时间验证器 -->
              <ng-template #StartDate let-control>
              	<!-- 判断时间验证器是否存在beginGtendDate属性,如果有说明没有通过验证 然后展示提示信息 -->
                <ng-container *ngIf="control.hasError('beginGtendDate')">
                  开始时间不能晚于结束时间
                </ng-container>
              </ng-template>
            </nz-form-control>
          </nz-form-item>
          <nz-form-item>
            <nz-form-control nzSpan="1-24" [nzErrorTip]="EndDate">
              <nz-date-picker style="width: 100%;" formControlName="EndDate" nzPlaceHolder="">
              </nz-date-picker>
              <ng-template #EndDate let-control>
                <ng-container *ngIf="control.hasError('beginGtendDate')">
                  开始时间不能晚于结束时间
                </ng-container>
              </ng-template>
            </nz-form-control>
          </nz-form-item>
          <nz-form-item>
            <nz-form-control nzSpan="1-24">
              <nz-select nzShowSearch nzAllowClear nzPlaceHolder="" formControlName="ContractType">
                <nz-option *ngFor="let option of ContractTypes" [nzValue]="option.Code" [nzLabel]="option.Value">
                </nz-option>
              </nz-select>
            </nz-form-control>
          </nz-form-item>
        </td>
        <td>
          <button style="color: #009688;" nz-button nzType="text">
            <i nz-icon nzType="save"></i>Save
          </button>
          <button nz-button nzType="text" nzDanger>
            <i nz-icon nzType="redo"></i>Revoke
          </button>
        </td>
      </tr>
    </ng-container>
  </tbody>
</nz-table>

2.在TypeScript代码中声明表单对象validateForm,然后初始化一个FormArray类型的属性aliases的实例作为表格formArrayName的值

3.点击Add按钮时向表单对象validateForm的属性aliases添加一条数据

4.定义Contract Type 联动的自定义校验器 contractTypeValidation()方法

5.定义时间校验器 timeValidation()方法,如果时间不合法,将FormControl的错误状态设置属性beginGtendDate,然后在模板中根据这个属性来选择是否渲染日式信息文章来源地址https://www.toymoban.com/news/detail-807338.html

//定义表单对象
validateForm:FormGroup;
//构造函数注入FormBuilder
constructor(private fb: FormBuilder){}

//在声明周期钩子函数中初始化一个表单对象validateForm
ngOnInit() {
this.validateForm = this.fb.group({
      aliases: this.fb.array([]),
 	});
}
//声明aliases属性用作界面formArrayName绑定
get aliases(){
	return this.validateForm.get('aliases') as FormArray;
}

addNewRow() {
  const group = this.fb.group({
    //添加给Type初始化验证器
    Type: [null, [CommonValidators.required, this.contractTypeValidation()]],
    //初始化禁用StartDate和EndDate的FormControl 
    StartDate: [{ value: null, disabled: true }, []],
    EndDate: [{ value: null, disabled: true },[]],
    ContractType: [null, [CommonValidators.required, CommonValidators.maxLength(20)]],
  })
  this.aliases.push(group);
}

  //自定义Contract Type验证器
contractTypeValidation() {
    return (control: AbstractControl): ValidationErrors | null => {
      let contents: any[] = this.validateForm.value.aliases;
      if (control.touched && !control.pristine) {
        //获取表单组
        const formArray: any = this.validateForm.controls.aliases;
        //找到正在编辑的行的索引
        const index = contents.findIndex((x) => !x.isShowEdit);

        //获取开始结束时间FormControl 实例
        const StartDate: AbstractControl =
          formArray.controls[index].get('StartDate'),
          EndDate: AbstractControl = formArray.controls[index].get('EndDate');

        if (control.value === "短期合同") {
          //给开始结束时间设置验证器用于验证时间合法性
          StartDate.setValidators([CommonValidators.required, this.timeValidation()]);
          EndDate.setValidators([this.timeValidation()]);

          //启动开始结束时间控件
          EndDate.enable();
          StartDate.enable();
        } else {
          //Contract Type不是短期合同就清除验证器
          StartDate.clearValidators();
          EndDate.clearValidators();
          //禁用开始结束时间
          EndDate.disable();
          StartDate.disable();
        }

      }
      return null;
    }
  }



  //自定义时间验证器
 timeValidation()
  {
    return (control: AbstractControl): ValidationErrors | null => {
      if (!control.pristine) {
        let contents: any[] = this.validateForm.value.aliases;
        const formArray: any = this.validateForm.controls.aliases;
        const index = contents.findIndex((x) => !x.isShowEdit);
        //获取开始结束时间FormControl实例
        const EndDate: string = formArray.controls[index].get('EndDate').value;
        const StartDate: string =formArray.controls[index].get('StartDate').value;

        if (EndDate === null || StartDate === null) return null;

        //如果时间不合法,那就设置当前控件的错误状态 beginGtendDate为true
        if (
          Date.parse(control.value) > Date.parse(EndDate) ||
          Date.parse(control.value) < Date.parse(StartDate)
        ) {
          return { beginGtendDate: true };
        }
      }
      return null;
    }
  }

到了这里,关于Angualr响应式表单的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • phpyun 7.0补丁升级后由于缓存极致bug造成“网络招聘会”栏目不能显示,给小程序上架审核造成影响解决方法如下

    最近Phpyun系统直接从6.44升级到了v7.0跨度非常大,以前都是0.1的标准升级现在直接跨度这么大升级内容很多,我看了下后台UI都改了 体验确实好了很多,老用户建议升级下,这次升级小程序不用重新上架,完美对接,但是有一个问题却出现了那就是升级后\\\"网络招聘会\\\"栏目不显

    2024年01月23日
    浏览(46)
  • HTTP进阶,Cookie,响应的回报结果含义,ajax,form表单,不同状态码代表的结果

    目录 一、Cookie 二、响应的回报结果含义 三、实际开发中的选择 Cookie是浏览器本地存储数据的一种机制, 在浏览器访问服务器之间,此时你的浏览器对着个服务器之间是一点也不了解的,你的浏览器上是没有任何和着个服务器相关的数据的。 浏览器拿到这些数据,就可以展示

    2024年02月08日
    浏览(41)
  • 使用Django框架+SIMPLEUI+import_export设计公司后台管理系统

    本文详细介绍了如何在Windows系统上,使用PyCharm和Python的web开发框架Django,结合SIMPLEUI插件和import_export,来搭建一个功能全面的公司后台管理系统。

    2024年02月08日
    浏览(55)
  • angualr:CSS一个div内两个子元素的高度自适应

    问题:         如题 参考:         CSS一个div内两个子元素的高度自适应-腾讯云开发者社区-腾讯云

    2024年02月09日
    浏览(39)
  • 【HTML | CSS | JAVASCRIPT】一款可交互的响应式登陆注册表单,你确定不来看看嘛(附源码)

    💂 作者简介: THUNDER王,一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读,同时任汉硕云(广东)科技有限公司ABAP开发顾问。在学习工作中,我通常使用偏后端的开发语言ABAP,SQL进行任务的完成,对SAP企业管理系统,SAP ABAP开发

    2023年04月16日
    浏览(53)
  • 基于JAVA公司介绍网站设计与实现(Springboot框架) 研究背景与意义、国内外研究现状

     博主介绍 :黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程,免费 项目配有对应开发文档、开题报告、任务书、

    2024年02月03日
    浏览(43)
  • Vue公共loading升级版(处理并发异步差时响应)

    公共loading是项目系统中很常见的场景,处理方式也不外乎三个步骤: 1.通过全局状态管理定义状态值(vuex、pinia等)。 2.在程序主入口监听状态值变化,从而展示/隐藏laoding动画。 3.在请求和相应拦截器中变更状态值。 第一二步骤处理大同小异,但在第三步中,网上很多博文

    2024年02月05日
    浏览(47)
  • 快快销ShopMatrix 分销商城多端uniapp可编译5端 - 升级申请(可自定义申请表单)

      在企业或组织中,升级申请通常涉及到员工职位、权限、设备或者其他资源的提升或更新。创建一个可自定义的升级申请表单可以帮助更高效地收集和处理这类申请信息。以下是一个基本的步骤: 确定表单字段 : 申请人信息:姓名、部门、职位、联系方式等。 升级类别:

    2024年01月20日
    浏览(44)
  • 【开源】SpringBoot框架开发无代码动态表单系统

    基于Vue+SpringBoot+MySQL的无代码平台的表单平台,包括了系统数据中心模块,用来存放管理系统通用的模块,另外分别设计了动态类型模块、动态文件模块、动态字段模块和动态值模块这四大模块,用于实现档案管理系统的核心表单逻辑。 本文设计的无代码平台的表单系统的包

    2024年03月17日
    浏览(49)
  • gin框架使用系列之三——获取表单数据

    系列目录 《gin框架使用系列之一——快速启动和url分组》 《gin框架使用系列之二——uri占位符和占位符变量的获取》 get请求的参数是直接加在url后面的,在gin中获取get请求的参数主要用Query()和DefaultQuery()两个方法,示例代码如下 在浏览器中输入全部参数的运行如下: 如果

    2024年02月04日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包