我提出了有关动态生成表单控制器的问题,此后我对动态模板生成和控制器生成有一些疑问。
在这个项目中,主要是我在数组中有4种类型的问题。我必须动态生成这些问题
问题类型为
MCQ(只需选择一个答案)
多项选择(用户可以选择多个答案,并且至少需要一个答案)
排名问题(用户必须给出正确的答案顺序。答案应该是唯一的)
这是我的html代码
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header bg-transparent border-success">
<h3>15 questions</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<form [formGroup]="surveyQuestionForm">
<div *ngFor="let question of questions; let i=index">
<div [ngSwitch]="question.qType">
<div *ngSwitchCase="1">
<div class="form-group">
<label class="control-label"> {{question.qNo}})
{{question.question}}</label>
<div class="ml-3">
<table>
<tr *ngFor="let anwr of question.answers; let a=index">
<td>{{a+1}}. {{anwr}} </td>
<td>
<div class="custom-radio custom-control">
<input type="radio" class="custom-control-input"
id="q{{question.qNo}}_{{a}}"
name="q{{question.qNo}}" value="{{a+1}}"
formControlName="q{{question.qNo}}"
[ngClass]="{'is-invalid':surveyQuestionForm.get('q'+ question.qNo).errors && formSubmitted}" />
<label class="custom-control-label"
for="q{{question.qNo}}_{{a}}"></label>
</div>
</td>
</tr>
<div class="text-danger"
*ngIf="surveyQuestionForm.get('q'+ question.qNo).hasError('required') && formSubmitted">
Answer required</div>
</table>
</div>
</div>
</div>
<div *ngSwitchCase="2">
<div class="form-group">
<label class="control-label"> {{question.qNo}})
{{question.question}}</label>
<div class="ml-3">
<table>
<tr *ngFor="let anwr of question.answers; let b=index">
<td>{{b+1}}. {{anwr}} </td>
<td>
<div class="custom-checkbox custom-control">
<input type="checkbox" class="custom-control-input"
id="q{{question.qNo}}_{{b}}" value="{{b+1}}"
formControlName="q{{question.qNo}}"
[ngClass]="{'is-invalid':surveyQuestionForm.get('q'+ question.qNo).errors && formSubmitted}" />
<label class="custom-control-label"
for="q{{question.qNo}}_{{b}}"></label>
</div>
</td>
</tr>
<div class="text-danger"
*ngIf="surveyQuestionForm.get('q'+ question.qNo).hasError('atLeastOneRequired') && formSubmitted">
At least One Answer required</div>
</table>
</div>
</div>
</div>
<div *ngSwitchCase="3">
<div class="form-group">
<label class="control-label"> {{question.qNo}})
{{question.question}}</label>
<div class="ml-3">
<table>
<tr *ngFor="let anwr of question.answers; let a=index">
<td>{{a+1}}. {{anwr}} </td>
<div class="invalid-feedback"
*ngIf="surveyQuestionForm.get('q'+ question.qNo).touched && surveyQuestionForm.get('q'+ question.qNo).hasError('required')">
Answer required</div>
<div class="invalid-feedback"
*ngIf="surveyQuestionForm.get('q'+ question.qNo).touched && surveyQuestionForm.get('q'+ question.qNo).hasError('max')">
max value</div>
<div class="invalid-feedback"
*ngIf="surveyQuestionForm.get('q'+ question.qNo).touched && surveyQuestionForm.get('q'+ question.qNo).hasError('min')">
min value</div>
<div class="invalid-feedback"
*ngIf="surveyQuestionForm.get('q'+ question.qNo).touched && surveyQuestionForm.get('q'+ question.qNo).hasError('notAllUnique')">
Already inserted value</div>
<td>
<input type="number" style="width:40px;"
id="q{{question.qNo}}_{{a}}"
[ngClass]="{'is-invalid': surveyQuestionForm.get('q'+ question.qNo).errors
&& surveyQuestionForm.get('q'+ question.qNo).touched}"
formControlName="q{{question.qNo}}"
class="text-center" />
</td>
</tr>
</table>
</div>
</div>
</div>
<div *ngSwitchCase="4">
<div class="form-group">
<label class="control-label"> {{question.qNo}})
{{question.question}}</label>
<div class="ml-3">
<table>
<th width="auto"></th>
<th></th>
<tr>
<td><textarea class="form-control" rows="5" id="comment" name="text"
[ngClass]="{'is-invalid':surveyQuestionForm.get('q'+ question.qNo).errors &&
surveyQuestionForm.get('q'+ question.qNo).touched}"
formControlName="q{{question.qNo}}"></textarea></td>
</tr>
<div class="text-danger"
*ngIf="surveyQuestionForm.get('q'+ question.qNo).hasError('required') && formSubmitted">
Answer required</div>
</table>
</div>
</div>
</div>
<div *ngSwitchCaseDefault></div>
</div>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12">
<button (click)="onSubmit()" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
这是我的打字稿代码
surveyQuestionForm: FormGroup;
formSubmitted = false;
constructor(private fb: FormBuilder) { }
questions: any = [
{
id: 11,
surveyNo: 5,
qNo: 1,
question: 'What is the country you would like to travel?',
qType: 1,
noAnswrs: 4,
answerType: 1,
answers: ['America', 'Australia', 'India', 'England']
},
{
id: 12,
surveyNo: 5,
qNo: 2,
question: 'What type of credit cards do you have?',
qType: 2,
noAnswrs: 4,
answerType: 1,
answers: ['Visa', 'Mastercard', 'American Express', 'Discover']
},
{
id: 13,
surveyNo: 5,
qNo: 3,
question: 'Please rank the following features in order of importance,where 1 is the most important to you.?',
qType: 3,
noAnswrs: 4,
answerType: 1,
answers: ['Location', 'Confort', 'Service', 'Value for money']
},
{
id: 14,
surveyNo: 5,
qNo: 4,
question: 'What is your idea about our institute?',
qType: 4,
noAnswrs: 0,
answerType: 1,
answers: []
}
];
ngOnInit() {
this.createForms();
}
createForms(): any {
this.surveyQuestionForm = this.fb.group(
this.questions.reduce((group: any, question: { qNo: string; }) => {
return Object.assign(group, { ['q' + question.qNo]: this.buildSubGroup(question) });
}, {})
);
}
private buildSubGroup(question) {
switch (question.qType) {
case 1:
return [Validators.required];
case 2:
return this.fb.group(
question.answers.reduce((subGroup, answer) => {
return Object.assign(subGroup, { [answer]: [false] });
}, {}), { validators: [this.atLeastOneRequired()] }
);
case 3:
return this.fb.group(
question.answers.reduce((subGroup, answer) => {
return Object.assign(subGroup, { [answer]: ['', [Validators.required, Validators.min(1), Validators.max(3)]] });
}, {}), { validators: [this.uniqueNumbersValidator()] }
);
case 4:
return this.fb.group({ answer: ['', [Validators.required]] });
default:
throw new Error('unhandled question type');
}
}
atLeastOneRequired() {
return (ctrl: AbstractControl) => {
const fg = ctrl as FormGroup;
const atLeastOneTrue = Object.values(fg.controls).some(fc => !!fc.value);
return (atLeastOneTrue) ? null : { atLeastOneRequired: true };
};
}
uniqueNumbersValidator() {
return (ctrl: AbstractControl) => {
const fg = ctrl as FormGroup;
let allUnique = true;
const values = [];
Object.values(fg.controls).forEach(fc => {
const val = fc.value;
if (val && allUnique) {
if (values.includes(val) && allUnique) {
allUnique = false;
}
values.push(val);
}
});
return (allUnique) ? null : { notAllUnique: true };
};
}
onSubmit() {
this.formSubmitted = true;
console.log(this.formSubmitted);
}
我可以看到此错误,
control.registerOnChange不是函数
这是stackblitz链接 https://stackblitz.com/edit/angular-nya7l9
请帮助我解决此问题。
谢谢
答案 0 :(得分:1)
首先,与您的广播建立和绑定有关:
像这样构建它:
case 1:
case 4:
return this.fb.group({ answer: ['', [Validators.required]] });
并这样绑定:
<div class="form-group" formGroupName="{{'q' + question.qNo}}">
<label class="control-label"> {{question.qNo}})
{{question.question}}</label>
<div class="ml-3">
<table>
<tr *ngFor="let anwr of question.answers; let a=index">
<td>{{a+1}}. {{anwr}} </td>
<td>
<div class="custom-radio custom-control">
<input type="radio" class="custom-control-input"
id="q{{question.qNo}}_{{a}}"
name="answer" value="{{a+1}}"
formControlName="answer"
[ngClass]="{'is-invalid':surveyQuestionForm.get('q'+ question.qNo).errors && formSubmitted}" />
<label class="custom-control-label"
for="q{{question.qNo}}_{{a}}"></label>
</div>
</td>
</tr>
<div class="text-danger"
*ngIf="surveyQuestionForm.get('q'+ question.qNo).hasError('required') && formSubmitted">
Answer required</div>
</table>
</div>
</div>
首先使用formGroupName指令,然后静态访问答案控件。
下一步您的复选框绑定:
<div class="form-group" formGroupName="{{'q' + question.qNo}}">
<label class="control-label"> {{question.qNo}})
{{question.question}}</label>
<div class="ml-3">
<table>
<tr *ngFor="let anwr of question.answers; let b=index">
<td>{{b+1}}. {{anwr}} </td>
<td>
<div class="custom-checkbox custom-control">
<input type="checkbox" class="custom-control-input"
id="q{{question.qNo}}_{{b}}" value="{{b+1}}"
formControlName="{{anwr}}"
[ngClass]="{'is-invalid':surveyQuestionForm.get('q'+ question.qNo).errors && formSubmitted}" />
<label class="custom-control-label"
for="q{{question.qNo}}_{{b}}"></label>
</div>
</td>
</tr>
<div class="text-danger"
*ngIf="surveyQuestionForm.get('q'+ question.qNo).hasError('atLeastOneRequired') && formSubmitted">
At least One Answer required</div>
</table>
</div>
</div>
再次使用formGroupName指令,因为您的整体表单只是一组组,然后在其中,您的formControlNames就是答案本身。
现在您的多项选择绑定,通常存在相同的问题:
<div class="form-group" formGroupName="{{'q' + question.qNo}}">
和
<input type="number" style="width:40px;"
id="q{{question.qNo}}_{{a}}"
[ngClass]="{'is-invalid': surveyQuestionForm.get('q'+ question.qNo).errors
&& surveyQuestionForm.get('q'+ question.qNo).touched}"
formControlName="{{anwr}}"
class="text-center" />
最后,您的自由文本答案(基本上是相同的问题)需要formGroupName指令,并正确绑定到其中的静态答案控件:
<td><textarea class="form-control" rows="5" id="comment" name="text"
[ngClass]="{'is-invalid':surveyQuestionForm.get('q'+ question.qNo).errors &&
surveyQuestionForm.get('q'+ question.qNo).touched}"
formControlName="answer"></textarea></td>
固定的闪电战:
https://stackblitz.com/edit/angular-d4p6ly?file=src/app/app.component.html
答案 1 :(得分:0)
您有嵌套的表单组,因此应在模板中向其中添加表单组名。我不知道它是否是故意的,您可以在控制台上看到它。我改变了你的堆叠闪电战。