Angular8表单内置API窥探

创建

formControl

实例用于追踪单个表单控件的值和验证状态
<section>
  <div nz-row nzJustify="start" nzGutter="16">
    <div nz-col nzSpan="8">
      <input nz-input type="text" [formControl]="projectName" placeholder="请输入项目名称">
      <span *ngIf="!projectName.valid">missing required!</span>
    </div>
  </div>
</section>
import { Component, OnInit } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'app-create-form',
  templateUrl: './create-form.component.html',
  styleUrls: ['./create-form.component.scss']
})
export class CreateFormComponent implements OnInit {
  projectName: FormControl;
  constructor() { }

  ngOnInit() {
    this.projectName = new FormControl('123');
  }
}

上述输入控件绑定了一个formControl类型的初始值,未添加任何校验规则,使用内置API添加规则、校验等操作。

  projectNameValidate() {
    // 清除已有的验证器
    this.projectName.clearValidators();
    // 添加新验证器
    this.projectName.setValidators([Validators.required]);
    this.projectName.updateValueAndValidity();
  }
 <div nz-col nzSpan="8">
      <button nz-button (click)="projectNameValidate()">验证</button>
    </div>

FormGroup

跟踪一组FormControl实例的值和有效性状态
<section>
  <form nz-form [formGroup]="outlayGroup" (ngSubmit)="outlayGroupSubmit($event)">
    <div nz-row nzGutter="8" nzJustify="start">
      <div nz-col nzFlex="flex" nzSpan="6">
        <nz-form-item>
          <nz-form-label nzSpan="8" nzRequired>团建</nz-form-label>
          <nz-form-control nzSpan="16">
            <input nz-input type="text" formControlName="activity">
          </nz-form-control>
        </nz-form-item>
      </div>
      <div nz-col nzFlex="flex" nzSpan="6">
        <nz-form-item>
          <nz-form-label nzSpan="8" nzRequired>项目调研</nz-form-label>
          <nz-form-control nzSpan="16">
            <input nz-input type="text" formControlName="project">
          </nz-form-control>
        </nz-form-item>
      </div>
    </div>
    <div nz-row>
      <div nz-col nzSpan="8">
        <button nz-button type="submit">提交申请</button>
      </div>
    </div>
  </form>
</section>
outlayFormInit() {
  this.outlayGroup = new FormGroup({
    activity: new FormControl(null, [Validators.required]),
    project: new FormControl(null, [Validators.required])
  });
}

目前表单是响应式类型,默认change状态进行校验,且初始值均为 空值(推荐使用null替代'')
现在需求有所变动,申请单默认团建费用初始值设为1001,但是公司今年效益不是很好,要求团建费用不得大于1000.

outlayFormInit() {
    this.outlayGroup = new FormGroup({
      activity: new FormControl(1001, [Validators.required, Validators.max(1000)]),
      project: new FormControl(null, [Validators.required])
    });
  }

如果我们进入申请页面,直接点击提交申请,虽然不会提交上去,但是也不会有提示或者报红效果,此刻需要我们进行手动校验表单内选项。

 outlayGroupSubmit(event) {
    if (!this.outlayGroup.valid) {
      const controls = this.outlayGroup.controls;
      for (const key in controls) {
        if (controls.hasOwnProperty(key)) {
          const formField = controls[key];
          formField.markAsDirty();
          formField.updateValueAndValidity();
        }
      }
      return;
    }
    // submit to ...
  }

如果要针对其中某个formControl进行操作,要先获取control再进行操作。

this.outlayGroup.get('activity').markAsDirty();
this.outlayGroup.get('activity').updateValueAndValidity();

tip:不要忘记标记为脏值!

FormArray

