angular - 无法让这个 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”。
你们中的任何人都可以看到问题(或可能是多个问题)吗?
解决方案
推荐阅读
- mysql - mysql - 为什么这个关于 WHERE 子句的子查询有效?
- gem5 - GEM5 中的 gem5OpJni -- 是否支持 x86 或者我必须自己构建它?
- python - 由于 OpenEdx 中的 vertical_demo 导致 PluginMissingError
- c# - SQL Dependency OnChange 未被调用
- c# - 使用自定义模板创建脚本
- python - 对大型矩形图像的卷积神经网络模型的建议?
- python - 多重 sigma 符号 python
- javascript - 如何创建角度指令文本框只允许数字空间和分数
- java - 有没有办法让 Guice 保留注释?
- java - 我无法根据来自下游的响应标头更改全局过滤器中的 Spring 云网关响应?