精华内容
下载资源
问答
  • Angular 深入浅出之----响应式表单

    千次阅读 2018-05-22 21:37:27
    this.fb.group({...}) 即可。这样我们的表单在拥有大量的表单项时,构造起来就方便多了。 自定义验证 对于响应式表单来说,构造一个自定义验证器是非常简单的,比如我们上面提到过的的验证 密码 和 重复输入密码...

    首先说明:这是我看到的最好的讲解响应式表单的文章,到今天是第三遍,终于全部弄懂了。感谢原作者。


    响应式表单乍一看还是很像模板驱动型表单的,但响应式表单需要引入一个不同的模块: ReactiveFormsModule 而不是 FormsModule

    import {ReactiveFormsModule} from "@angular/forms";
    @NgModule({
      // 省略其他
        imports: [..., ReactiveFormsModule],
      // 省略其他
    })
    // 省略其他

    与模板驱动型表单的区别

    接下来我们还是利用前面的例子,用响应式表单的要求改写一下:

    <form [formGroup]="user" (ngSubmit)="onSubmit(user)">
      <label>
        <span>电子邮件地址</span>
        <input type="text" formControlName="email" placeholder="请输入您的 email 地址">
      </label>
      <div *ngIf="user.get('email').hasError('required') && user.get('email').touched" class="error">
        email 是必填项
      </div>
      <div *ngIf="user.get('email').hasError('pattern') && user.get('email').touched" class="error">
        email 格式不正确
      </div>
      <div>
        <label>
          <span>密码</span>
          <input type="password" formControlName="password" placeholder="请输入您的密码">
        </label>
        <div *ngIf="user.get('password').hasError('required') && user.get('password').touched" class="error">
          密码是必填项
        </div>
        <label>
          <span>确认密码</span>
          <input type="password" formControlName="repeat" placeholder="请再次输入密码">
        </label>   
        <div *ngIf="user.get('repeat').hasError('required') && user.get('repeat').touched" class="error">
          确认密码是必填项
        </div>
        <div *ngIf="user.hasError('validateEqual') && user.get('repeat').touched" class="error">
          确认密码和密码不一致
        </div>
      </div>
      <div formGroupName="address">
        <label>
          <span>省份</span>
          <select formControlName="province">
            <option value="">请选择省份</option>
            <option [value]="province" *ngFor="let province of provinces">{{province}}</option>
          </select>
        </label>
        <label>
          <span>城市</span>
          <select formControlName="city">
            <option value="">请选择城市</option>
            <option [value]="city" *ngFor="let city of (cities$ | async)">{{city}}</option>
          </select>
        </label>
        <label>
          <span>区县</span>
          <select formControlName="area">
            <option value="">请选择区县</option>
            <option [value]="area" *ngFor="let area of (areas$ | async)">{{area}}</option>
          </select>
        </label>
        <label>
          <span>地址</span>
          <input type="text" formControlName="addr">
        </label>
      </div>
      <button type="submit" [disabled]="user.invalid">注册</button>
    </form>

    这段代码和模板驱动型表单的那段看起来差不多,但是有几个区别:

    • 表单多了一个指令 [formGroup]="user"
    • 去掉了对表单的引用 #f="ngForm"
    • 每个控件多了一个 formControlName
    • 但同时每个控件也去掉了验证条件,比如 requiredminlength
    • 在地址分组中用 formGroupName="address" 替代了 ngModelGroup="address"

    模板上的区别大概就这样了,接下来我们来看看组件的区别:

    import { Component, OnInit } from '@angular/core';
    import { FormControl, FormGroup, Validators } from "@angular/forms";
    @Component({
      selector: 'app-model-driven',
      templateUrl: './model-driven.component.html',
      styleUrls: ['./model-driven.component.css']
    })
    export class ModelDrivenComponent implements OnInit {
    
      user: FormGroup;
    
      ngOnInit() {
        // 初始化表单
        this.user = new FormGroup({
          email: new FormControl('', [Validators.required, Validators.pattern(/([a-zA-Z0-9]+[_|_|.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|_|.]?)*[a-zA-Z0-9]+.[a-zA-Z]{2,4}/)]),
          password: new FormControl('', [Validators.required]),
          repeat: new FormControl('', [Validators.required]),
          address: new FormGroup({
            province: new FormControl(''),
            city: new FormControl(''),
            area: new FormControl(''),
            addr: new FormControl('')
          })
        });
      }
    
      onSubmit({value, valid}){
        if(!valid) return;
        console.log(JSON.stringify(value));
      }
    }

    从上面的代码中我们可以看到,这里的表单( FormGroup )是由一系列的表单控件( FormControl )构成的。其实 FormGroup 的构造函数接受的是三个参数: controls(表单控件『数组』,其实不是数组,是一个类似字典的对象) 、 validator(验证器) 和 asyncValidator(异步验证器) ,其中只有 controls 数组是必须的参数,后两个都是可选参数。

    // FormGroup 的构造函数
    constructor(
      controls: {
        [key: string]: AbstractControl;
      }, 
      validator?: ValidatorFn, 
      asyncValidator?: AsyncValidatorFn
    )

    我们上面的代码中就没有使用验证器和异步验证器的可选参数,而且注意到我们提供 controls 的方式是,一个 key 对应一个 FormControl 。比如下面的 keypassword,对应的值是 new FormControl('', [Validators.required]) 。这个 key 对应的就是模板中的 formControlName 的值,我们模板代码中设置了 formControlName="password" ,而表单控件会根据这个 password 的控件名来跟踪实际的渲染出的表单页面上的控件(比如 <input formcontrolname="password">)的值和验证状态。

    password: new FormControl('', [Validators.required])

    那么可以看出,这个表单控件的构造函数同样也接受三个可选参数,分别是:控件初始值( formState )、控件验证器或验证器数组( validator )和控件异步验证器或异步验证器数组( asyncValidator )。上面的那行代码中,初始值为空字符串,验证器是『必选』,而异步验证器我们没有提供。

    // FormControl 的构造函数
    constructor(
      formState?: any, // 控件初始值
      validator?: ValidatorFn | ValidatorFn[], // 控件验证器或验证器数组
      asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] // 控件异步验证器或异步验证器数组
    )

    由此可以看出,响应式表单区别于模板驱动型表单的的主要特点在于:是由组件类去创建、维护和跟踪表单的变化,而不是依赖模板。

    那么我们是否在响应式表单中还可以使用 ngModel 呢?当然可以,但这样的话表单的值会在两个不同的位置存储了: ngModel 绑定的对象和 FormGroup ,这个在设计上我们一般是要避免的,也就是说尽管可以这么做,但我们不建议这么做。

    FormBuilder 快速构建表单

    上面的表单构造起来虽然也不算太麻烦,但是在表单项目逐渐多起来之后还是一个挺麻烦的工作,所以 Angular 提供了一种快捷构造表单的方式 -- 使用 FormBuilder。

    import { Component, OnInit } from '@angular/core';
    import { FormBuilder, FormGroup, Validators } from "@angular/forms";
    @Component({
      selector: 'app-model-driven',
      templateUrl: './model-driven.component.html',
      styleUrls: ['./model-driven.component.css']
    })
    export class ModelDrivenComponent implements OnInit {
    
      user: FormGroup;
    
      constructor(private fb: FormBuilder) {
      }
    
      ngOnInit() {
        // 初始化表单
        this.user = this.fb.group({
          email: ['', [Validators.required, Validators.email]],
          password: ['', Validators.required],
          repeat: ['', Validators.required],
          address: this.fb.group({
            province: [],
            city: [],
            area: [],
            addr: []
          })
        });
      }
      // 省略其他部分
    }

    使用 FormBuilder 我们可以无需显式声明 FormControl 或 FormGroup 。 FormBuilder 提供三种类型的快速构造: control , grouparray ,分别对应 FormControl, FormGroup 和 FormArray。 我们在表单中最常见的一种是通过 group 来初始化整个表单。上面的例子中,我们可以看到 group 接受一个字典对象作为参数,这个字典中的 key 就是这个 FormGroup 中 FormControl 的名字,值是一个数组,数组中的第一个值是控件的初始值,第二个是同步验证器的数组,第三个是异步验证器数组(第三个并未出现在我们的例子中)。这其实已经在隐性的使用 FormBuilder.control 了,可以参看下面的 FormBuilder 中的 control 函数定义,其实 FormBuilder 利用我们给出的值构造了相对应的 control

    control(
        formState: Object, 
        validator?: ValidatorFn | ValidatorFn[], 
        asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[]
        ): FormControl;

    此外还值得注意的一点是 address 的处理,我们可以清晰的看到 FormBuilder 支持嵌套,遇到 FormGroup 时仅仅需要再次使用 this.fb.group({...}) 即可。这样我们的表单在拥有大量的表单项时,构造起来就方便多了。

    自定义验证

    对于响应式表单来说,构造一个自定义验证器是非常简单的,比如我们上面提到过的的验证 密码重复输入密码 是否相同的需求,我们在响应式表单中来试一下。

      validateEqual(passwordKey: string, confirmPasswordKey: string): ValidatorFn {
        return (group: FormGroup): {[key: string]: any} => {
          const password = group.controls[passwordKey];
          const confirmPassword = group.controls[confirmPasswordKey];
          if (password.value !== confirmPassword.value) {
            return { validateEqual: true };
          }
          return null;
        }
      }

    这个函数的逻辑比较简单:我们接受两个字符串(是 FormControl 的名字),然后返回一个 ValidatorFn。但是这个函数里面就奇奇怪怪的,
    比如 (group: FormGroup): {[key: string]: any} => {...} 是什么意思啊?还有,这个 ValidatorFn 是什么鬼?我们来看一下定义:

    export interface ValidatorFn {
        (c: AbstractControl): ValidationErrors | null;
    }

    这样就清楚了, ValidatorFn 是一个对象定义,这个对象中有一个方法,此方法接受一个 AbstractControl 类型的参数(其实也就是我们的 FormControl,而 AbstractControl 为其父类),而这个方法还要返回 ValidationErrors ,这个 ValidationErrors 的定义如下:

    export declare type ValidationErrors = {
        [key: string]: any;
    };

    回过头来再看我们的这句 (group: FormGroup): {[key: string]: any} => {...},大家就应该明白为什么这么写了,我们其实就是在返回一个 ValidatorFn 类型的对象。只不过我们利用 javascript/typescript 对象展开的特性把 ValidationErrors 写成了 {[key: string]: any}

    弄清楚这个函数的逻辑后,我们怎么使用呢?非常简单,先看代码:

        this.user = this.fb.group({
          email: ['', [Validators.required, Validators.email]],
          password: ['', Validators.required],
          repeat: ['', Validators.required],
          address: this.fb.group({
            province: [],
            city: [],
            area: [],
            addr: []
          })
        }, {validator: this.validateEqual('password', 'repeat')});

    和最初的代码相比,多了一个参数,那就是 {validator: this.validateEqual('password', 'repeat')}。FormBuilder 的 group 函数接受两个参数,第一个就是那串长长的,我们叫它 controlsConfig,用于表单控件的构造,以及每个表单控件的验证器。但是如果一个验证器是要计算多个 field 的话,我们可以把它作为整个 group 的验证器。所以 FormBuilder 的 group 函数还接收第二个参数,这个参数中可以提供同步验证器或异步验证器。同样还是一个字典对象,是同步验证器的话,key 写成 validator,异步的话写成 asyncValidator

    现在我们可以保存代码,启动 ng serve 到浏览器中看一下结果了:

    响应式表单对于多值验证的处理

    FormArray 有什么用?

    我们在购物网站经常遇到需要维护多个地址,因为我们有些商品希望送到公司,有些需要送到家里,还有些给父母采购的需要送到父母那里。这就是一个典型的 FormArray 可以派上用场的场景。所有的这些地址的结构都是一样的,有省、市、区县和街道地址,那么对于处理这样的场景,我们来看看在响应式表单中怎么做。

    首先,我们需要把 HTML 模板改造一下,现在的地址是多项了,所以我们需要在原来的地址部分外面再套一层,并且声明成 formArrayName="addrs"。 FormArray 顾名思义是一个数组,所以我们要对这个控件数组做一个循环,然后让每个数组元素是 FormGroup,只不过这次我们的 [formGroupName]="i" 是让 formGroupName 等于该数组元素的索引。

    <div formArrayName="addrs">
        <button (click)="addAddr()">Add</button>
        <div *ngFor="let item of user.controls['addrs'].controls; let i = index;">
          <div [formGroupName]="i">
            <label>
              <span>省份</span>
              <select formControlName="province">
                <option value="">请选择省份</option>
                <option [value]="province" *ngFor="let province of provinces">{{province}}</option>
              </select>
            </label>
            <label>
              <span>城市</span>
              <select formControlName="city">
                <option value="">请选择城市</option>
                <option [value]="city" *ngFor="let city of (cities$ | async)">{{city}}</option>
              </select>
            </label>
            <label>
              <span>区县</span>
              <select formControlName="area">
                <option value="">请选择区县</option>
                <option [value]="area" *ngFor="let area of (areas$ | async)">{{area}}</option>
              </select>
            </label>
            <label>
              <span>地址</span>
              <input type="text" formControlName="street">
            </label>
          </div>
        </div>
      </div>

    改造好模板后,我们需要在类文件中也做对应处理,去掉原来的 address: this.fb.group({...}),换成 addrs: this.fb.array([]) 。而

    this.user = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', Validators.required],
      repeat: ['', Validators.required],
      addrs: this.fb.array([])
    }, {validator: this.validateEqual('password', 'repeat')});

    但这样我们是看不到也增加不了新的地址的,因为我们还没有处理添加的逻辑呢,下面我们就添加一下:其实就是建立一个新的 FormGroup,然后加入 FormArray 数组中。

      addAddr(): void {
        (<FormArray>this.user.controls['addrs']).push(this.createAddrItem());
      }
    
      private createAddrItem(): FormGroup {
        return this.fb.group({
          province: [],
          city: [],
          area: [],
          street: []
        })
      }

    到这里我们的结构就建好了,保存后,到浏览器中去试试添加多个地址吧!

    FormArray 处理结构相同的多组表单项

    响应式表单的优势

    首先是可测试能力。模板驱动型表单进行单元测试是比较困难的,因为验证逻辑是写在模板中的。但验证器的逻辑单元测试对于响应式表单来说就非常简单了,因为你的验证器无非就是一个函数而已。

    当然除了这个优点,我们对表单可以有完全的掌控:从初始化表单控件的值、更新和获取表单值的变化到表单的验证和提交,这一系列的流程都在程序逻辑控制之下。

    而且更重要的是,我们可以使用函数响应式编程的风格来处理各种表单操作,因为响应式表单提供了一系列支持 Observable 的接口 API 。那么这又能说明什么呢?有什么用呢?

    首先是无论表单本身还是控件都可以看成是一系列的基于时间维度的数据流了,这个数据流可以被多个观察者订阅和处理,由于 valueChanges 本身是个 Observable,所以我们就可以利用 RxJS 提供的丰富的操作符,将一个对数据验证、处理等的完整逻辑清晰的表达出来。当然现在我们不会对 RxJS 做深入的讨论,后面有专门针对 RxJS 进行讲解的章节。

    this.form.valueChanges
            .filter((value) => this.user.valid)
            .subscribe((value) => {
               console.log("现在时刻表单的值为 ",JSON.stringify(value));
            });

    上面的例子中,我们取得表单值的变化,然后过滤掉表单存在非法值的情况,然后输出表单的值。这只是非常简单的一个 Rx 应用,随着逻辑复杂度的增加,我们后面会见证 Rx 卓越的处理能力。


    作者:接灰的电子产品
    链接:https://juejin.im/post/593fa043a0bb9f006b684df4
    来源:掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    展开全文
  • Angular:嵌套表单的验证

    千次阅读 2018-12-04 18:20:39
    Angular6嵌套表单的验证嵌套表单ngOnInit()中的表单验证初始化表单的方法表单验证的方法保存方法中怎么取到每个字段页面中表单的写法效果 嵌套表单 ngOnInit()中的表单...this.validateForm = this.fb.group({ bi...

    嵌套表单

    ngOnInit()中的表单验证

    以下文代码为例,表单validateForm中包含三个子表单,每个表单中有一定的字段值,初始化表单验证:

    ngOnInit() {
        this.validateForm = this.fb.group({
          bizMeruser: this.fb.group({                                  // 登录信息
            username: ['', [Validators.required]],                     // 管理登录账号
          }),
          bizMerchantBasic: this.fb.group({                            // 商户/公司基础信息
            merchantFullName: ['', [Validators.required, Validators.maxLength(32)]],             // 全称
            merchantShortName: ['', [Validators.required, Validators.maxLength(32)]],            // 简称
            ........
         }),
          bizMerchantFreight: this.fb.group({                  // 供应商运费
            merchantFreight: ['', [Validators.required, Validators.maxLength(32)]],      // 运费信息
           ........
          })
        });
        /* 加载页面字段值 */
        if (!this.isAdd) {                                    // 查看或修改页面
          this.commercialProvider.getDataById(this.id).subscribe(res => {
            this.initFormData(res);
          });
        } else {
          this.isSpinning = false;
        }
      }
    

    从上面的表单结构可以看出来,嵌套表单的原理很简单,也就是把子表单作为父表单的一个元素而已,初始化用下面的格式即可:
    父表单名= this.fb.group({
    子表单名1: this.fb.group({

    }),
    子表单名2: this.fb.group({

    }),
    })

    初始化表单的方法

     /**
       * 初始化form
       * @param data
       */
      initFormData(data) {
        this.validateForm.controls['bizMeruser'].setValue({
          username: _.get(data.bizMeruser, 'username', '')
        });
        this.validateForm.controls['bizMerchantBasic'].setValue({
          merchantFullName: _.get(data.bizMerchantBasic, 'merchantFullName', ''),
          merchantShortName: _.get(data.bizMerchantBasic, 'merchantShortName', ''),
          .......
        });
        this.validateForm.controls['bizMerchantFreight'].setValue({
          merchantFreight: _.get(data.bizMerchantFreight, 'merchantFreight', ''),
          .......
        });
        this.isSpinning = false;
      }
    
    

    使用 _.get(data.子表单名, ‘子表单中字段名’, ‘’) 从后端返回的data中取出每个表单中的字段,在页面上显示出来

    表单验证的方法

    getFormGroupControl(group, name) {
        return this.validateForm.get(group).get(name);
      }
    
    

    保存方法中怎么取到每个字段

    嵌套表单的取字段的思路就是先遍历表单,再遍历每个表单内部的字段:

    doSave(event, value) {
        event.preventDefault();
        const controls = this.validateForm.controls;
        for (const key in controls) {
          if (controls.hasOwnProperty(key)) {
            const group = this.validateForm.get(key) as FormGroup;
            const items = group.controls;
            for (const key2 in items) {             // 先遍历表单,再遍历每个表单内部的字段
              if (items.hasOwnProperty(key2)) {
                group.controls[key2].markAsDirty();
                group.controls[key2].updateValueAndValidity();
              }
            }
          }
        }
        if (!this.validateForm.invalid) {
          console.log('取到字段了!');
                 .........
         } else {
           console.log('没有取到字段');
         }
    
    

    页面中表单的写法

     <form [formGroup]="validateForm">
              <!-- 三个form放在validation中一起验证 -->
    
              <div formGroupName="bizMeruser">
                <!-- 第一张表 -->
                       <!-- 登录信息-->
                <nz-card nzTitle="登录信息">
                  <div nz-row>
                    <!-- 登录账号:审批不通过的用户除登录账号外其他均可修改 -->
                    <div nz-col [nzSpan]="10">
                      <nz-form-item nzFlex>
                        <nz-form-label nz-col [nzRequired]="!isReadOnly" [nzSpan]="6" nzFor="username">管理登录账号</nz-form-label>
                        <nz-form-control nz-col [nzSpan]="14">
                          <input nz-input formControlName="username" readonly="readonly">
                        </nz-form-control>
                      </nz-form-item>
                    </div>
                  </div>
                </nz-card>
              </div>
         </div>
         <!-- 第二张表 -->
         .........
         <!-- 第三张表 -->
         .........
         </form>
    

    效果

    在这里插入图片描述
    可以看到点击保存的时候,页面对所有必填的字段进行了验证

    展开全文
  • this.editModel = fb.group({ params: this.fb.array([ new FormGroup({ x: new FormControl(null), y: new FormControl(null) }) ]), }); // this.i...
        this.editModel = fb.group({
          params: this.fb.array([
            new FormGroup({
              x: new FormControl(null),
              y: new FormControl(null)
            })
          ]),
        });
    
    
      // this.index = 0;
       // this.dynamicArr = [];
    
      addForm (flag) {
        this.index += 1;
        this.dynamicArr.push({
          id: this.index,
          value: `obj${this.index}`
        });
        (this.editModel.controls[flag] as FormArray).push(new FormGroup({x: new                 FormControl(null), y: new FormControl(null)}));
      }
    
      minusForm(index, flag) {
        this.dynamicArr.splice(index, 1);
        (this.editModel.get(flag) as FormArray).removeAt(index);
      }
    
    
    
    

     

          <div class="ui-grid-row">
            <div class="ui-grid-col-2"></div>
            <div class="ui-grid-col-8">
              <label>参数:</label>
              <i class="fa fa-plus-square" aria-hidden="true" style="cursor: pointer;" (click)="addForm('params')"></i>
            </div>
          </div>
          <div formArrayName="params" class="ui-grid-row margin-bottom-1vw" *ngFor="let item of editModel.get('params')['controls']; let i = index;">
            <div class="ui-grid-col-2"></div>
            <div class="ui-grid-col-8" [formGroupName]="i">
              <label></label>
              <p-dropdown [options]="parameterOption" formControlName="x" filter="true" [style]="{'width': '130px'}"></p-dropdown>
              <span>-</span>
              <input type="text" formControlName="y" pInputText [style.width.%]="30"/>
              <i class="fa fa-minus-square" aria-hidden="true" style="cursor: pointer;" (click)="minusForm(i, 'params')"></i>
            </div>
          </div>

    展开全文
  • 有时候,在FormArray中,不仅仅是一个控件,有可能是多个,这个时候,这个FormArray中的元素就是一个FormGroup,并且这个FormGroup中的某一个实例又是一个FormArray,这样FormGroup和FormArray就会产生...

    由于在工作中需要做多层的表单提交校验功能,但一直没有好的方法 ,查找了一下网上资料刚好有解决的办法 , 所以借鉴了一下并收藏下来 , 做以后再次使用

    有时候,在FormArray中,不仅仅是一个控件,有可能是多个,这个时候,这个FormArray中的元素就是一个FormGroup,并且这个FormGroup中的某一个实例又是一个FormArray,这样FormGroup和FormArray就会产生深层次的嵌套
    在这里插入图片描述
    有时会遇到这样的场景,模拟一个工作流。
    在这里插入图片描述
    现在我们来开始学习它

    创建一个表单嵌套组件

     ng g c form-nested
    CREATE src/app/form-nested/form-nested.component.less (0 bytes)
    CREATE src/app/form-nested/form-nested.component.html (30 bytes)
    CREATE src/app/form-nested/form-nested.component.spec.ts (657 bytes)
    CREATE src/app/form-nested/form-nested.component.ts (289 bytes)
    UPDATE src/app/app.module.ts (1627 bytes)
    
    

    修改模板文件

    <nz-divider [nzText]="'表单嵌套'"></nz-divider>
    

    修改根组件,将这个组件显示出来

    <!--<router-outlet></router-outlet>-->
    
    <!--动画-->
    <!--<app-animation-demo></app-animation-demo>-->
    <!--<app-complex-animations></app-complex-animations>-->
    
    <!--可编辑的table的Demo-->
    <!--<app-edit-table></app-edit-table>-->
    
    <!--响应式表单 FormGroup FormArray-->
    <!--<app-user-info></app-user-info>-->
    
    <!--多层级的表单嵌套-->
    <app-form-nested></app-form-nested>
    

    保存运行
    在这里插入图片描述
    先构造出最上层(工作流)的实例
    修改类文件

    import {Component, OnInit} from '@angular/core';
    import {FormBuilder, FormGroup, Validators} from '@angular/forms';
    
    @Component({
      selector: 'app-form-nested',
      templateUrl: './form-nested.component.html',
      styleUrls: ['./form-nested.component.less']
    })
    export class FormNestedComponent implements OnInit {
      public validateForm: FormGroup;
    
      constructor(private fb: FormBuilder) {
        this.validateForm = this.fb.group({
          workFlowName: [null, [Validators.required]],
          workFlowType: [null, [Validators.required]],
          workFlowContent: this.fb.array([
            this.fb.control(null)
          ])
        });
      }
    
      ngOnInit() {
      }
    
    }
    
    

    修改模板文件

    <nz-divider [nzText]="'表单嵌套'"></nz-divider>
    
    <form [formGroup]="validateForm">
    
      <nz-form-item>
        <nz-form-label nzSpan="3" nz-col>
          工作流名称
        </nz-form-label>
        <nz-form-control nzSpan="7" nz-col>
          <input nz-input type="text" placeholder="请输入工作流名称" formControlName="workFlowName">
        </nz-form-control>
    
        <nz-form-label nzSpan="3" nzOffset="1" nz-col>
          工作流类型
        </nz-form-label>
        <nz-form-control nzSpan="7" nz-col>
          <input nz-input type="text" placeholder="请输入工作流类型" formControlName="workFlowType">
        </nz-form-control>
      </nz-form-item>
    
      <nz-form-item>
        <nz-form-label nzSpan="3" nz-col>
          工作流内容
        </nz-form-label>
        <nz-form-control nzSpan="14">
          <nz-row formArrayName="workFlowContent"
                  *ngFor="let content of validateForm.controls['workFlowContent'].controls;
                  let workFlowIndex = index">
            <nz-form-control nz-col nzSpan="12">
              <input nz-input [formControlName]="workFlowIndex" type="text">
            </nz-form-control>
    
          </nz-row>
        </nz-form-control>
      </nz-form-item>
    
    </form>
    
    <nz-form-item>
      <nz-form-label nzSpan="3" nz-col>
        表单的值
      </nz-form-label>
      <nz-form-control nzSpan="21" nz-col>
        {{validateForm.value | json}}
      </nz-form-control>
    </nz-form-item>
    
    
    

    保存运行以后,发现最上层(工作流)的实例已经实现:
    在这里插入图片描述
    构造出工作流内容(workFlowContent)中的的实例
    将validateForm中的workFlowContent实例中的this.fb.control(null),转换成this.fb.group({})。
    在这个this.fb.group({})中添加workFlowContent(FormGroup的实例).

    1. 修改workFlowContent(FormGroup的实例):
    workFlowContent: this.fb.array([
            this.fb.group({
              stageName: [null, [Validators.required]],
              stageType: [null, [Validators.required]],
              stageContent: this.fb.array([
                this.fb.control(null)
              ]),
            })
          ])
    
    

    现在以:stageName(FormControl)、stageType(FormControl)、stageContent(FormArray)这三个为一个FormGroup作为workFlowContent的其中一个实例。

    2.修改模板文件
    注意,在这里有两种写法
    第一种:将stageName(FormControl)、stageType(FormControl)、stageContent(FormArray)这些组成的 FormGroup 的 FormGroupName用一个div表示,将stageName(FormControl)、stageType(FormControl)、stageContent(FormArray)包在这个div中。

    <nz-form-control nzSpan="18">
          <div class="workFlowContent" formArrayName="workFlowContent"
               *ngFor="let content of validateForm.controls['workFlowContent'].controls;
                  let workflowIndex = index">
            <div [formGroupName]="workflowIndex.toString()">
              <nz-row>
                <nz-form-label nzSpan="3" nz-col>
                  stage{{workflowIndex + 1}}
                </nz-form-label>
              </nz-row>
              <nz-row>
                <nz-form-label nzSpan="3" nz-col>stageName</nz-form-label>
                <nz-form-control nz-col nzSpan="7">
                  <input nz-input type="text" formControlName="stageName">
                </nz-form-control>
                <nz-form-label nzOffset="1" nzSpan="3" nz-col>stageType</nz-form-label>
                <nz-form-control nz-col nzSpan="7">
                  <input nz-input type="text" formControlName="stageType">
                </nz-form-control>
              </nz-row>
    
              <nz-row>
                <nz-form-label nzSpan="3" nz-col>stageContent</nz-form-label>
                <nz-form-control nz-col nzSpan="18">
                  <nz-row formArrayName="stageContent"
                          *ngFor="let stage of content.get('stageContent').controls;let stageIndex = index">
                    <input nz-input [formControlName]="stageIndex.toString()">
                  </nz-row>
                </nz-form-control>
              </nz-row>
            </div>
          </div>
        </nz-form-control>
    
    

    第二种:将stageName(FormControl)、stageType(FormControl)、stageContent(FormArray)这些组成的 FormGroup 的信息分别写在对应的FormControl的外面。

    <nz-form-control nzSpan="18">
          <div class="workFlowContent" formArrayName="workFlowContent"
               *ngFor="let content of validateForm.controls['workFlowContent'].controls;
                  let workflowIndex = index">
            <nz-row>
              <nz-form-label nzSpan="3" nz-col>
                stage{{workflowIndex + 1}}
              </nz-form-label>
            </nz-row>
            <nz-row>
              <nz-form-label nzSpan="3" nz-col>stageName</nz-form-label>
              <nz-form-control nz-col nzSpan="7" [formGroupName]="workflowIndex.toString()">
                <input nz-input type="text" formControlName="stageName">
              </nz-form-control>
              <nz-form-label nzOffset="1" nzSpan="3" nz-col>stageType
              </nz-form-label>
              <nz-form-control nz-col nzSpan="7" [formGroupName]="workflowIndex.toString()">
                <input nz-input type="text" formControlName="stageType">
              </nz-form-control>
            </nz-row>
    
            <nz-row>
              <nz-form-label nzSpan="3" nz-col>stageContent</nz-form-label>
              <nz-form-control nz-col nzSpan="18" [formGroupName]="workflowIndex.toString()">
                <nz-row formArrayName="stageContent"
                        *ngFor="let stage of content.get('stageContent').controls;let stageIndex = index">
                  <input nz-input [formControlName]="stageIndex.toString()">
                </nz-row>
              </nz-form-control>
            </nz-row>
          </div>
        </nz-form-control>
    
    

    增加一点点样式,以突出显示stage信息

    .workFlowContent {
      background: #e6f7ff;
      border: 1px solid #91d5ff;
      padding: 10px;
      border-radius: 4px;
    }
    
    

    保存运行以后,就会发现,原来的workFlowContent数组中的元素由一个单纯的’string’ 类型办成了 ‘object’ 类型,并且这个object中的key都是FormGroup中的每一个实例。
    在这里插入图片描述
    3.添加两个按钮,使FormArray:workFlowContent 可以动态的增减。
    为了方便,将 workFlowContent 使用get 将其装换成FormArray;

    import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';
    
      get workFlowContent(): FormArray {
        return this.validateForm.get('workFlowContent') as FormArray;
      }
    

    然后修改模板文件,使用属性workFlowContent;
    将原来的

     <div class="workFlowContent" formArrayName="workFlowContent"
               *ngFor="let content of validateForm.controls['workFlowContent'].controls;
                  let workflowIndex = index">
                  ...
                  ...
    </div>
    
    

    修改为:

    <div class="workFlowContent" formArrayName="workFlowContent"
               *ngFor="let content of workFlowContent.controls;
                  let workflowIndex = index">
                  ...
                  ...
    </div>
    
    

    a. 添加 ‘增加stage’ 的按钮,并绑定一个点击事件

    模板文件:

    <button nz-button (click)="addStage()">增加stage</button>
    

    类文件:

    public addStage(): void {
        this.workFlowContent.push(
          this.fb.group({
            stageName: [null, [Validators.required]],
            stageType: [null, [Validators.required]],
            stageContent: this.fb.array([
              this.fb.control(null)
            ]),
          })
        );
      }
    
    

    保存运行:在这里插入图片描述
    b. 添加 ‘删除’ 的按钮,并绑定一个点击事件

    模板文件:

    <nz-row>
        <nz-form-label nzSpan="3" nz-col>
                stage{{workflowIndex + 1}}
        </nz-form-label>
        <nz-form-control nzOffset="15" nzSpan="3" nz-col>
            <button nz-button (click)="removeStage(workflowIndex)">
                  删除
            </button>
        </nz-form-control>
    </nz-row>
    
    

    类文件:

    public removeStage(workflowIndex: number): void {
        this.workFlowContent.removeAt(workflowIndex);
      }
    

    保存运行:
    在这里插入图片描述
    构造出stage内容(stageContent)中的的实例
    将 workFlowContent 中的每一个 stageContent 实例中的this.fb.control(null),转换成this.fb.group({})。
    在这个this.fb.group({})中添加workFlowContent(FormGroup的实例).

    1.修改workFlowContent(FormGroup的实例):

    stageContent: this.fb.array([
                this.fb.group({
                  stepName: [null, [Validators.required]],
                  stepType: [null, [Validators.required]],
                  stepContent: this.fb.array([
                    this.fb.control(null)
                  ])
                })
              ]),
    

    现在以:stepName(FormControl)、stepType(FormControl)、stepContent(FormArray)这三个为一个FormGroup作为 stageContent 的其中一个实例。

    由于stageContent是workFlowContent中的实例,所以在修了初始化的部分,还需要修改增加按钮的点击事件

    public addWorkflow(): void {
        this.workFlowContent.push(
          this.fb.group({
            stageName: [null, [Validators.required]],
            stageType: [null, [Validators.required]],
            stageContent: this.fb.array([
              this.fb.group({
                stepName: [null, [Validators.required]],
                stepType: [null, [Validators.required]],
                stepContent: this.fb.array([
                  this.fb.control(null)
                ])
              })
            ]),
          })
        );
      }
    
    

    2.修改模板文件

     <nz-row>
              <nz-row>
                <nz-form-label nzSpan="3" nz-col>
                  stageContent
                </nz-form-label>
              </nz-row>
              <nz-row>
                <nz-form-control nzOffset="3" nzSpan="18" [formGroupName]="workflowIndex.toString()">
                  <div formArrayName="stageContent"
                       class="stageContent"
                       *ngFor="let stage of content.get('stageContent').controls;let stageIndex = index">
                    <div [formGroupName]="stageIndex.toString()">
                      <nz-row>
                        <nz-form-label nzSpan="3" nz-col>step{{stageIndex + 1}}</nz-form-label>
                      </nz-row>
                      <nz-row>
                        <nz-form-label nzSpan="3" nz-col>stepName</nz-form-label>
                        <nz-form-control nzSpan="7" nz-col>
                          <input nz-input type="text" formControlName="stepName" placeholder="请输入stepName">
                        </nz-form-control>
    
                        <nz-form-label nzOffset="1" nzSpan="3" nz-col>stepType</nz-form-label>
                        <nz-form-control nzSpan="7" nz-col>
                          <input nz-input type="text" formControlName="stepType" placeholder="请输入stepType">
                        </nz-form-control>
                      </nz-row>
                      <nz-row>
                        <nz-form-label nzSpan="3" nz-col>stepContent</nz-form-label>
                        <nz-form-control nzSpan="7" nz-col formArrayName="stepContent">
                          <nz-row *ngFor="let step of stage.get('stepContent').controls;let stepIndex = index">
                            <input nz-input type="text"
                                   [formControlName]="stepIndex.toString()"
                                   placeholder="请输入stepContent">
                          </nz-row>
                        </nz-form-control>
                      </nz-row>
                    </div>
                  </div>
                </nz-form-control>
              </nz-row>
    
            </nz-row>
    
    

    为了方便区别,在step增加一些样式:

    .stageContent {
      background: #fffbe6;
      border: 1px solid #ffe58f;
      padding: 10px;
      border-radius: 4px;
      margin-bottom: 10px;
    }
    

    在这里插入图片描述
    3.添加两个按钮,使FormArray:stageContent 可以动态的增减。
    a. 添加 ‘增加step’ 按钮

    修改模板文件

     <button nz-button (click)="addStep(workflowIndex)">增加step</button>
    

    在类文件中增加 addStep() 方法

    // add stage
      public addStep(workflowIndex: number): void {
        (this.workFlowContent.at(workflowIndex).get('stageContent') as FormArray).push(
          this.fb.group({
            stepName: [null, [Validators.required]],
            stepType: [null, [Validators.required]],
            stepContent: this.fb.array([
              this.fb.control(null)
            ])
          }));
      }
    
    

    b. 添加 ‘删除step’ 按钮
    修改模板文件

    <nz-row>
      <nz-form-label nzSpan="3" nz-col>step{{stageIndex + 1}}</nz-form-label>
      <nz-form-control nzOffset="15" nzSpan="3">
        <button nz-button (click)="removeStep(workflowIndex,stageIndex)">删除step</button>
      </nz-form-control>
    </nz-row>
    
    

    在类文件中增加 removeStep() 方法

      removeStep(workflowIndex: number, stageIndex: number) {
        (this.workFlowContent.at(workflowIndex).get('stageContent') as FormArray).removeAt(stageIndex);
      }
    
    

    在这种情况下,是将取到workFlowContent 中第 workflowIndex 个 FormGroup,在这个FormGroup的stageContent实例中中找到第stageIndex个stageContent中的FormGroup,然后将其删除。

    为了区别stage和step的删除,将原来删除stage 的按钮修改为’删除stage’,
    保存运行以后
    在这里插入图片描述
    4.添加两个按钮,使FormArray:stepeContent 可以动态的增减。
    在这里使用一种简单的方式,不在关注index,而是直接使用遍历出来的元素

    a. 添加 ‘增加stepContent’ 按钮

    修改模板文件

    <nz-row>
        <button nz-button (click)="addStepContent(stage)">
            添加StepContent
        </button>
    </nz-row>
    
    

    由于StepContent 是 stage 实例中的,所以只要将 stage的 stepContent as FormArray,然后在其中增加

    在类文件中增加 addStepContent() 方法

    // add StepContent
     public addStepContent(stage: FormGroup) {
        (stage.get('stepContent') as FormArray).push(this.fb.control(null));
      }
    
    

    b. 添加 ‘删除step’ 按钮(采用和增加StepContent一样的思路进行删除操作)
    修改模板文件

    <nz-form-control nzSpan="4">
        <button nz-button (click)="removeStepContent(stage,stepIndex)">
            删除StepContent
        </button>
    </nz-form-control>
    
    

    在类文件中增加 removeStep() 方法

     // remove StepContent
      public removeStepContent(stage: FormGroup, stepIndex: number): void {
        (stage.get('stepContent') as FormArray).removeAt(stepIndex);
      }
    
    

    保存运行:

    在这里插入图片描述
    这样深层嵌套的表单就完成了。在此感谢博主…

    展开全文
  • FormGroup使用

    千次阅读 2019-09-06 16:02:18
    目前只是会复制粘贴,还不是很理解。。。 form : FormGroup; //创建 this.form = this.fb.group({ receiver: [null, [Validators.required]], }); //表单验证 if (this.form .invalid...
  • this.myForm = this.fb.group({ emName: ['张珊'], emSex: ['女'], emNumber: ['F0123'] }); //把emNumber修改成F0124 this.myForm.patchValue({emNumber: 'F.124'});
  • 创建FormGroup的实例 import {Component, EventEmitter, OnInit, Output} from '@angular/core'; import {FormGroup,FormBuilder} from "@angular/forms"; @Component({ selector: 'app-login', templateUrl: './...
  • 由于前段时间使用angular框架并在其中使用到了formGroup响应式表单多条件校验功能所以总结一下前段时间的收获及成果 ,方便以后查阅 Angular 支持非常强大的内置表单验证,maxlength()、minlength()、min()、max()、...
  • 接上篇angular踩坑之初期取不到后台数据 在初期化中有表单验证和异步变同步获取后台数据: /** * 初期化 */ ... this.validateForm = this.fb.group( { xxxx: [null, [this.xxxxValidator]],.
  • angular6的响应式表单

    千次阅读 2018-10-09 16:45:13
    profileForm = this.fb.group({ firstName: ['张'], lastName: ['娉'], address: this.fb.group({ street: [''], city: [''], state: [''], zip: [''] }), }); constructor(private fb: FormBuilder) {...
  • 有时候,在FormArray中,不仅仅是一个控件,有可能是多个,这个时候,这个FormArray中的元素就是一个FormGroup,并且这个FormGroup中的某一个实例又是一个FormArray,这样FormGroup和FormArray就会产生深层次的嵌套....
  • 原本的数据格式: mapList: [ { everyCount: 5, name: "NB 物联网表", code: "nb", id: "8cfc89181f664c629e4d7c66fb830e81", rate: 100 }, { everyCount: 6, name: "NB 物联网表",
  • Angular6-表单校验-脏校验

    千次阅读 2019-01-05 20:38:26
    老规矩上图 构造表单 import {Component, OnInit} from '@...import {FormBuilder, FormGroup, Validators} from '@angular/forms'; import {encryptedDES} from '../../tools/nb-fn.utils'; import {NbMess...
  • 一 .创建一个响应式表单 1.引入模块 import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';... this.storyForm = this.fb.group({ sonsName: [null, [Validators.require
  • 以我的项目作为示例,总结一下Angular响应式表单的应用和常用的方法: 1.创建表单 ...import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"...
  • this.myForm = this.fb.group({ roleName:[{ value:null,disabled:true}] }); 2、方法内使用 1)单项使用 this.myForm.get("roleName").disable(); 2)整个表格使用 this.myForm.disable();
  • angular 如何动态修改响应式表单验证数据: 使用angular 响应式表单时候,根据select/radio 选择不同的值,渲染出不同...this.validateForm.removeControl(‘something’); 2.然后使用addControl往组中添加一个控件...
  • componentDidMount(){ ... const {form: { setFieldsValue, getFieldsValue }} = this.props; const { selectedType } = this.state setFieldsValue({ "type1": selectedType }); getFieldsValue(); } ...
  • angular整合editor.md

    2019-10-08 22:02:06
    [formGroup]="markdownForm"> appEditorMd [editormdConfig]="conf" class="editor-md-content" (onEditorChange)="syncModel($event)"> ;"> ` , styleUrls : [ './editor-markdown.component.scss...
  • Angular 响应式表单 FormArray 与 FormGroup 相互嵌套 在类文件中(组件的TS文件): 声明一个Form表单: ... private fb: FormBuild; 声明一个FormBuild的对象 在构造方法中: constructor(pri...
  • 接下来我们将介绍如何利用 AbstractControl 实现 FormGroup 的验证。文章中会涉及 FormGroup 、FormControl 和 FormBuilder 的相关知识,因此建议不了解上述知识的读者,阅读本文前先阅读 Angular 4.x Reactiv...
  • Angular 响应式表单 patchValue和setValue

    千次阅读 2019-01-18 16:57:00
    this.form = this.fb.group({ name: ['', Validators.required], event: this.fb.group({ title: ['', Validators.required], location: ['', Validators.required] }) }); } onSubmit({ value, valid }: ...
  • Angular表单中的FormControl、FormGroup、FormBuilder、FormArray 要使用响应式表单,首先需要在@angular/forms包中导入ReactiveFormsModule,并把它放到ngModule的imports数组中去。 login.module.ts import { ...
  • angular4中关于表单的校验

    万次阅读 2017-09-10 22:55:49
    本章中介绍响应式表单的创建及表单输入值的校验,对于模板表单就略过。...2、在组件的ts文件中使用响应式表单import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms'; export c
  • angular7 FormGroup基本使用

    千次阅读 2019-03-27 08:30:24
    在项目中使用Materail中Stepper组件, 需要进行跳转限制使用到 stepControl 绑定表单, 所以对FormGroup的基本使用进行了整理记录~ 基本使用 需要引入相关模块 // 引入相关模块 import {FormArray, FormBuilder, ...
  • Angular 响应式表单校验

    千次阅读 2018-05-04 18:08:22
    this.formModel=this.fb.group({ nickname:['',[Validators.required,Validators.minLength(6)]], //使用内置校验器 });  //[1]根据模型对象的valid属性判断当前对象是否合法 let nicknameValid=this.formModel...
  • 文章目录响应式表单和模型驱动表单相同与不同之处响应式表单创建案例模板驱动表单创建案例表单底层原理(底层构造块):响应式表单fromGroup与FormBuilder对比表单的验证方法 响应式表单和模型驱动表单相同与不同之...
  • 记录FormGroup和FormArray的使用场景和简单用法,以及一些感悟和想法,部分场景未经验证
  • OpenStack

    千次阅读 多人点赞 2019-10-30 18:10:35
    Enable this on your own risk. Do you want to use unsupported parameters [y|n] [n] : STEP16:网卡名称是否被自动识别为子网+CIDR的格式 Should interface names be automatically recognized ...
  • Angular 4 表单 patchValue and setValue

    千次阅读 2018-06-28 11:45:53
    There are no form controls registered with this group yet. If you're using ngModel, you may want to check next tick (e.g. use setTimeout). ` ); } if (! this .controls[name]) { throw new ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,555
精华内容 5,422
关键字:

this.fb.group