跟踪一个控件数组的值和有效性状态。
<section>
  <form nz-form [formGroup]="prosecuteForm" (ngSubmit)="prosecuteFormSubmit($event)">
    <div nz-row nzGutter="8" nzJustify="start">
      <div nz-col nzFlex="flex" nzSpan="6">
        <nz-form-item>
          <nz-form-label nzSpan="8" nzRequired>日期</nz-form-label>
          <nz-form-control nzSpan="16">
            <nz-date-picker formControlName="date"></nz-date-picker>
          </nz-form-control>
        </nz-form-item>
      </div>
      <div nz-col nzFlex="flex" nzSpan="6">
        <nz-form-item>
          <nz-form-label nzSpan="8" nzRequired>被告</nz-form-label>
          <nz-form-control nzSpan="16">
            <input nz-input type="text" formControlName="defendant">
          </nz-form-control>
        </nz-form-item>
      </div>
    </div>
    <div>
      <button nz-button (click)="addMembers()">继续添加</button>
      原告团体:
      <div formArrayName="plaintiff">
        <div nz-row nzGutter="8">
          <div nz-col nzSpan="6" *ngFor="let control of plaintiff.controls;let i = index;">
            <nz-form-item>
              <nz-form-control nzSpan="16">
                <input type="text" nz-input [formControlName]="i">
              </nz-form-control>
            </nz-form-item>
          </div>
        </div>
      </div>
    </div>
    <div nz-row>
      <div nz-col nzSpan="8">
        <button nz-button type="submit">提交申请</button>
      </div>
    </div>
  </form>
</section>
get plaintiff() {
    return this.prosecuteForm.get('plaintiff') as FormArray;
  }
  prosecuteFormInit() {
    this.prosecuteForm = new FormGroup({
      date: new FormControl(null, [Validators.required]),
      defendant: new FormControl(null, [Validators.required]),
      plaintiff: new FormArray([
        new FormControl(2, [Validators.required])
      ])
    });
  }
  addMembers() {
    this.plaintiff.push(new FormControl(null,[Validators.required]));
  }
  prosecuteFormSubmit() {
    console.log(this.prosecuteForm);
    this.plaintiff.controls.forEach((control, index) => {
      control.markAsDirty();
      control.updateValueAndValidity();
    });
  }

FormArray 的数组元素为 FormControl实例;从一组List内获取指定Control 可以通过at(index)获取后进行各种操作,这个很重要;添加新的Control需要获取到FromArray实例后push 而不是add方法。

FormBuilder

这是个可注入的服务提供者,用于创建响应式表单控件。

创建一个记录公司信息的简单表单

<section>
  <form nz-form [formGroup]="enterprisesFormData">
    <div nz-row nzGutter="8">
      <div nz-col nzSpan="8">
        <nz-form-item>
          <nz-form-label nzSpan="6">企业名称</nz-form-label>
          <nz-form-control nzSpan="18">
            <input type="text" nz-input formControlName="enterpriseName">
          </nz-form-control>
        </nz-form-item>
      </div>
      <div nz-col nzSpan="8">
        <nz-form-item>
          <nz-form-label nzSpan="6">企业地址</nz-form-label>
          <nz-form-control nzSpan="18">
            <input type="text" nz-input formControlName="enterpriseAddr">
          </nz-form-control>
        </nz-form-item>
      </div>
      <div nz-col nzSpan="8">
        <nz-form-item>
          <nz-form-label nzSpan="6">企业电话</nz-form-label>
          <nz-form-control nzSpan="18">
            <input type="text" nz-input formControlName="tel">
          </nz-form-control>
        </nz-form-item>
      </div>
    </div>
  </form>
</section>
enterprisesInit() {
    this.enterprisesFormData = this.fb.group({
      enterpriseName: ['', Validators.required],
      enterpriseAddr: [''],
      tel: ['']
    });
  }

使用这个服务创建表单比前面的三种方式都更加简单、明了。

更新(值、状态、添加、删除等)

FormControl

this.projectName.setValue('456');
this.projectName.patchValue('789');
this.projectName.setValidators([Validators.required, Validators.minLength(5)]);
this.projectName.markAsDirty();
this.projectName.updateValueAndValidity();

formControl就是一个控件了,无法添加和删除

FormGroup

