【问题标题】:Dynamically creating a form of checkboxes from nested JSON with ReactiveForms使用 ReactiveForms 从嵌套 JSON 动态创建复选框形式
【发布时间】:2019-08-09 20:31:33
【问题描述】:

我有一个由诸如音乐之类的父类别组成的数据结构。它的标题是在hobbies[0].parent.title 下的对象中定义的987654326@ 作为一个数组 - 我想将这些作为复选框输出,这同样没问题,但是当我尝试将它们设置为反应形式时出现问题,我的基本需要是验证至少 1 或 2 已被检查并使用 ReactiveForms 获取选中项目的信息我很清楚如何在没有嵌套 json 结构的情况下实现这一点,这真的让我失望

我在这些文章中尝试了很多不同的方法,但我似乎无法让它发挥作用,非常感谢任何帮助。

https://stackblitz.com/edit/form-array-3-levels

https://medium.com/hashtaagco/3-levels-of-nested-form-arrays-including-reactive-validations-we-decided-to-go-inception-mode-on-4fffe667fb2a

https://www.toptal.com/angular-js/angular-4-forms-validation

https://jenniferwadella.com/blog/managing-dynamic-and-nested-forms-angular

我已经玩了几个小时了,我的 html 保持不变

  <form [formGroup]="form"  action="">
    <label *ngFor="let parent of fetchInterests; let i = index">
      <div  *ngFor="let item of parent.children; let i = index">
        <ion-checkbox ></ion-checkbox>  {{  item.title }} {{  item.selected }}
      </div>
    </label>
  </form> 

我的 json 演示数据如下

var hobbies = [{
            "parent": {
                "parentId": "dMGkZuB8JV",
                "title": "Music",
                "hero": "https://s3-eu-west-1.amazonaws.com/music/placeholder.jpg"
            },
            "children": [{
                "title": "Jazz",
                "hero": "https://s3-eu-west-1.amazonaws.com/music/placeholder.jpg",
                "about": "",
                "selected": false
            }, {
                "title": "Rock",
                "hero": "https://s3-eu-west-1.amazonaws.com/music/placeholder.jpg",
                "about": "",
                "selected": false
            }, {
                "title": "Classical",
                "hero": "https://s3-eu-west-1.amazonaws.com/music/placeholder.jpg",
                "about": "",
                "selected": false
            }, {
                "title": "Soul",
                "hero": "https://s3-eu-west-1.amazonaws.com/music/placeholder.jpg",
                "about": "",
                "selected": false
            }]
        }, {
            "parent": {
                "parentId": "19h2yOfZaq",
                "title": "computers and systems",
                "hero": "https://s3-eu-west-1.amazonaws.com/it/placeholder.jpg"
            },
            "children": [{
                "title": "data processing",
                "hero": "https://s3-eu-west-1.amazonaws.com/it/placeholder.jpg",
                "about": "",
                "selected": false
            }]
        }, {
            "parent": {
                "parentId": "m2zQkAgOog",
                "title": "African planes",
                "hero": "https://s3-eu-west-1.amazonaws.com/outdoors/placeholder.jpg"
            },
            "children": [{
                "title": "camping",
                "hero": "https://s3-eu-west-1.amazonaws.com/outdoors/placeholder.jpg",
                "about": "",
                "selected": false
            }, {
                "title": "swimming",
                "hero": "https://s3-eu-west-1.amazonaws.com/outdoors/placeholder.jpg",
                "about": "",
                "selected": false
            }, {
                "title": "hunting",
                "hero": "https://s3-eu-west-1.amazonaws.com/outdoors/placeholder.jpg",
                "about": "",
                "selected": false
            }]
        }]

