相对角形式>用户自定义表格错误 [英] Angular Reative Forms > User customized forms error

查看:28
本文介绍了相对角形式>用户自定义表格错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在我的角度应用程序中实现一项功能,使用户能够创建自己的表单(选择字段/验证/等).一旦他们创建了表单,我将其JSON结构存储在数据库中以备后用:

export interface FormModel {
  id: string;
  name: string;
  type: 'group' | 'control';
  children: FormModel[];
  isArray: boolean;
}

样本结构是这样的:

[{
    id: 'users',
    name: 'Users',
    type: 'group',
    isArray: true,
    children: [
      {
        id: "user",
        name: "User",
        type: 'group',
        isArray: false,
        children: [
          {
            id: "name",
            name: "Name",
            type: 'control',
            children: null,
            isArray: false
          },
          {
            id: "age",
            name: "Age",
            type: 'control',
            children: null,
            isArray: false
          }
        ]
      },
      {
        id: "user",
        name: "User",
        type: 'group',
        isArray: false,
        children: [
          {
            id: "name",
            name: "Name",
            type: 'control',
            children: null,
            isArray: false,
          },
          {
            id: "age",
            name: "Age",
            type: 'control',
            children: null,
            isArray: false
          }
        ]
      }
    ]
  }, {
    id: 'comments',
    name: 'Comments',
    type: 'control',
    children: null,
    isArray: false
  }
  ];

基于从数据库加载的JSON创建响应式表单后,由于具有递归,我很难创建相应的html.

经过多次尝试,我设法到达以下位置,在该处生成类似于我所需要的HTML:

<div formGroupName="users">
  <div formArrayName="0">
    <div formGroupName="user">
      <input type="text" formControlName="name">
      <input type="text" formControlName="age">
    </div>
  </div>
  <div formArrayName="0">
    <div formGroupName="user">
      <input type="text" formControlName="name">
      <input type="text" formControlName="age">
    </div>
  </div>
</div>

我使用的模板如下:

<form name="myForm" [formGroup]="myForm" fxLayout="column" fxFlex>
    <div formGroupName="variables">

        <ng-template #recursiveList let-controls let-prefix="prefix">
            <ng-container *ngFor="let item of controls; let i = index;">

                <input type="text" [formControlName]="item.id" *ngIf="(item?.children?.length > 0) == false">

            <div *ngIf="item?.children?.length > 0 && item.isArray" [formArrayName]="item.id">
                <ng-container
                    *ngTemplateOutlet="recursiveArray; context:{ $implicit: item.children, prefix: item.isArray }">
                </ng-container>
            </div>
            <div *ngIf="item?.children?.length > 0 && !item.isArray" [formGroupName]="item.id">
                <ng-container
                    *ngTemplateOutlet="recursiveList; context:{ $implicit: item.children, prefix: item.isArray }">
                </ng-container>
            </div>
        </ng-container>
    </ng-template>
    <ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: formFields, prefix: '' }">
    </ng-container>

    <ng-template #recursiveArray let-controls let-prefix="prefix">
        <ng-container *ngFor="let item of controls; let i = index;">

            <div [formGroupName]="i">

                <input type="text" [formControlName]="item.id" *ngIf="(item?.children?.length > 0) == false">

                <div *ngIf="item?.children?.length > 0 && item.isArray" [formArrayName]="item.id">
                    <ng-container
                        *ngTemplateOutlet="recursiveArray; context:{ $implicit: item.children, prefix: item.isArray }">
                    </ng-container>
                </div>
                <div *ngIf="item?.children?.length > 0 && !item.isArray" [formGroupName]="item.id">
                    <ng-container
                        *ngTemplateOutlet="recursiveList; context:{ $implicit: item.children, prefix: item.isArray }">
                    </ng-container>
                </div>
            </div>

        </ng-container>
    </ng-template>
  </div>

</form>

在我看来这是正确的,但我不断出错:

ERROR
Error: Cannot find control with path: 'variables -> 0'
ERROR
Error: Cannot find control with path: 'variables -> 0 -> user'

我用示例创建了一个堆栈闪电: https://stackblitz.com/edit/angular-mtbien

