【问题标题】:ERROR TypeError: Cannot read property 'invalid' of undefined while trying to list FormArray of FormGroups in Angular错误类型错误:尝试在 Angular 中列出 FormGroup 的 FormArray 时无法读取未定义的属性“无效”
【发布时间】:2020-03-19 16:51:40
【问题描述】:

我有一个可以根据客户需要进行修改的项目。为了获取具有不同修改的项目列表,我决定创建一个表单,他们可以在其中添加任意数量的项目,并为每个项目选择规格。为此,我需要创建一个 FormGroups 的 FormArray。一切都按原样编译,但是当程序尝试呈现表单时出现错误:ERROR TypeError: Cannot read property 'invalid' of undefined

这是我的 app.component.ts 的代码(items 和 addItem())

import {FormArray, FormControl, FormGroup, Validators} from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  form: FormGroup

  ngOnInit() {
    this.form = new FormGroup({
      companyName: new FormControl('', Validators.required),
      items: new FormArray([])
    })
    this.addItem()
  }

  submit() {
    if(this.form.valid){
      console.log('Form submited', this.form)
      const formData = {...this.form.value}

      console.log('Form Data: ', formData)
    }
  }

  controlSelectChanged() {
    if(this.form.get('item').get('controlSelect').value == 'Ручное'){

    }
  }

  addItem() {
    const item = new FormGroup({
      modelSelect: new FormControl('', Validators.required),
      quantity: new FormControl('', Validators.required),
      width: new FormControl('', Validators.required),
      height: new FormControl('', Validators.required),
      colorSelect: new FormControl('', Validators.required),
      factorySelect: new FormControl('', Validators.required),
      articleSelect: new FormControl('', Validators.required),
      articleSelect2: new FormControl('', Validators.required),
      controlSelect: new FormControl('', Validators.required),
      controlSideSelect: new FormControl('', Validators.required),
      controlFactorySelect: new FormControl('', Validators.required),
      controlAcceptorSelect: new FormControl('', Validators.required),
      automaticFactorySelect: new FormControl('', Validators.required),
      quantityPult: new FormControl('', Validators.required),
      automaticAdditionSelect: new FormControl('', Validators.required),
      notes: new FormControl(''),
    });
    (this.form.get('items') as FormArray).push(item)
    console.log(this.form.get('items'))
  }
}

这里有一段代码从 app.component.html 渲染这个数组

<form [formGroup]="form" (ngSubmit)="submit()">
        <div class="form-group invis">
          <label for="inputCompanyName">Название вашей компании</label>
          <input type="email" class="form-control" id="inputCompanyName" formControlName="companyName" aria-describedby="companyNameHelp" placeholder="Введите название компании">
          <small id="companyNameHelp" class="form-text text-muted">Это поле обязательно для заполнения.</small>
          <button type="button" class="btn btn-primary mt-20" [disabled]="form.controls['companyName'].invalid">Далее</button>
        </div>

<div *ngFor="let item of form.get('items')" class="form-group">
          <ng-container [formGroupName]="item">
          <div class="form-row">
            <div class="form-group col">
              <label for="modelSelect">Модель</label>
              <select class="form-control" id="modelSelect" formControlName="modelSelect">
                <option>ASN130</option>
                <option>ASN</option>
                <option>ASN110 cab</option>
                <option>ASN 110 GPZ TENS</option>
                <option>Зашивка на люверсах</option>
              </select>
            </div>

            <div class="col-md-1">
              <label for="quantity">Кол-во</label>
              <input type="text" class="form-control" id="quantity" placeholder="-" formControlName="quantity">
            </div>

            <div class="col-md-1">
              <label for="width">Ширина</label>
              <input type="text" class="form-control" id="width" placeholder="-" formControlName="width">
            </div>

            <div class="col-md-1">
              <label for="height">Высота</label>
              <input type="text" class="form-control" id="height" placeholder="-" formControlName="height">
            </div>

            <div class="form-group col">
              <label for="colorSelect">Цвет профилей</label>
              <select class="form-control" id="colorSelect" formControlName="colorSelect">
                <option>9003 Белый</option>
                <option>9004 Черный</option>
                <option>8017 Коричневый</option>
                <option>7024 Антрацит</option>
                <option>1024 Бежевый</option>
              </select>
            </div>

            <div class="form-group col">
              <label for="factorySelect">Производитель тента</label>
              <select class="form-control" id="factorySelect" formControlName="factorySelect">
                <option>COPACO</option>
                <option>DICKSON</option>
                <option>М8</option>
              </select>
            </div>

            <div class="form-group col">
              <label for="articleSelect">Артикул тента</label>
              <select class="form-control" id="articleSelect" formControlName="articleSelect">
                <option>COPACO</option>
                <option>DICKSON</option>
                <option>М8</option>
              </select>
            </div>

            <div class="form-group col">
              <label for="articleSelect2">Сторона</label>
              <select class="form-control" id="articleSelect2" formControlName="articleSelect2">
                <option>Back со стороны короба</option>
                <option>Front со стороны короба</option>
                <option>Back со стороны подворота</option>
                <option>Front со стороны подворота</option>
              </select>
            </div>

          </div>
          <div class="form-row">
            <div class="form-group col">
              <label for="controlSelect">Управление</label>
              <select class="form-control" id="controlSelect" formControlName="controlSelect" (change)="controlSelectChanged()">
                <option>Ручное</option>
                <option>Автоматическое</option>
              </select>
            </div>

            <div class="form-group col">
              <label for="controlSideSelect">Сторона управления </label>
              <select class="form-control" id="controlSideSelect" formControlName="controlSideSelect">
                <option>Справа</option>
                <option>Слева</option>
              </select>
            </div>

            <div class="form-group col">
              <label for="controlFactorySelect">Производитель привода</label>
              <select class="form-control" id="controlFactorySelect" formControlName="controlFactorySelect">
                <option>Дорхан</option>
                <option>Somfy</option>
              </select>
            </div>

            <div class="form-group col">
              <label for="controlAcceptorSelect">Вид привода</label>
              <select class="form-control" id="controlAcceptorSelect" formControlName="controlAcceptorSelect">
                <option>Без приемника</option>
                <option>Cо встроенным приемником </option>
              </select>
            </div>
          </div>
          <div class="form-row">
            <div class="form-group col">
              <label for="automaticFactorySelect">Производитель автоматики</label>
              <select class="form-control" id="automaticFactorySelect" formControlName="automaticFactorySelect">
                <option>Дорхан</option>
                <option>Somfy</option>
              </select>
            </div>

            <div class="col-lg-2">
              <label for="quantityPult">Кол-во пультов</label>
              <input type="text" class="form-control" id="quantityPult" placeholder="-" formControlName="quantityPult">
            </div>

            <div class="form-group col">
              <label for="automaticAdditionSelect">Дополнительная автоматика</label>
              <select class="form-control" id="automaticAdditionSelect" formControlName="automaticAdditionSelect">
                <option>Нет</option>
                <option>Датчик ветра</option>
              </select>
            </div>

            <div class="col">
              <label for="notes">Примечания</label>
              <textarea class="form-control" id="notes" rows="3" formControlName="notes"></textarea>
            </div>
          </div>
          </ng-container>
        </div>
