我在一个有嵌套Formarray的页面上工作。
constructor(private fb: FormBuilder) {
this.buildForm();
}
private buildForm() {
this.trialBudgetForm = this.fb.group({
clients: this.fb.array([])
})
}
addCustomer() {
this.clients.push(this.initCustomer());
}
initCustomer() {
return this.fb.group({
earnedIncomes: this.fb.array([this.initIncome()]),
thirtyAndOneThirdYN: [],
thirtyYN: [],
earnedIncomeExpenseType: [],
earnedIncomeExpense: [],
dependentCareExpense: []
})
}
initIncome() {
return this.fb.group(new ScrAmountType());
}
ngOnInit() {
this.populateData();
}
populateData() {
const clients = this.TrialBudgetData.clients;
const customerFGs = clients.map(customer => this.fb.group({
earnedIncomes: this.populateIncome(customer.earnedIncomes),
thirtyAndOneThirdYN: [this.utilService.YNConvert(customer.thirtyAndOneThirdYN)],
thirtyYN: [this.utilService.YNConvert(customer.thirtyYN)],
earnedIncomeExpenseType: [customer.earnedIncomeExpenseType],
earnedIncomeExpense: [customer.earnedIncomeExpense],
dependentCareExpense: [customer.dependentCareExpense]
}));
const customerFormArray = this.fb.array(customerFGs);
this.trialBudgetForm.setControl('clients', customerFormArray);
}
出于演示目的,我没有发布整个ts文件,因为有近600行。但如果需要,我可以提供更多细节。 这是html页面(也只是一个片段)
<form novalidate [formGroup]="trialBudgetForm" (ngSubmit)="save(trialBudgetForm)">
<div class="settingsPage-box">
<h3 class="box-border-bottom">Earned Income
<span class="float_right add-field add-margin hand" (click)="addCustomer()">
<i class=" fa fa-plus-circle fa-lg plusIconColor"></i>
<span>
Add Earned Income
</span>
</span>
</h3>
<div formArrayName="clients">
<div *ngFor="let earnedIncomeCustomer of clients.controls; let i = index" [formGroupName]="i" class="row-view box-border-bottom">
<div class="box-border-bottom row-view">
<div class="col-sm-3 padding0">
<label for="customer{{i+1}}" class="pushDown">Customer {{i+1}}:</label>
</div>
<div class="col-sm-9 grid-block padding0">
<div class="col-sm-1 float_right" (click)="deleteCustomer(i)">
<span class="delete-row hand">
<i class="fa fa-trash-o"></i>
</span>
</div>
</div>
<div class="col-sm-3">
<label class="pushDown">Earned Income</label>
</div>
<div class="col-sm-9 grid-block">
<div class="row">
<div class="col-sm-6 u-justifyEnd">
<span class="add-margin float-left hand" (click)="addEarnedIncome(i)"><i class=" fa fa-plus-circle fa-lg plusIconColor"></i><span> Add Income</span></span>
</div>
</div>
<div class="row grid-block pd-left amount-grid" formArrayName="earnedIncomes" >
<div class="row-view" *ngFor="let earnedIncome of earnedIncomeCustomer.controls.earnedIncomes.controls; let j = index"
[formGroupName]="j">
<div class="col-sm-5 padding5">
<span class="label-type" *ngIf="j==0">Type</span>
<select class="form-control" formControlName="type" (change)="checkForDuplicateEarnedIncomeType(this.trialBudgetForm.controls.clients.controls[i].controls.earnedIncomes.controls[j].value.type)">
<option value="">Select</option>
<option *ngFor="let optionEntity of this.constantsService.getDropDownFromLookup(this.constantsService.getText('EarningType'))"
value="{{optionEntity.lookupBean.code}}">{{optionEntity.lookupBean.longLabel}}</option>
</select>
</div>
<div class="col-sm-6 pd-left padding5 amount-bg">
<span class="label-type" *ngIf="j==0">Amount</span>
<div class="search-container">
<input (keypress)="this.customValidatorsService.validateNumberOnly($event)" formControlName="amount" class="form-control amount-input search-box"
placeholder="$0.00" currencyMask [options]="{ allowNegative: false,prefix: '$',hundreds: ',' }" maxlength="12"
(keyup)="calculateTotalIncome(earnedIncomeCustomer, i)" />
</div>
<div class="clearfix"></div>
</div>
<div class="col-sm-1 pd-left">
<span class="label-type" *ngIf="j==0"> </span>
<span class="delete-row hand" (click)="deleteIncome(earnedIncomeCustomer, i, j)"> <!-- G.S Income.controls.amount -->
<i class="fa fa-trash-o"></i>
</span>
</div>
</div>
<div class="row-view">
<div class="col-sm-5">
<span class="total-info text-right">Total Earned Income</span>
</div>
<div class="col-sm-6 pd-left amount-bg">
<span class="total-info text-right">${{ earnedIncomeCustomer.totalEarnedIncome | number : '1.2-2'}}
</span>
<div class="clearfix"></div>
</div>
</div>
<div class="row-view">
<div class="col-sm-12 text-center">
<div class="clearfix"></div>
</div>
</div>
</div>
<span *ngIf="duplicateEarnedIncomeType" class="red-font-color">Choose a different use type</span>
</div>
<div class="clearfix"></div>
</div>
<div class="box-border-bottom row-view">
<div class="col-sm-3">
<label class="pushDown">Earned Income Options</label>
</div>
<div class="col-sm-8 grid-block">
<div class="col-sm-5 form-group label-check display-check">
<input type="checkbox" id="check-1" formControlName="thirtyAndOneThirdYN">
<label for="check-1">30+1/3</label>
</div>
<div class="col-sm-6 form-group label-check display-check">
<input type="checkbox" id="check-2" formControlName="thirtyYN">
<label for="check-2">$30</label>
</div>
</div>
<div class="clearfix"></div>
</div>
<div class="box-border-bottom row-view">
<div class="col-sm-3">
<label class="pushDown">Earned Income Expenses</label>
</div>
<div class="col-sm-8 grid-block">
<div class="col-sm-5 form-group padding_right">
<div class="control-label">Type</div>
<select class="form-control" formControlName="earnedIncomeExpenseType">
<option value="-1">Select</option>
<option *ngFor="let optionEntity of this.constantsService.getDropDownFromLookup(this.constantsService.getText('EIexpenseType'))"
value="{{optionEntity.lookupBean.code}}">{{optionEntity.lookupBean.longLabel}}</option>
</select>
</div>
<div class="col-sm-6 padding0 padding_custom">
<div class="control-label">Amount</div>
<input formControlName="earnedIncomeExpense" currencyMask (keypress)="numberOnly($event)" class="form-control" [options]="{ allowNegative: false, prefix: '$',hundreds: ',' }"
placeholder="$0.00" maxlength="10" />
</div>
</div>
<div class="clearfix"></div>
</div>
<div class="row-view">
<div class="col-sm-3">
<label class="pushDown">Dependent Care Expenses</label>
</div>
<div class="col-sm-8 grid-block">
<div class="col-sm-5">
<div class="control-label">Expenses</div>
<input (keypress)="numberOnly($event)" currencyMask formControlName="dependentCareExpense" class="form-control" maxlength="10"
placeholder="$0.00" [options]="{ allowNegative: false, prefix: '$',hundreds: ',' }" />
</div>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
</form>
我一直在努力调试这几天。问题发生在构造函数执行之后和调用ngOnInit之前。只有当我添加了多个客户时才会出现此错误,并返回此页面。 (添加客户不会导致任何问题,只有当我回到此页面时才显示错误this.validator is not a function
。
这很奇怪,因为错误发生在构造函数和ngOnInit之间,而populateData在ngOnInit中调用,这意味着在页面知道我将加载多少客户之前发生错误。但是如果只是因为我添加了多个客户而发生错误,那么应用程序似乎必须在构造函数中获取该信息,其中我还没有加载数据。
更新1:美国东部时间9月25日下午3:39 用populateData函数更新
更新2:完整堆栈错误:9月25日美国东部时间下午4:09
TrialBudgetComponent_Host.html:1 ERROR TypeError: this.validator is not a function
at FormControl.webpackJsonp.../../../forms/@angular/forms.es5.js.AbstractControl._runValidator (forms.es5.js:2645)
at FormControl.webpackJsonp.../../../forms/@angular/forms.es5.js.AbstractControl.updateValueAndValidity (forms.es5.js:2613)
at new FormControl (forms.es5.js:2936)
at FormBuilder.webpackJsonp.../../../forms/@angular/forms.es5.js.FormBuilder.control (forms.es5.js:5780)
at FormBuilder.webpackJsonp.../../../forms/@angular/forms.es5.js.FormBuilder._createControl (forms.es5.js:5822)
at forms.es5.js:5804
at Array.forEach (<anonymous>)
at FormBuilder.webpackJsonp.../../../forms/@angular/forms.es5.js.FormBuilder._reduceControls (forms.es5.js:5803)
at FormBuilder.webpackJsonp.../../../forms/@angular/forms.es5.js.FormBuilder.group (forms.es5.js:5762)
at TrialBudgetComponent.webpackJsonp.../../../../../src/app/trial-budget/trial-budget.component.ts.TrialBudgetComponent.toFromGroup (trial-budget.component.ts:102)
更新3问题已结束:感谢@ yurzui的评论 我刚刚意识到这个架构搞砸了这个页面。此组件的父级在其模板中只有(当前组件选择器)。这两个组件在不同的文件夹下具有完全相同的名称。父母处于/试验预算,儿童处于/试验预算/试验 - 预算 - 主要。 TrialBudgetComponent.toFromGroup实际上是父级内部的一个函数。出于某种原因,它在ngInit中被调用而不是构造函数。仍然无法弄清楚为什么错误只发生在2位客户身上。 但经验教训: 1.仔细阅读错误信息,尤其是第一行和最后几行。我没看过最后一行。 2.总是在构造函数中构造表单并在ngOnInit()
中填充数据