你们能帮助我找出问题所在吗?我已经为此工作了2天,但没有成功:(

谢谢!

解决方案

正如Ersenkoening所说,要使用FormsGroup和Form Arrays,可以直接使用控件".使用FormArrayName,Form Group等可能真是令人头疼.

看到Ersenkoening的form-array.component.html可以像这样更简单地编码

<div [formGroup]="formArray">
    <ng-container *ngFor="let child of formArray.controls; let i = index">
        <app-form-group [formGroup]="child"></app-form-group>
    </ng-container>
</div>

是,是管理FormArray的另一种方法,但请记住,formArray只是一个特别的" FormGroup.

更新 有了这个想法,我们将更加深入,在html中仅使用[formControl],因此,我们需要将"control"作为变量传递.参见 stackblitz

表单提交的视图就像

<form name="myForm" [formGroup]="myForm" fxLayout="column" fxFlex>
    <div *ngFor="let item of formFields;let i=index">
        {{item.id}}
        <ng-container *ngTemplateOutlet="recursiveList; context:{ 
            $implicit: formFields[i], 
            <!--see how pass the control of myForm--->
            control:myForm.get(item.id) }">
        </ng-container>
    </div>

    <ng-template #recursiveList let-item let-control="control">
        <div *ngIf="!item.children">
            {{item.id}}<input [formControl]="control">
    </div>
    <div *ngIf="item.children">
      <div *ngIf="!item.isArray">
        <div *ngFor="let children of item.children">
            <ng-container *ngTemplateOutlet="recursiveList; context:{
                 $implicit:children, 
                 <!--see how pass the control of a formGroup--->
                 control:control.get(children.id)}">
            </ng-container>
        </div>
      </div>
      <div *ngIf="item.isArray">
        <div *ngFor="let children of item.children;let i=index">
            <ng-container *ngTemplateOutlet="recursiveList; context:{ 
                  $implicit:children, 
                 <!--see how pass the control of a formArray--->
                  control:control.at(i)}">
            </ng-container>
        </div>
      </div>
    </div>
    </ng-template>
</form>
<pre>
{{myForm?.value|json}}
</pre>

更新2 简化了表单的创建,请参见

I am trying to implement a feature on my angular app where the users will be able to create their own forms (selecting fields/validation/etc). Once they create the form, I'll store it's JSON structure on the DB for use later:

export interface FormModel {
  id: string;
  name: string;
  type: 'group' | 'control';
  children: FormModel[];
  isArray: boolean;
}

A sample structure is this:

[{
    id: 'users',
    name: 'Users',
    type: 'group',
    isArray: true,
    children: [
      {
        id: "user",
        name: "User",
        type: 'group',
        isArray: false,
        children: [
          {
            id: "name",
            name: "Name",
            type: 'control',
            children: null,
            isArray: false
          },
          {
            id: "age",
            name: "Age",
            type: 'control',
            children: null,
            isArray: false
          }
        ]
      },
      {
        id: "user",
        name: "User",
        type: 'group',
        isArray: false,
        children: [
          {
            id: "name",
            name: "Name",
            type: 'control',
            children: null,
            isArray: false,
          },
          {
            id: "age",
            name: "Age",
            type: 'control',
            children: null,
            isArray: false
          }
        ]
      }
    ]
  }, {
    id: 'comments',
    name: 'Comments',
    type: 'control',
    children: null,
    isArray: false
  }
  ];

After creating the reactive form based on the JSON loaded from the DB, I'm having difficulties creating the respective html since it has recursion.

After many attempts, I managed to get to the following, where it generates a HTML similar to what I understand should be needed:

<div formGroupName="users">
  <div formArrayName="0">
    <div formGroupName="user">
      <input type="text" formControlName="name">
      <input type="text" formControlName="age">
    </div>
  </div>
  <div formArrayName="0">
    <div formGroupName="user">
      <input type="text" formControlName="name">
      <input type="text" formControlName="age">
    </div>
  </div>
</div>

The template I've used is the following:

