angular表单踩坑之二

一:如何实现表单项blackList的toggle为true时,显示limitNumber和limitDay提交表单时这两个选项为必填项,如果toggle为false,则无需校验这两个字短。也就是非必填项

this.fm=this.fb.group({
    config:this.fb.group({
       blackList:this.fb.group({
            toggle:[null],
            limitNumber:[null,],
            limitDay:[null,]
     }),

    })
  

}) 

实现

1 页面结构

Blocklist
No-show Limits
不能为空
Days of No-show Limits
不能为空
第三步:定义一个函数,接受toggle的值,当为true时,通过setValidators手动设置其为必填项,否则通过clearValidators()函数清除验证规则
 setBlackListControlValidators(value: boolean): void {
    const limitNumControl = this.validateForm.get('config.blackList.limitNumber');
    const limitDayControl = this.validateForm.get('config.blackList.limitDay');
    if (value === true) {
      limitNumControl?.setValidators([Validators.required]);
      limitDayControl?.setValidators([Validators.required]);
    } else {
      limitNumControl?.clearValidators();
      limitDayControl?.clearValidators();
    }
    limitNumControl?.updateValueAndValidity();
    limitDayControl?.updateValueAndValidity();
  }
第四步:调用。我们通过监听toggle值来控制相关逻辑
watchTogles(){
    console.log("999999")
        this.validateForm.get('config.geoFencing.geoFencingToggle')?.valueChanges.subscribe((value) => {
          setTimeout(() => {
            this.setGeoControlValidators(value);
          });
        });
        this.validateForm.get('config.dynamicQRCode.dynamicQRCodeToggle')?.valueChanges.subscribe((value) => {
          setTimeout(() => {
            this.setDynamicControlValidators(value);
          });
        });
        this.validateForm.get('config.blackList.toggle')?.valueChanges.subscribe((value) => {
          setTimeout(() => {
            console.log("subcribe--",value)
            this.setBlackListControlValidators(value);
          });
        });
   }
}

二:angular表单多层嵌套的回显问题

表单结构如下

  this.validateForm=this.fb.group({
      storeId: [null],
      storeName:[null],
      status: 0,
      config:this.fb.group({ 
          tableSize:this.fb.group({
              toggle:[null],
              groupSize:this.fb.array([
                this.fb.group({
                  cate:['indoor'],
                  title_en:[''],
                  title_zh:[''],
                  alias_en:[''],
                  alias_zh:[''],
                  tableSize:this.fb.array([]),
                  moreToggle:[null]
                }),                
              ]),
          }),
          geoFencing:this.fb.group({
            geoFencingToggle:[null],
            range:[null],
            latitude:[null],
            longitude:[null]
          }),
          dynamicQRCode:this.fb.group({
            dynamicQRCodeToggle:[null],
            refreshEx:[null]
          }),
          profile:this.fb.group({
            toggle:[null]
          }),
          blackList:this.fb.group({
            toggle:[null],
            limitNumber:[null],
            limitDay:[null]
          }),
          entryToggle:this.fb.group({
            dynamicQRCodeToggle:[null],
            geoFencingToggle:[ null],
          })
      }),
    })

表单的patchValue()只能对一层数据结构有效,而对多层数据结构patchValue()则无效。

正确的办法是对于formarray需要清除之前的数据再进行重新push新的formcontrol,组成新的formarray以后最后进行patch。具体做法如下:

1 通过请求接口得到新的表单项的值

getStoreInfo(id:number){
    this.http.getStoreData({id}).subscribe((res:any)=>{
      console.log("detailres:",res.data)
      if(res.errcode===0){
        this.repatchForm(res.data)
        this.repatchDialogCate(res.data?.config?.tableSize?.groupSize??[])

       )
      }
    })
  }

2 定义一个repatchForm用于处理整个表单字段

 repatchForm(responseData:any){
    let arr2=this.resetAndGetGroupSize(responseData)            
    this.validateForm.patchValue({
      storeId:responseData.storeId,
      status:responseData.status,
      storeName: responseData.storeName,
      config: {
        tableSize: {
          toggle: responseData?.config?.tableSize?.toggle,
          groupSize: arr2
        },
        geoFencing: {
          geoFencingToggle:responseData?.config?.geoFencing?.geoFencingToggle,
          range: responseData?.config?.geoFencing?.range,
          latitude: responseData?.config?.geoFencing?.latitude,
          longitude: responseData?.config.geoFencing?.longitude,
        },
        dynamicQRCode: {
          dynamicQRCodeToggle:responseData?.config?.dynamicQRCode?.dynamicQRCodeToggle,
          refreshEx:responseData?.config?.dynamicQRCode?.refreshEx
        },
        profile:{
          toggle:responseData?.config?.profile?.toggle,
        },
        blackList:{
          toggle:responseData?.config?.blackList?.toggle,
          limitNumber:responseData?.config?.blackList?.limitNumber,
          limitDay:responseData?.config?.blackList?.limitDay,
        }
      }
    });
    console.log("pathvalue---over",this.validateForm)
  }

