从 json 创建动态表单时出错无法找到名称为“数据"的控件 [英] Error during dynamic form creation from json Cannot find control with name: 'data'

查看:41
本文介绍了从 json 创建动态表单时出错无法找到名称为“数据"的控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 angular 7 应用程序,我正在编写一个通用的动态表单创建器.我正在使用响应式表单和 ng-template 和 递归显示表单元素.项目的代码可以看到

我做错了什么?请帮忙.

问题的重现是这里是stackblitz

解决方案

你说得对,这里的 ng-template 有问题.

FormControlName 指令严重依赖于当前元素之上的元素层次结构来确定 FormControl.由于您使用的是 ngTemplateOutlet,这个层次结构混淆了,Angular 找不到父 FormGroupFormArray 控件.

您可以重构您的示例以使用嵌套组件,如果您保留子 AbstractControls 的层次结构,它应该可以工作.

另一方面,如果您将使用 ngTemplateOutlet 使其工作>FormControl 指令.您只需要确保为 input 元素提供了正确的控制.

这是如何做到的:

<ng-container *ngTemplateOutlet="controlList; context: {controls: schema, path: []}"></ng-container><ng-template #controlList let-controls="controls" let-path="path" let-isArray="isArray"><ng-container *ngFor="let control of controls; let i = index;"><ng-container *ngIf="control?.type === 'simple'"><div class="控件"><标签>{{ control.label }}<input type="text" [formControl]="form.get(isArray ? path.concat(i, control.name) : path.concat(control.name))">

</ng-容器><ng-container *ngIf="['object', 'array'].indexOf(control.type) > -1"><字段集><legend>{{control.name}}</legend><ng-容器*ngTemplateOutlet="controlList; context: {controls: control.items, isArray: control.type === 'array', path: isArray ? path.concat(i, control.name) : path.concat(control.name)}"></ng-容器></fieldset></ng-容器></ng-容器></ng-模板></表单>

这里的关键点是获得正确的path 到目标控件,我将其作为上下文的一部分传递到下一个子级别.

此外,如果您想生成无限结构的嵌套元素,您应该重构构建 FormGroup 的代码:

ngOnInit() {this.schema = data.start.fields.schema;this.form = this.createFormGroup(this.schema);}createFormGroup(项目){让组:{ [controlId: string]: AbstractControl;} = {};items.forEach((item) => group[item.name] = this.createFormControl(item));返回 this.fb.group(group);}创建窗体控件(项目){if (item.type === '数组') {返回 this.fb.array(item.items.map((subItem) => {返回 this.fb.group({[subItem.name]: this.createFormControl(subItem)})}));} else if (item.type === 'object') {返回 this.createFormGroup(item.items);} 别的 {返回 this.fb.control(item.value, []);}}

Ng-run 示例

I am using angular 7 appplication and i am writing a generic dynamic form creator. I am using reactive forms and ng-template and <ng-container *ngTemplateOutlet> for recursively displaying the form element.The code of the project could be seen Here.

But i am facing the following error

ERROR Error: Cannot find control with name: 'data'
    at _throwError (forms.js:1775)
    at setUpControl (forms.js:1683)
    at FormGroupDirective.push../node_modules/@angular/forms/fesm5/forms.js.FormGroupDirective.addControl (forms.js:4532)
    at FormControlName.push../node_modules/@angular/forms/fesm5/forms.js.FormControlName._setUpControl (forms.js:5030)
    at FormControlName.push../node_modules/@angular/forms/fesm5/forms.js.FormControlName.ngOnChanges (forms.js:4980)
    at checkAndUpdateDirectiveInline (core.js:9239)
    at checkAndUpdateNodeInline (core.js:10507)
    at checkAndUpdateNode (core.js:10469)
    at debugCheckAndUpdateNode (core.js:11102)
    at debugCheckDirectivesFn (core.js:11062)

But if i see my formGroup object i can see a control with data as the name of the control as shown below.

What is the mistake i am doing ? Please help.

Reproduction of the problem is Here stackblitz

解决方案

You're right there is a problem with ng-template here.

FormControlName directive heavily relies on hierarhy of elements above current element to determine FormControl. Since you're using ngTemplateOutlet this hierarhy mixed up and Angular can't find parent FormGroup or FormArray controls.

You can refactor your example to use nested components and it should work if you will keep hierarhy of child AbstractControls.

On the other hand, you can make it work with ngTemplateOutlet if you will use FormControl directive. You need to only make sure you provided correct control to your input element.

Here's how it could be done:

<form [formGroup]="form">
    <ng-container *ngTemplateOutlet="controlList; context: {controls: schema, path: []}"></ng-container>

    <ng-template #controlList let-controls="controls" let-path="path" let-isArray="isArray">

        <ng-container *ngFor="let control of controls; let i = index;">
            <ng-container *ngIf="control?.type === 'simple'">
                <div class="control">
                    <label>
              {{ control.label }}
              <input type="text" [formControl]="form.get(isArray ? path.concat(i, control.name) : path.concat(control.name))"> 
            </label>
                </div>
            </ng-container>
            <ng-container *ngIf="['object', 'array'].indexOf(control.type) > -1">
                <fieldset>
          <legend>{{control.name}}</legend>
                    <ng-container
                        *ngTemplateOutlet="controlList; context: {controls: control.items, isArray: control.type === 'array', path: isArray ? path.concat(i, control.name) : path.concat(control.name)}">
                    </ng-container>
                </fieldset>
            </ng-container>
        </ng-container>

    </ng-template>
</form>

The key point here is to get correct path to destination control which I'm passing as part of context to next child level.

Also if you want to generate infinity structure nested elements you should refactor your code for building FormGroup:

ngOnInit() {
  this.schema = data.start.fields.schema;
  this.form = this.createFormGroup(this.schema);
} 

createFormGroup(items) {
  let group: { [controlId: string]: AbstractControl; } = {};

  items.forEach((item) => group[item.name] = this.createFormControl(item));

  return this.fb.group(group);
}

createFormControl(item) {
  if (item.type === 'array') {
    return this.fb.array(item.items.map((subItem) => {
      return this.fb.group({
        [subItem.name]: this.createFormControl(subItem)
      })
    }));
  } else if (item.type === 'object') {
    return this.createFormGroup(item.items);
  } else {
    return this.fb.control(item.value, []);
  }
}

Ng-run Example

这篇关于从 json 创建动态表单时出错无法找到名称为“数据"的控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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