outlayFormInit() {
    this.outlayGroup = new FormGroup({
      activity: new FormControl(1001, [Validators.required, Validators.max(1000)]),
      project: new FormControl(null, [Validators.required])
    });
    // 更新单个值
    this.outlayGroup.get('activity').setValue(2000);
    // 多个更新
    this.outlayGroup.patchValue({
      project: '11100001',
      activity: 0
    });
    // 添加
    this.outlayGroup.addControl('other', new FormControl(100,[Validators.max(99)]));
    this.outlayGroup.addControl('game', new FormControl(1000));
    // 移除
    this.outlayGroup.removeControl('game');
    // 状态
    this.outlayGroup.get('other').markAsDirty();
    this.outlayGroup.get('other').updateValueAndValidity();
  }

FormArray

get plaintiff() {
    return this.prosecuteForm.get('plaintiff') as FormArray;
  }
  addMembers() {
    this.plaintiff.push(new FormControl(null, [Validators.required]));
  }

添加使用 this.plaintiff.push(new FormControl())
获取使用 this.plaintiff.at(i);
移除使用 this.plaintiff.removeAt(i)
更新具体 Control控件和校验 与 前面的API一致;

FormBuilder

这玩意可以创建任意类型控件,根据创建表单类型去使用对应的API;如果创建的是group类型,则与 FormGroup 一样使用。

校验

单个FormControl

直接使用

control.markAsDirty();
control.updateValueAndValidity();

整个form校验

就是将整个表单进行挨个校验的过程,一般为表单的controls属性值进行迭代校验,动态表单可能需要迭代嵌套的逻辑。

outlayGroupSubmit(event) {
    if (!this.outlayGroup.valid) {
      const controls = this.outlayGroup.controls;
      for (const key in controls) {
        if (controls.hasOwnProperty(key)) {
          const formField = controls[key];
          formField.markAsDirty();
          formField.updateValueAndValidity();
        }
      }
      return;
    }
    // submit to ...
  }

前端成长之踩坑经验
前端在开发中扮演者什么样得角色?可有可无?无足轻重?花里胡哨?应该是杂乱有章

前端路上摸爬滚打;野路子前端debug

106 声望
6 粉丝
0 条评论
推荐阅读
从零搭建 Node.js 企业级 Web 服务器(零):静态服务
过去 5 年,我前后在菜鸟网络和蚂蚁金服做开发工作,一方面支撑业务团队开发各类业务系统,另一方面在自己的技术团队做基础技术建设。期间借着 Node.js 的锋芒做了不少 Web 系统,有的至今生气蓬勃、有的早已夭折...

乌柏木143阅读 12k评论 10

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木60阅读 6k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs39阅读 6.2k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木41阅读 7.2k评论 6

CSS 绘制一只思否猫
欢迎关注我的公众号:前端侦探练习 CSS 有一个比较有趣的方式,就是发挥想象,绘制各式各样的图案,比如来绘制一只思否猫?思否猫,SegmentFault 思否的吉祥物,是一只独一无二、特立独行、热爱自由的(&gt;^ω^&lt...

XboxYan42阅读 2.8k评论 14

封面图
还在用 JS 做节流吗?CSS 也可以防止按钮重复点击
举个例子:一个保存按钮,为了避免重复提交或者服务器考虑,往往需要对点击行为做一定的限制,比如只允许每300ms提交一次,这时候我想大部分同学都会到网上直接拷贝一段throttle函数,或者直接引用lodash工具库

XboxYan34阅读 2.3k评论 2

封面图
【关于Javascript】--- 正则表达式篇
基础知识一、元字符 {代码...} 二、量词 {代码...} 三、集合 字符类 {代码...} 四、分支 {代码...} 五、边界 开始结束 {代码...} 六、修饰符 {代码...} 七、贪婪模式和非贪婪模式js默认贪婪模式即最大可能的匹配...

Jerry35阅读 2.9k

前端路上摸爬滚打;野路子前端debug

106 声望
6 粉丝
宣传栏