<div class="form-row justify-content-md-center">
          <button type="button" class="btn btn-primary mt-30 addNewItemButton" (click)="addItem()">Добавить новый товар</button>
        </div>
<button type="button" class="btn btn-primary mt-20" [disabled]="form.controls['item'].invalid">Далее</button>


        <button type="submit" class="btn btn-primary mt-20" [disabled]="form.invalid">submit</button>
      </form>

提前致谢!

【问题讨论】:

  • 您错过了 HTML 的关键部分。您能否在&lt;form&gt; 和表单数组之间添加 HTML 层次结构。
  • 代码已更新
  • form.controls['item'].invalid - 你确定controls['item'] 被定义了吗?
  • 非常感谢!没有错误了,但问题是表单仍然没有呈现

标签: javascript angular forms


【解决方案1】:

您可以在这里找到一个工作示例:

https://stackblitz.com/edit/angular-obnxyi

  1. 我使用formBuilder 反射组件以获得更清晰的代码。
  2. *NgFor 中,访问FormArray 控件的正确方法是使用form.get('items').controlscontrols,这样您就可以访问控件数组了
  3. 我将循环移动到ng-container,以便在重复标记的父级内设置formArrayName 属性。所以你现在有&lt;div formArrayName="items" class="form-group"&gt;

【讨论】:

  • src/app/app.component.html:24:34 - 错误 TS2339:“AbstractControl”类型上不存在属性“控件”。 24
    ~~~~~~~~~~~~~~~~ ~~~~~~~~~~ src/app/app.component.ts:6:16 6 templateUrl: './app.component.html', ~~~~~~~~~~~~~~~ ~~~~~~~~ 组件AppComponent的模板出错。添加 .controls 后显示此错误
  • 您是否完全按照 stackblitz 上的示例进行操作?我使用FormBuilder 更改了表单上的构建
  • 是的,我是。但问题是由于某种原因我不能使用 .controls,但它适用于 ['controls'],所以它应该看起来像:form.get('items')['controls']
【解决方案2】:

您的表单 HTML 应该反映您的表单结构。

所以如果你构建如下表格:

ngOnInit() {
  this.form = new FormGroup({
    companyName: new FormControl('', Validators.required),
    items: new FormArray([
      new FormGroup({
        modelSelect: new FormControl('', Validators.required)
      }),
      new FormGroup({
        modelSelect: new FormControl('', Validators.required)
      })
    ])
  });
}

onSubmit() {
  const models = this.form.get('items')
    .controls
    .map(x => x.get('modelSelect').value);
  console.log(models);
}

那么你需要的最小 HTML 是这样的:

<form [formGroup]="form" (ngSubmit)="onSubmit()">
  <input formControlName="companyName" />
  <div formArrayName="items">
    <ng-container *ngFor="let control of form.get('items').controls; index as i">
      <div [formGroupName]="i">
        <input formControlName="modelSelect" />
      </div>
    </ng-container>
  </div>
</form>

注意表单指令如何反映FormGroup 的结构。如果您的数组由表单组组成,您可以使用 [formGroupName]="i" 绑定 FormArray 的项目。

【讨论】:

    猜你喜欢
    相关资源
    最近更新 更多
    热门标签