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
不能为空
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();
}
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);
});
});
}
}
表单结构如下
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的输入框里失去焦点的时候验证的错误提示都是一样的,所以我们把这个错误提示合二为一。
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能验证吗?
如图。红色框线内结构如下:
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