<form name="myForm" [formGroup]="myForm" fxLayout="column" fxFlex>
    <div formGroupName="variables">

        <ng-template #recursiveList let-controls let-prefix="prefix">
            <ng-container *ngFor="let item of controls; let i = index;">

                <input type="text" [formControlName]="item.id" *ngIf="(item?.children?.length > 0) == false">

            <div *ngIf="item?.children?.length > 0 && item.isArray" [formArrayName]="item.id">
                <ng-container
                    *ngTemplateOutlet="recursiveArray; context:{ $implicit: item.children, prefix: item.isArray }">
                </ng-container>
            </div>
            <div *ngIf="item?.children?.length > 0 && !item.isArray" [formGroupName]="item.id">
                <ng-container
                    *ngTemplateOutlet="recursiveList; context:{ $implicit: item.children, prefix: item.isArray }">
                </ng-container>
            </div>
        </ng-container>
    </ng-template>
    <ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: formFields, prefix: '' }">
    </ng-container>

    <ng-template #recursiveArray let-controls let-prefix="prefix">
        <ng-container *ngFor="let item of controls; let i = index;">

            <div [formGroupName]="i">

                <input type="text" [formControlName]="item.id" *ngIf="(item?.children?.length > 0) == false">

                <div *ngIf="item?.children?.length > 0 && item.isArray" [formArrayName]="item.id">
                    <ng-container
                        *ngTemplateOutlet="recursiveArray; context:{ $implicit: item.children, prefix: item.isArray }">
                    </ng-container>
                </div>
                <div *ngIf="item?.children?.length > 0 && !item.isArray" [formGroupName]="item.id">
                    <ng-container
                        *ngTemplateOutlet="recursiveList; context:{ $implicit: item.children, prefix: item.isArray }">
                    </ng-container>
                </div>
            </div>

        </ng-container>
    </ng-template>
  </div>

</form>

It seems to me that it is right, but I keep getting errors:

ERROR
Error: Cannot find control with path: 'variables -> 0'
ERROR
Error: Cannot find control with path: 'variables -> 0 -> user'

I have created a stackblitz with the sample: https://stackblitz.com/edit/angular-mtbien

Could you guys help me identify the problem? I've been working on this for 2 days without any success :(

Thanks!

解决方案

As Ersenkoening say, for work with FormsGroup and Form Arrays you can use the "controls", directly. Use FormArrayName, Form Group, etc, can be a real headache.

See that the form-array.component.html of Ersenkoening can be coded more simple like

<div [formGroup]="formArray">
    <ng-container *ngFor="let child of formArray.controls; let i = index">
        <app-form-group [formGroup]="child"></app-form-group>
    </ng-container>
</div>

Yes is a different way to mannage a FormArray but remember that a formArray it's only a "especial" FormGroup.

Update With this idea, we are going to go more deeper, in the html only use [formControl], so, we need pass the "control" as a variable. See stackblitz

The form-filed-view is like

<form name="myForm" [formGroup]="myForm" fxLayout="column" fxFlex>
    <div *ngFor="let item of formFields;let i=index">
        {{item.id}}
        <ng-container *ngTemplateOutlet="recursiveList; context:{ 
            $implicit: formFields[i], 
            <!--see how pass the control of myForm--->
            control:myForm.get(item.id) }">
        </ng-container>
    </div>

    <ng-template #recursiveList let-item let-control="control">
        <div *ngIf="!item.children">
            {{item.id}}<input [formControl]="control">
    </div>
    <div *ngIf="item.children">
      <div *ngIf="!item.isArray">
        <div *ngFor="let children of item.children">
            <ng-container *ngTemplateOutlet="recursiveList; context:{
                 $implicit:children, 
                 <!--see how pass the control of a formGroup--->
                 control:control.get(children.id)}">
            </ng-container>
        </div>
      </div>
      <div *ngIf="item.isArray">
        <div *ngFor="let children of item.children;let i=index">
            <ng-container *ngTemplateOutlet="recursiveList; context:{ 
                  $implicit:children, 
                 <!--see how pass the control of a formArray--->
                  control:control.at(i)}">
            </ng-container>
        </div>
      </div>
    </div>
    </ng-template>
</form>
<pre>
{{myForm?.value|json}}
</pre>

Update 2 simplify the creation of the form, see stackblitz

  ngOnInit() {
    let group = new FormGroup({});
    this.formFields.forEach(element => {
      let formItem = this.createFormGroup(element);
      group.addControl(element.id, formItem);
    });

    this.myForm = group;
  }

  private createFormGroup(formItem: FormModel) {

    if (formItem.type=="group")
    {
      if (formItem.isArray && formItem.children.length<formItem.minQtd)
      {
        const add={...formItem.children[0]}
        //here we can "clean" the value
        while (formItem.children.length<formItem.minQtd)
        formItem.children.push(add)
      }
      let group:FormGroup=new FormGroup({});
      let controls:any[]=[]
      formItem.children.forEach(element=>{
        let item=this.createFormGroup(element);
        if (formItem.isArray)
           controls.push(item);
        else
          group.addControl(element.id, item);
      })
      if (!formItem.isArray)
        return group;

      return new FormArray(controls)
    } 
    if (formItem.type=="control")
      return new FormControl();
  }

这篇关于相对角形式&gt;用户自定义表格错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