【问题讨论】:

    标签: javascript angular angular-reactive-forms


    【解决方案1】:

    Kravitz,首先想想你想改变什么。您可以计划更改仅选定的对象,或所有对象。

    我想你只想改变“检查”,所以你需要一个formArrays的formArrays

      hobbiesForm: FormArray
      ngOnInit() {
        this.hobbiesForm = new FormArray(this.hobbies.map(x => new FormArray([], this.customValidator())))
    
        //this give so formArray as hobbies you get
        this.hobbies.forEach((x, index) => {  //for each hobiee
            const array=this.hobbiesForm.at(index) as FormArray
            x.children.forEach(c=>{
              array.push(new FormControl(c.selected))
            })
          })
      }
    

    管理FormArrays的formArray是一些复杂的事情,但你只需要考虑到当我们制作*ngFor="let control of formArray.controls"时,“控制”是数组中的元素。所以在hobbiesForm.controls 中将是一个 FormArray,而在 formArray 中将是一个控件。

    好吧,代码更好

    <form [formGroup]="hobbiesForm" (submit)="submit(hobbiesForm)">
      <!--array is the inner FormArray--->
      <div *ngFor="let array of hobbiesForm.controls;let i=index">
        <b>{{hobbies[i].parent.title}}:</b>
        <!--control is the inner control of the array-->
        <div *ngFor="let control of array.controls;let j=index">
        <label>{{hobbies[i].children[j].title}}</label>
        <input  type="checkbox" [formControl]="control">
        </div>
      </div>
      <button>submit</button>
    </form>
    

    好吧,在提交时我们可能想更改“爱好”对象,请记住我们只有 [[true,false,false,true],[false],[true,false,false]] 之类的值

    submit(form)
      {
        if (form.valid)
        {
          form.value.forEach((x,i)=>
          {
             x.forEach((c,j)=>{
                this.hobbies[i].children[j].selected=c;
             })
          })
          console.log(this.hobbies)
        }
      }
    

    customValidator 可以是这样的

    customValidator() {
        return (formArray: FormArray) => {
          return formArray.value.filter(x=>x).length>0?null:{error:"at leat one"}
        }
      }
    

    如果超过了所有的形式

    customValidatorOverAll(min)
      {
        return (formArray: FormArray) => {
          let count=0;
    
          formArray.value.forEach(x=>{
             count+=x?x.filter(c=>c).length:0;
          })
          return count>=min?null:{error:"At least select "+min}
        }
      }
    

    你可以在这个stackblitz看到它

    更新好吧,这个问题是关于离子选择的。一个离子选择多重返回一个带有值的数组,所以事情是非常不同的。我们将获得一些类似的 [["Jazz","Rock"]["dat aprocessing"],["camping"]]

    这是一个数组数组,但我们只需要一个 FormControls 数组。是的,FormControl 可以存储任何东西,甚至是数组。所以

      ngOnInit() {
        this.hobbiesForm = new FormArray(
          this.hobbies.map(x => new FormControl('', this.customValidator())))
      }
    

    和我们的 .html 有点像

    <form [formGroup]="hobbiesForm" (submit)="submit(hobbiesForm)">
        <ion-item *ngFor="let control of hobbiesForm.controls;let i=index">
            <ion-label>{{hobbies[i].parent.title}}:</ion-label>
    
            <ion-select [formControl]="control" multiple="true" cancelText="cancel" okText="ok">
                <ion-option *ngFor="let hobbiee of hobbies[i].children; let i = index" 
                   [value]="hobbiee.title" selected="false">{{hobbiee.title}}</ion-option>
            </ion-select>
        </ion-item>
    </form>
    

    我们再次迭代hobbiesForm.controls。但是在这种情况下,select 给了我们一个数组。

    好吧,我放了一个 customValidator。如果我们希望至少选择一个,那只是我们的 customValidator 就像

    customValidator() {
        return (formControl: FormControl) => {
          return formControl.value && formControl.value.length>0?null:{error:"At least select one"}
        }
      }
    

    如果我们想要一个 customValidator 覆盖所有表单,我们可以使用类似

    customValidatorOverAll(min)
      {
        return (formArray: FormArray) => {
          let count=0;
          formArray.value.forEach(x=>{
             count+=x.length;
          })
          return count>=min?null:{error:"At least select "+min}
        }
      }
    

    当我们创建formArray时

    this.hobbiesForm = new FormArray(this.hobbies.map(x => new FormControl(''))
    ,this.customValidatorOverAll(2))
    

    ion-select stackblitz

    注意:看到我们迭代formArray.controls,而不是hobbies对象,并使用两个索引i,j和hobbies对象来显示“标签”,但这不属于formArray

    【讨论】:

    • 哇,这太棒了!
    • K@Kravitz,不,这不好,我很抱歉,你正在使用离子选择。我想多个。让我有时间用 ion-select multiple 更新我的答案
    • 非常感谢,您将如何进行验证以确保至少 2/3 被选中
    • 我使用离子选择更新了我的答案。我不知道你想说什么“至少选择了 2/3”形式的全部或每个选项至少一个?如果您希望选择 2/3,则需要验证器覆盖所有表单
    • 它也不是一个离子选择 :) 它只是离子输入复选框,但也有这个例子真是太棒了......我试图使用这个带有复选框的最小验证器,但它不起作用,表单保持不变无效
    【解决方案2】:

    您可以在父级中提及,而不是将子级作为单独的数组。

    var hobbies = [{
            "parent": {
                "parentId": "dMGkZuB8JV",
                "title": "Music",
                "hero": "https://s3-eu-west-1.amazonaws.com/music/placeholder.jpg"
                "children": [{
                     "title": "Jazz",
                     "hero": "https://s3-eu-west-1.amazonaws.com/music/placeholder.jpg",
                     "about": "",
                      "selected": false
                      }, {
                "title": "Rock",
                "hero": "https://s3-eu-west-1.amazonaws.com/music/placeholder.jpg",
                "about": "",
                "selected": false
            }, {
                "title": "Classical",
                "hero": "https://s3-eu-west-1.amazonaws.com/music/placeholder.jpg",
                "about": "",
                "selected": false
            }, {
                "title": "Soul",
                "hero": "https://s3-eu-west-1.amazonaws.com/music/placeholder.jpg",
                "about": "",
                "selected": false
            }]
    
            }
    
        }
    

    【讨论】:

    • 如何创建表单控件
    • 能否将您的代码上传到 stackblitz 并分享链接
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-02
    • 1970-01-01
    • 1970-01-01
    • 2016-09-04
    相关资源
    最近更新 更多