• angular之formgroup


    文章目录

    说明

    看到百度上没有特别好的formgroup详解,这里翻译一下,顺便记录下自己的理解

    内容

    这里直接就搬官网的说明了:

    FormGroup 每个子 FormControl
    的值聚合到一个对象中,以每个控件名称作为键。它通过减少其子项的状态值来计算其状态。例如,如果组中的某个控件无效,则整个组都将变得无效。

    FormGroup 是用于在 Angular 中定义表单的四个基本构建块之一,与 FormControl、FormArray 和
    FormRecord 。

    实例化 FormGroup 时,请传入子控件的集合作为第一个参数。每个子项的键都会注册控件的名称。

    FormGroup 旨在用于提前已知键的用例。如果需要动态添加和删除控件,请改用 FormRecord 。

    FormGroup 接受一个可选的类型参数 TControl ,它是一种以内部控件类型作为值的对象类型

    构造函数

    new FormGroup('默认值',同步验证器,异步验证器)
    
    • 1

    属性
    属性是继承 AbstracControl,下面翻译一下:(这里要说明一下,这里翻译的,都是针对formgroup的,因为对于formArray,formControl可能是不同的)

    属性说明
    value: TValue控件组的键值对
    validator: ValidatorFn | null返回用于同步确定此控件的有效性的函数
    asyncValidator: AsyncValidatorFn | null返回用于异步确定此控件的有效性的函数
    parent:FormGroup | FormArray | null父控件
    status控件的验证状态
    valid:boolean当 status 为 VALID 时
    invalid: boolean当 status 为 INVALID 时
    pending: boolean当 status 为 PENDING 时
    disabled: boolean当控件的 status 为 DISABLED 时,禁用的控件免于验证检查,并且不包含在其祖先控件的汇总值中
    enabled:boolean同disabled
    errors:ValidationErrors | null包含因验证失败生成的任何错误的对象,如果没有错误,则为 null
    pristine: boolean如果用户尚未更改 UI 中的值,则控件是 pristine 的
    dirty: boolean如果用户更改了 UI 中的值,则控件是 dirty 的
    touched: boolean如果控件被标记为 touched ,则为真。一旦用户在其上触发了 blur 事件,则控件就会被标记为已 touched
    untouched: booleantouched的相反用法
    valueChanges: Observable一种多播 observable,每次控件的值发生更改(在 UI 中或以编程方式)时都会发出事件。它还会在你每次调用 enable() 或 disable() 时发出一个事件,而不将 {emitEvent: false} 作为函数参数传递
    statusChanges: Observable每次重新计算控件的验证 status 时都会发出事件的多播 observable。
    updateOn: FormHooks报告 AbstractControl 的更新策略(意味着控件更新本身的事件)。可能的值: ‘change’
    root: AbstractControl检索此控件的顶级祖先

    这里有几个比较难理解的,进行说明一下
    1 disabled/enabled
    这里需要注意的是,当我们disabled某个控件的时候,在提交表单时,不会进行验证。
    在这里插入图片描述
    这里密码输入框为DISABLED,在返回的value里面是没有age属性的。并且其状态是DISABLED.

    2 valueChanges/statusChanges
    这两个用法差不多,这里就讲下valueChanges的用法。这个主要是用来监视表单值变化的,是一个订阅。具体用法如下
    在这里插入图片描述
    这里呢,在构造函数里,用valueChanges进行数据监控,当name里的数值发生变化时,会订阅这个回调函数。返回的是所有的数据。如果监听单一的数据变化怎么写呢?如下:

     this.validForm.get('name')?.valueChanges.subscribe(data=>{
            debugger
     })
    
    • 1
    • 2
    • 3

    3 updateOn
    这个总共有3个默认值’change’ | ‘blur’ | ‘submit’;什么意思呢。就是规定表单的值什么时候进行验证。我们基本都是在输入的时候进行校验,所以默认是change,如果想在点击提交按钮时验证的话,就改成submit。具体写法如下:
    在这里插入图片描述

    方法

    属性说明
    registerControl()使用组的控件列表注册控件
    addControl()向此组添加控件
    removeControl()从此组中删除控件
    setControl()替换现有的控件
    contains()检查组中是否存在具有给定名称的启用控件
    setValue()它接受一个与组结构匹配的对象,以控件名称作为键
    patchValue()它接受以控件名称作为键的对象,并尽力将值与组中的正确控件进行匹配
    reset()将所有后代标记为 pristine 和 untouched ,并将所有后代的值设置为默认值,如果没有提供默认值,则为 null
    getRawValueFormGroup 的聚合值,包括任何禁用的控件

    这里举几个例子说明下,用法基本差不多
    1 patchValue
    html的写法
    在这里插入图片描述
    TS的写法
    在这里插入图片描述
    这里梳理下思路:在界面上定义了一组表单,其中年龄这个控件,在初始化时,对其数据进行了监控。那么我们用patchValue对部分数据
    进行修改时,可以设置emitEvent属性。当为true时,则在数值改变时,会触发这个订阅,即会进入到valueChanges的回调中。如果设置为false,则不会进入。

    这里把其父类的方法也总结下:

    方法说明
    setValidators()设置此控件上处于活动状态的同步验证器。调用此方法会覆盖任何现有的同步验证器
    setAsyncValidators()设置此控件上处于活动状态的异步验证器。调用此方法会覆盖任何现有的异步验证器。
    addValidators()向此控件添加一个或多个同步验证器,而不影响其他验证器。
    addAsyncValidators()向此控件添加一个或多个异步验证器,而不影响其他验证器。在运行时添加或删除验证器时,你必须调用 updateValueAndValidity() 以使新验证生效。添加已存在的验证器将没有任何效果。
    removeValidators()从此控件中删除同步验证器,而不影响其他验证器。
    removeAsyncValidators()从此控件中删除异步验证器,而不影响其他验证器。
    hasValidator()检查此控件上是否存在同步验证器函数。
    hasAsyncValidator()检查此控件上是否存在异步验证器函数。
    clearValidators()清空同步验证器列表。
    clearAsyncValidators()清空异步验证器列表。
    markAsTouched()将控件标记为 touched 。不更改值的焦点和模糊事件会触及控件。
    markAllAsTouched()将控件及其所有后代控件标记为 touched 。
    markAsUntouched()将控件标记为 untouched
    markAsDirty()将控件标记为 dirty 。当通过 UI 更改控件的值时,控件会变脏;
    markAsPristine()将控件标记为 pristine 。
    markAsPending()将控件标记为 pending 。
    disable()禁用控件。这意味着此控件免于验证检查,并从任何父级的聚合值中排除。
    enable()同disable
    setParent()设置控件的父级
    setValue()设置控件的值。抽象方法(在子类中实现)
    patchValue()修补控件的值。抽象方法(在子类中实现)。
    reset()重置控件。抽象方法(在子类中实现)。
    getRawValue()此控件的原始值。对于大多数控件实现,原始值将包括禁用的子项。
    updateValueAndValidity()重新计算控件的值和验证状态。
    setErrors()手动而不是自动运行验证时,在表单控件上设置错误。
    get()
    getError()报告具有给定路径的控件的错误数据。
    hasError()报告具有给定路径的控件是否具有指定的错误。

    推论

    关于表单验证,一套完整的复杂的表单验证,其实有很多需要注意的。这里我列举一个在企业中,实际产生的操作。
    需求:

    我们给一个学校做一个操作系统,其中有一个需求,就是根据班级,筛选出其中的班级总人数,男女人数。这个学校的10班是个特殊班级,只有女生。

    在这里插入图片描述
    我们的弹出框:
    在这里插入图片描述

    功能描述:
    1 点击添加时,弹出添加弹窗。均可编辑
    2 勾选时,弹出编辑弹窗,当选择10班时,隐藏男生条目。并且女生条目不可编辑。

    这里稍微用ng-zorro-antd框架美化一下
    代码:

    这里分两种思维方式去写代码
    第一种:
    html

    <div style="border-bottom: solid 1px rgb(165, 153, 153)">
      <button nz-button style="margin: 10px" nzType="primary">添加</button>
      <button nz-button style="margin: 10px" nzType="default">编辑</button>
    </div>
    <nz-table #basicTable [nzData]="listOfData">
      <thead>
        <tr>
          <th>班级</th>
          <th>总人数</th>
          <th>男生人数</th>
          <th>女生人数</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let data of basicTable.data">
          <td>{{ data.class }}</td>
          <td>{{ data.total }}</td>
          <td>{{ data.man }}</td>
          <td>{{ data.woman }}</td>
          <td>
            <a><i nz-icon nzType="edit" nzTheme="outline"></i></a>
            <a><i nz-icon nzType="delete" nzTheme="outline"></i></a>
          </td>
        </tr>
      </tbody>
    </nz-table>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在这里插入图片描述
    请添加图片描述
    其实添加都很简单,初始化表单之后,点击确定,就可以调用ajax把数据传到后台了。那么编辑怎么做呢?其实,编辑要做的就是回显以及初始化数据之后的联动。

    TS

     add(){
        this.isVisible=true;
      }
      edit(){
        this.isVisible=true;
      }
    
    
      isVisible:boolean=false;
      //取消
      handleCancel(){
        this.isVisible = false
      }
      //确定
      submit() {
        this.isVisible = false
      }
    
      validForm!: FormGroup;
      constructor(private fb: FormBuilder){}
      ngOnInit(): void {
        this.initForm();
      }
      initForm() {
        this.validForm = this.fb.group({
          class: ['three', [Validators.required]],
          total: ['', [Validators.required]],
          man: ['', [Validators.required]],
          woman: ['', [Validators.required]],
        });
      }
      listOfData = [
        {
          key: '1',
          class: '一班',
          total:50,
          man: 32,
          woman: 18
        },
        {
          key: '4',
          class: '四班',
          total:52,
          man: 32,
          woman: 20
        },
        {
          key: '3',
          class: '三班',
          total:26,
          man: 15,
          woman: 11
        }
      ];
      list = [
        { name: '一班', value: 'one' },
        { name: '二班', value: 'two' },
        { name: '三班', value: 'three' },
        { name: '四班', value: 'four' },
        { name: '五班', value: 'five' },
        { name: '六班', value: 'six' },
        { name: '七班', value: 'seven' },
        { name: '八班', value: 'eight' },
        { name: '九班', value: 'nine' },
        { name: '十班', value: 'ten' },
      ];
      clsChange(e: any) {
        //当班级为ten时,隐藏男生条目,并且女生条目不可编辑
        if (e == 'ten') {
          this.validForm.get('man')?.disable()
          this.validForm.get('woman')?.disable()
        }else{
          this.validForm.get('man')?.enable()
          this.validForm.get('woman')?.enable()
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    我们已经在clsChange()方法中对表单进行了启用以及禁用的逻辑判断。那么,当我们点击编辑按钮时,我们假设已经通过ajax获取到了后台返回的数据。那么我们需要赋初值了。

    假设,我们编辑10班的数据,我修改一下数据后,是这样的:
    在这里插入图片描述

    但是,发现没有,当手动设置值的时候,男生人数竟然没有自动隐藏。
    最后检查发现啊,是clsChange事件没有触发,但是我们知道ngmodelchange是angular内置的绑定事件,在patchvalue中是会被触发的。那只能说明在点击编辑按钮时,dom元素还没有渲染出来;由于我是在onInit里初始化的表单数据,当我把其放到构造函数中发现也不行。那么我们进一步测试,把弹出框的表单独立成一个组件,如下
    在这里插入图片描述
    父组件的代码:
    在这里插入图片描述

    最后测试的过程很复杂,就不记录了,把结果告诉大家:

    结论:
    1 表单回显时,初始化在oninit里面时,调用patchValue不会触发绑定在dom元素上的事件。因为在oninit周期内,dom元素还没有绑定方法。必须在ngafterviewinit里面去调用才可以。这是第一种思维方式。就是把表单联动的逻辑绑定在dom元素上时,调用的生命周期不是Oninit

    2 如果不用上面的方法,我们可以利用valueChanges方法,直接监听表单数据,改变时控制其启用禁用状态从而来控制联动效果。我个人认为这样的写法比较好,层次比较分明,业务逻辑比较清晰。

  • 相关阅读:
    【斗罗二】再度魔改,弓老被删,冰帝武魂提前曝光,官方经费已不足
    Java中常用判断方法
    解决下载中文文件名乱码问题
    使用PHPStudy在本地快速建立网站并实现局域网外访问(无公网IP)
    可视化学习:WebGL的基础使用
    Python技能树评测
    ffplay.c源码阅读之暂停、重播、快进、快退实现细节
    TF坐标变换
    sqlalchemy查询数据为空,查询范围对应的数据在数据库真实存在
    SpringSecurity初探(一)
  • 原文地址:https://blog.csdn.net/wangbiao9292/article/details/126446112