首页 > 解决方案 > 无法让这个 Angular Reactive Form(s) 工作:数据绑定错误、存储/获取数据未正确填充表单等

问题描述

所以这是一个 Angular 5 应用程序。对于这个问题,我查看了 Angular.io 基本文档/示例应用程序(可以在这里找到:https ://angular.io/guide/reactive-forms ),但我似乎无法让它工作(正确)在我的最后:(。我还想说我对Angular非常陌生。

所以我会分解这篇文章来解释我想要什么,尝试用打印屏幕(以及我的评论)展示它,然后展示我的组件和服务的代码,这样你就可以全面了解我想要的东西,我有什么,什么可能是错的。另外,不要担心到处都是可怕的布局,现在并不重要(它只需要工作)。

因此,它是一个从/在 Sharepoint 上获取/创建内容的应用程序,但如果您对 Sharepoint 的任何内容一无所知,那么理解这个问题并不是问题。

首先我有这个页面,你可以登录:在此处输入图像描述

用户将登录特定的 Sharepoint 租户/站点,然后被重定向到我的第一个视图,我希望用户能够在表单中动态地创建内容类型。我的意思是,理想情况下,显示一个空的,他可以随意添加“内容类型”。内容类型可以看作是普通编程中的“类”。

这是我的内容类型视图,我希望用户能够在其中制作几个:在此处输入图像描述

首先,这会产生一些错误:

ERROR Error: Cannot find control with path: 'contentTypesInputFields -> 0 -> Name'
    at _throwError (forms.js:2432)
    at setUpControl (forms.js:2300)
    at FormGroupDirective.addControl (forms.js:6658)
    at FormControlName._setUpControl (forms.js:7308)
    at FormControlName.ngOnChanges (forms.js:7221)
    at checkAndUpdateDirectiveInline (core.js:12365)
    at checkAndUpdateNodeInline (core.js:13893)
    at checkAndUpdateNode (core.js:13836)
    at debugCheckAndUpdateNode (core.js:14729)
    at debugCheckDirectivesFn (core.js:14670)

当我在任何地方单击任何内容或键入任何字符/字符串时,它似乎在我的控件中“循环”(它似乎无法找到),最终看起来像这样(应该如此):

在此处输入图像描述

它循环出现的错误与上面相同,只是这次也适用于“Group”和“ContentTypeInherit”。此图像与上一个图像之间的一些区别:现在它显示“内容类型 #1”,还有我的“从内容类型继承:”标签和选择显示(并且选择已填满)。

我现在要单击按钮以添加一个新的“测试行”,第一张图片是单击按钮后立即显示的内容,第二张图片显示了输入几项内容后的外观(因此它可以循环显示错误再次):

第一张图片:
在此处输入图像描述

第二张图片:
在此处输入图像描述

当我单击“删除”按钮时,它应该删除它所单击的“内容类型”字段,所以如果我单击“内容类型#2”下的“删除”按钮,它应该删除那个,而不是其他。现在这个按钮什么都不做(但我试着编码,见后)。

好的,现在我将分享我的整个代码文件:

我的组件 HTML:

<form [formGroup]="contentTypeForm" (ngSubmit)="onSubmit($event)">
  <div formArrayName="contentTypesInputFields">
      <div *ngFor="let currentContentType of contentTypesInputFields.controls; let i=index" [formGroupName]="i">
        <h4>Content Type #{{i + 1}}</h4>
        <ul>
          <li class="form-group">
            <label>Content Type Name:</label>
            <input class="json-input form-control" type="text" formControlName="Name">
          </li>
          <li class="form-group">
            <label>Content Type Group:</label>
            <input class="json-input form-control" type="text" formControlName="Group">
          </li>
          <li class="form-group" *ngIf="contentTypeDropDown.length != '0'">
            <label>Inherit from Content Type:</label>
            <select class="json-input form-control" formControlName="ContentTypeInherit" *ngIf="!checkboxCTchecked">
              <option *ngFor="let ct of contentTypeDropDown" [value]="ct.name">{{ ct.name }}</option>
            </select>
            <!--
            <input class="json-input form-control" type="text" formControlName="ContentTypeInherit" *ngIf="checkboxCTchecked" placeholder="Cannot be left empty!">
            -->
          </li>
        </ul>
        <button type="button" (click)="removeContentTypeInputFields(currentContentType)">Remove</button>
      </div>
      <div>
        <!--
        <button type="submit" [disabled]="contentTypeForm.pristine">Save</button>
        <button type="button" [disabled]="contentTypeForm.pristine" (click)="revertData()">Revert</button>
        -->
        <button type="submit">Save</button>
        <button type="button" (click)="revertData()">Revert</button>
      </div>
  </div>
</form>
<div>
  <button type="button" (click)="addContentTypeInputFields()">Click here to add a new test row!</button>
</div>  

我的组件 TS:

import { Component, OnInit, OnChanges } from '@angular/core';
import { DataServicesService } from '../data-services.service';
import { ContentType } from '../ContentType';
import { FormGroup, FormBuilder, FormArray } from '@angular/forms';

@Component({
  selector: 'app-content-type-detail',
  templateUrl: './content-type-detail.component.html',
  styleUrls: ['./content-type-detail.component.css']
})
export class ContentTypeDetailComponent implements OnInit, OnChanges {

  contentTypeForm: FormGroup;

  contentTypeInputFieldsArrayOnLoad: ContentType[] = [
    {
      id: '',
      name: '',
      group: '',
      contentTypeInherit: ''
    }
  ];
  contentTypeInputFieldsArray: ContentType[] = this.contentTypeInputFieldsArrayOnLoad.slice();
  contentTypeDropDown: ContentType[] = [];

  checkboxCTchecked = false;

  constructor(
    private dataService: DataServicesService,
    private fb: FormBuilder
  ) {
    this.createForm();
  }

  ngOnInit() {

  }

  ngOnChanges() {
    this.rebuildForm();
  }

  public createForm() {
    const tt2 = this.dataService.getData('ContentTypesDropDown');
    if (this.dataService.getData('ContentTypesDropDown') != null) {
      this.dataService.getData('ContentTypesDropDown').subscribe(
        res => this.contentTypeDropDown = JSON.parse(res)
      );
    }

    let data = null;
    if (this.dataService.getData('ContentTypes') != null) {
      this.dataService.getData('ContentTypes').subscribe(
        res => data = JSON.parse(res)
      );
    }

    if (data != null) {
      const arr = JSON.parse(data);
      this.contentTypeInputFieldsArrayOnLoad = arr;
      this.contentTypeInputFieldsArray = this.contentTypeInputFieldsArrayOnLoad.slice();
    }

    this.contentTypeForm = this.fb.group({
      // contentTypesInputFields: this.fb.array([])
      contentTypesInputFields: this.fb.array(this.contentTypeInputFieldsArrayOnLoad)
    });
    const tt = this.contentTypesInputFields;
  }

  public rebuildForm() {
    this.setContentTypes(this.contentTypeInputFieldsArray);
  }

  get contentTypesInputFields(): FormArray {
    return this.contentTypeForm.get('contentTypesInputFields') as FormArray;
  }

  public setContentTypes(ctArr: ContentType[]) {
    const contentTypeFGs = ctArr.map(x => this.fb.group(x));
    const contentTypeFormArray = this.fb.array(contentTypeFGs);
    this.contentTypeForm.setControl('contentTypesInputFields', contentTypeFormArray);
  }

  public onSubmit(event: Event) {
    event.preventDefault();

    this.saveData();
  }

  public addContentTypeInputFields(): void {
    this.contentTypesInputFields.push(this.fb.group(new ContentType('', '', '', '')));
  }

  public removeContentTypeInputFields(test: FormGroup): void {
    const index = this.contentTypeInputFieldsArray.indexOf(test.value['name']);
    if (index > -1) {
      this.contentTypeInputFieldsArray.splice(index, 1);
    }
  }

  public saveData() {
    const ct: ContentType[] = this.prepareSaveContentType();
    const json = JSON.stringify(ct);
    this.dataService.saveData('ContentTypes', json);
    this.contentTypeInputFieldsArray = ct;
    this.contentTypeInputFieldsArrayOnLoad = ct;
    this.rebuildForm();
  }

  public prepareSaveContentType(): ContentType[] {
    const formModel = this.contentTypeForm.value;

    // deep copy of form model input fields
    const inputFieldsDeepCopy: ContentType[] = formModel.contentTypesInputFields.map(
      // nog ke zien of "javascript literal object" goed is
      (ct: ContentType) => Object.assign({}, ct)
    );

    // return new contenttype array object containing a combination of original values
    // and deep copies of changed form model values
    const saveCT: ContentType[] = inputFieldsDeepCopy;

    return saveCT;
  }

  public revertData() {
    /*this.contentTypeInputFieldsArray = this.contentTypeInputFieldsArrayOnLoad.slice();
    const tt = this.contentTypeInputFieldsArray;*/
    this.rebuildForm();
  }

}  

我还想要实现的是,当我单击“保存”按钮时,它将数据保存到服务中(它只是在地图中设置键/值)。当我离开这个组件时(例如导航到查看“SiteColumns”),然后当我回来时,我希望组件检查服务以查看在服务/地图内部是否有一条带有键“ContentTypes”的记录,如果因此,动态填写字段数量,以便显示用户保存的内容,而不是再次显示空的“内容类型”。

此外,它似乎没有正确地将我的内容类型绑定到它看起来的“*ngFor”。

你们中的任何人都可以看到问题(或可能是多个问题)吗?

标签: angularformsdata-bindingangular-reactive-forms

解决方案


推荐阅读