Home  >  Article  >  Web Front-end  >  A brief analysis of responsive forms in Angular

A brief analysis of responsive forms in Angular

青灯夜游
青灯夜游forward
2022-04-25 10:26:403341browse

This article will talk about the responsive form in Angular, and introduce the simple form implementation method through examples. I hope it will be helpful to everyone!

A brief analysis of responsive forms in Angular

Due to the recent upgrade of the company’s framework, the original method of manually checking forms was abandoned and all forms were changed to responsive. Since I had never used them before, I thought at the beginning I am the only one who has never used it. After getting to know other colleagues in the group, I learned that they are basically not very familiar with it. Time is tight in the future, so I have no choice but to learn and change while doing it, so it is inevitable that I have stepped on some pitfalls. Of course, I also spent some time studying. Although it may be very simple for those who are familiar with it, it is still a kind of refinement to summarize the learning process and summary as well as the methods to solve the problems. Here it is more about combining theory with actual business needs, rather than blindly writing API introductions in the way of official documents. If that is the case, it will be study notes, not a summary.

Why mainly introduce responsive forms? Because reactive forms provide direct, explicit access to the underlying

Form Object Model

. They are more robust than template-driven forms: they are more extensible, reusable, and testable. It is suitable for more complex forms. In fact, the most important thing is that I don’t know how to do other things. [Related tutorial recommendations: "angular tutorial"]1. Basic concepts of responsive forms

1.FormControl, FormArray, FormGroup1.

FormControl

: Used to track the value and validation status of a single form control, such as a field binding

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

FormArray

: used to track the values ​​and status of form control arrays, such as several fields together, commonly used tables or embedded tables in forms

//定义表单对象的属性为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

: used For tracking the value and validation status of a single form control, it can contain single or multiple FormControl and FormArray. Generally, a form corresponds to a FormGroup instance, and each field of the form corresponds to FormControl and FormArray. Of course, they can be nested in each other, such as FormArray FormGroups can be nested in them, which is how flexible they are.

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

FormBuilder

: It is an injectable service provider. Manually creating multiple form control instances will be very tedious. The FormBuilder service provides some convenient methods to generate form controls. Previously Each creation must first generate a FormGroup and then a FormControl, and using the group method of FormBuilder can reduce repeated code. To put it bluntly, it helps to easily generate forms

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

2. Validator form verification
Form validation is used to ensure that user input is complete and correct. How to add a single validator to a form control and how to display the overall status of the form. Usually the validator returns null to indicate that all validations passed.

1.

Synchronous validator

: The synchronous validator function accepts a control instance and then returns a set of validation errors or null, which can be passed when instantiating the FormControl Pass in as the second parameter <pre class="brush:js;toolbar:false;"> //formControlName的值必须和ts代码中FormControl 的实例一致 &lt;input type=&quot;text&quot; id=&quot;name&quot; class=&quot;form-control&quot; formControlName=&quot;name&quot; required&gt; //判断对应的FormControl 是否没通过校验 而有错误信息 &lt;div *ngIf=&quot;name.errors?.[&amp;#39;required&amp;#39;]&quot;&gt; Name is required. &lt;/div&gt;</pre><pre class="brush:js;toolbar:false;">//初始化一个栏位并且加入必填校验验证器 const name:FormControl = new FormControl({&amp;#39;测试名字&amp;#39;, disabled: true },Validators.required,); //获取这个FormControl get name() { return this.heroForm.get(&amp;#39;name&amp;#39;); }</pre>2.

Asynchronous validator

: The asynchronous function accepts a control instance and returns a Promise or Observable. Only after all synchronous validators have passed, Angular The asynchronous validator will be run. When instantiating the FormControl, you can pass it in as the third parameter3.

Built-in validator

: For example, to verify some lengths, it cannot be empty and can be used The Validator class has been provided to implement 4.

Custom validator

: The validator provided within the system cannot meet the existing needs. You can use a custom validator to do some personalized calibration. For verification, the custom validator must return the ValidationErrors type or empty

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

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

3. Basic methods and attributes of forms and elements

  • Method
##MethodUse effectUsing setVlue can set the value of the control FormControl, but when using it, all properties of the FormGroup must be assigned values ​​together. It cannot Single assignment, often used to modify load assignments. Using patchValue, you can also set the value of FormControl. You can set the specified FormControl as needed, without setting all of them. Commonly used to update a field valueFormControl is used to reset all the states of the current control. It is used in FormGroup to reset. The content in the form object, for example, the control is set to disabled, control.reset({ value: 'Drew', disabled: true }); marks the form control value as unchanged. This method is mainly used when the form is reset. At this time, its status pristine is true ##markAsDirty()updateValueAndValidity( )setValidators()setValidators([v1,v2,v3])disable()enable()
setValue()
patchValue()
reset ()
markAsPristine()
marks the form FormControl control value as changed. At this time, its status Dirty is true
Recalculate the value and validation status of the FormControl control, etc.
to the form The FormControl control sets the validator. If you set multiple validators, use the array ""
Set the FormControl control to be unavailable. Note that when the FormControl is disabled, the corresponding value of the form's regular value getValue() will be empty. You can use getRawValue() to get the original value object to get the value of the corresponding FormControl.
Enable the FormControl control
  • Attributes

##AttributesUsage Descriptiontouched##untouchedpristinedirtystatusErrors
When the touched value of the FormControl control is true, it means that the control has gained focus, and vice versa.
When untouched is true, it means that the control has not gained focus, and vice versa
Indicates that the form element is pure and has not been operated by the user. You can use the markAsPristine method to set it to true
Indicates that the form element has been operated by the user. You can use the markAsDirty method to set it to true
Get the status of the form FormControl control
Get the error information of the current control

二.实例分析及应用

1. 简单的表单实现

######需求1

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

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

A brief analysis of responsive forms in Angular

分析

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(&#39;required&#39;)">
      员工编号为必填项目
    </ng-container>
  </ng-template>
</form>

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

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

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

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

    this.validateForm = this.fb.group({
      EmployeeID: [&#39;&#39;, [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可用,需要验证开始结束时间合法性,例如开始事件不能超过结束时间

A brief analysis of responsive forms in Angular

分析

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(&#39;beginGtendDate&#39;)">
                  开始时间不能晚于结束时间
                </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(&#39;beginGtendDate&#39;)">
                  开始时间不能晚于结束时间
                </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,然后在模板中根据这个属性来选择是否渲染日式信息

//定义表单对象
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(&#39;aliases&#39;) 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(&#39;StartDate&#39;),
          EndDate: AbstractControl = formArray.controls[index].get(&#39;EndDate&#39;);

        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(&#39;EndDate&#39;).value;
        const StartDate: string =formArray.controls[index].get(&#39;StartDate&#39;).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;
    }
  }

更多编程相关知识,请访问:编程入门!!

The above is the detailed content of A brief analysis of responsive forms in Angular. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.cn. If there is any infringement, please contact admin@php.cn delete