3 对于最里层的formarray我们需要重新生成一个新的formarry来替换

//处理会显时table列表数据
  resetAndGetGroupSize(resData:any){

    let arr=resData?.config?.tableSize?.groupSize.map((group: any) => {
      return this.fb.group({
        cate: group.cate,
        title_en: group.title_en,
        title_zh: group.title_zh,
        alias_en:group.alias_en,
        alias_zh:group.title_zh,
        tableSize: this.fb.array(group.tableSize.map((table: any) => {
          return this.fb.group({
            size: [table.size,],
            desc_en: [table.desc_en,[Validators.required]],
            desc_zh: [table.desc_zh,[Validators.required]],
            alias_en: [table.alias_en,],
            alias_zh: [table.alias_zh,],
            preTitle: [table.preTitle,],
            maxPerson: [table.maxPerson,[Validators.required]],
            minPerson: [table.minPerson,[Validators.required,]],
          },{validator:this.minValueValidator()});
        })),
        moreToggle:group.moreToggle
      })
    })
    this.groupSize.clear() //接下来几步是关键
    
    arr.forEach((item:FormGroup)=>{
      this.groupSize.push(item)
    })
    let arr2=arr.map((item:FormGroup)=>{
      return item.value
    })
    return arr2
  }

至此完成了数据的回显

三: 如何实现一个表单项中有两个输入框,分别为最大值和最小值,要求最大值的输入值 必须大于最小值的输入值,即时提醒的效果

minPerson不能为空
maxPerson不能为空
{{table.value.showErr}}
最大值必须大于最小值

由于这里的每行的表单项是动态添加的,所以在这里我们在添加的逻辑里做如下验证:

addTable(event:Event){
    event.preventDefault()
    //先找到是indoor还是outdoor的tablsesize
    let curTableSizeArr=this.tableSizeControls(this.actvieTabIndex) as FormArray
    console.log("this.curTableSizeArr",curTableSizeArr)
    if(curTableSizeArr.length>3){
      return
    }else{
        let newdata=this.fb.group({
          size: ["",],
          desc_en:["",[Validators.required]],
          desc_zh: ["",[Validators.required]],
          preTitle:["",[Validators.required]],
          alias_en: ["",],
          alias_zh: [""],
          minPerson: [null,[Validators.required,]],
          maxPerson: [null,[Validators.required,]]
          // minPerson: [null,[Validators.required,]],
          // maxPerson: [null,[Validators.required,]],
        },{validator:this.minValueValidator()})
        newdata.setValidators(this.minValueValidator());
        curTableSizeArr.push(newdata)
        console.log("curTableSizeArr",curTableSizeArr)          
    }
    console.log("this.cateform",this.cateForm)
  
  }

注意这里我们把最大值必须大于最小值的验证的规则放在了fb.group里而不是minperson和maxperson这个formconrol里。其规则定义如下

  minValueValidator():ValidatorFn{
    return (control: AbstractControl): { [key: string]: any } | null => {
      const minValueControl = control.get('minPerson')
      const maxValueControl = control.get('maxPerson')
      console.log("minValue",minValueControl)
      console.log("maxValue",maxValueControl)
      if(minValueControl&&maxValueControl){
        let maxValue=maxValueControl.value
        let minValue=minValueControl.value
        console.log("minValue",minValue)
        console.log("maxValue",maxValue)
        if (minValue&&maxValue&&(maxValue<= minValue)) {
         
           maxValueControl.setErrors({ maxValueInvalid: true });
           console.log("eroor--maxValue

回过来再看下我们的dom结构里,

{{table.value.showErr}}
最大值必须大于最小值

由于minperson和maxperosn的输入框里失去焦点的时候验证的错误提示都是一样的,所以我们把这个错误提示合二为一。

四:关于表单中Validator对象的errors处理。

1 formcontorl对象下的errors对象下有一些常见的属性。

 (1)requried属性。

this.fb.group({
            
            desc_en: [table.desc_en,[Validators.required]],
            desc_zh: [table.desc_zh,[Validators.required]],
          
})

//对应的dom结构:
不能为空

(2)max/min属性

 dynamicQRCode:this.fb.group({
            dynamicQRCodeToggle:[false],
            refreshEx:[null,[Validators.max(20)]]
 }),


//对应的dom的errortip
只最大值错误

(3)常见的正则验证:则在errors上会挂载一个pattern属性

 blackList:this.fb.group({
            toggle:[false],
            limitNumber:[null,[Validators.pattern(/^[1-9]\d*$/)]],
})

//对应dom的errortip

  
只能输入整数

(4)对于自定义验证规则,如果最后验证的函数返回一个属性,那么该属性则会自动挂在errors对象上。

this.fb.group({
            size: [table.size,],
            desc_en: [table.desc_en,[Validators.required]],
            desc_zh: [table.desc_zh,[Validators.required]],
            alias_en: [table.alias_en,Validators.required],
            alias_zh: [table.alias_zh,],
            preTitle: [table.preTitle,],
            maxPerson: [table.maxPerson,[Validators.required]],
            minPerson: [table.minPerson,[Validators.required,]],
          },{validator:this.minValueValidator()});
})

//自定义验证函数,注意其返回值返回的对象maxValueInvalid
  minValueValidator():ValidatorFn{
    return (control: AbstractControl): { [key: string]: any } | null => {
      const minValueControl = control.get('minPerson')
      const maxValueControl = control.get('maxPerson')
      if(minValueControl&&maxValueControl){
        let maxValue=maxValueControl.value
        let minValue=minValueControl.value
        if (minValue&&maxValue&&(maxValue<= minValue)) {
           maxValueControl.setErrors({ maxValueInvalid: true });      
          return { 
            maxValueInvalid: true 
          };
        }
      }
      return null;
    };
  }
//页面dom中的errortip
                                                                                                    

我们在自定义函数中返回一个maxValueInvalid,告诉formcontrol,其errors下被添加了一个maxValueInvalid属性

五:angular表单中button按下回车键时会触发submit

解决办法:

引入,HostListener装饰器。具体如下:

import { HostListener} from '@angular/core';

export class StoreEditComponent implements OnInit{
  
  @HostListener('keydown.enter', ['$event'])
  onEnterKey(event: KeyboardEvent) {
    event.preventDefault();
  }


constructor(public router:Router){
    
this.cateForm=this.fb.group({
      cates:this.fb.array([])      
    })
}



}

六:formGroup能验证吗?

angular表单踩坑之二_第1张图片

如图。红色框线内结构如下:

groupSize:this.fb.array([
                this.fb.group({
                  cate:['Indoor'],
                  title_en:['indoor'],
                  title_zh:['小桌'],
                  alias_en:['IN'],
                  alias_zh:['内'],
                  tableSize:this.fb.array([{
                          size: ["",],
                          desc_en:["",[Validators.required]],
                          desc_zh: ["",[Validators.required]],
                          alias_en:[null,],
                          alias_zh:[null,],
                          preTitle:["",[Validators.required,Validators.pattern(/^[A-Z]$/)]],    
                          minPerson: [null,[Validators.required]],
                          maxPerson: [null,[Validators.required]],
                    }]),
                  moreToggle:[null]
                }),                
              ]),

此时我们要对红色框线的最大值和最小值两个内容进行比较验证。而问题就在如何在minPerson中得到maxPerson的值呢?我们换一个思路。直接验证formGroup,

this.fb.group({
          size: ["",],
          desc_en:["",[Validators.required]],
          desc_zh: ["",[Validators.required]],
          alias_en:[null,],
          alias_zh:[null,],
          preTitle:["",[Validators.required,Validators.pattern(/^[A-Z]$/)]],
          minPerson: [null,[Validators.required]],
          maxPerson: [null,[Validators.required]],
        },{validator:this.minValueValidator()})


 //用户输入最大值/最小值时验证前后值大小
  minValueValidator():ValidatorFn{
    return (control: AbstractControl): { [key: string]: any } | null => {
      const minValueControl = control.get('minPerson')
      const maxValueControl = control.get('maxPerson')
      if(minValueControl&&maxValueControl){
        let maxValue=maxValueControl.value
        let minValue=minValueControl.value
        if (minValue&&maxValue&&(maxValue<= minValue)) {
           maxValueControl.setErrors({ maxValueInvalid: true });      
          return { 
            maxValueInvalid: true 
          };
        }
      }
      return null;
    };
  }

这样我们在整个formgroup里就可以得到minoerson和maxPerson

你可能感兴趣的:(angular,javascript,angular,formbuilder)