我正在尝试在Angular 2中创建动态表单,我正在使用Angular cookbook here中的设置作为我的起点。我的设置没有任何问题,只是将服务中的数据硬编码为api调用。我的问题是,当我尝试使用api调用时,值似乎没有正确设置。
在Angular食谱中,他们将question.service.ts文件硬编码为:
getQuestions() {
let questions: QuestionBase<any>[] = [
new DropdownQuestion({
key: 'brave',
label: 'Bravery Rating',
options: [
{key: 'solid', value: 'Solid'},
{key: 'great', value: 'Great'},
{key: 'good', value: 'Good'},
{key: 'unproven', value: 'Unproven'}
],
order: 3
}),
new TextboxQuestion({
key: 'firstName',
label: 'First name',
value: 'Bombasto',
required: true,
order: 1
}),
new TextboxQuestion({
key: 'emailAddress',
label: 'Email',
type: 'email',
order: 2
})
];
return questions.sort((a, b) => a.order - b.order);
}
}
然后从app.component.ts文件中简单地从构造函数调用:
constructor(service: QuestionService) {
this.questions = service.getQuestions();
}
哪些&#34;问题&#34;然后在app.component.ts模板中绑定到这个
<dynamic-form [questions]="questions"></dynamic-form>
我对question.service.ts进行了更改以进行api调用(现在来自json文件,因为我无法在家中访问api)
getFirstQuestion() {
return this._http.get(this.__questionJSONApiBaseUrl)
.map(data => data.json())
.do(data => console.log('All: ' + JSON.stringify(data)))
.catch(this.handleError);
}
从app.component.ts调用
ngOnInit() {
this.service.getFirstQuestion()
.subscribe(data => {
this.data = data;
if (data.Type == 'TextBox') {
let questions: QuestionBase<any>[] = [
new TextboxQuestion({
key: data.Title,
label: data.Text,
value: '',
required: true,
order: data.Id
})];
}
}
);
}
如您所见,我在.subscribe()中设置了属性,但它似乎没有正常工作,因为当它绑定到模板中的[问题]时,我得到了一个&#34;无法读取财产&#39; forEach&#39;未定义&#34;来自question-control.service文件的错误,该文件将问题转换为FormGroup。
我知道数据正在进入,因为我可以在if语句中设置警报并成功查看api调用中的数据。我相信我的问题是[问题]在数据准备好之前具有约束力。有人可以告诉我一个更好的方法来做到这一点,或者请提出任何建议,我做错了吗?有没有办法可以先在api中设置属性?
答案 0 :(得分:1)
正如@silentsod指出的那样,这里的问题是你正在进行异步操作,并尝试将其存储为你的问题。您需要处理异步。
您可以采用以下两种方式......或者从组件类中执行:
while read -r branch_line; do
或者,您可以从模板中使用异步管道:
service.getQuestions((questions) => this.questions = questions);
async
pipe订阅了observable,在本例中为<dynamic-form [form]="questions | async"></dynamic-form>
,并返回该值。
答案 1 :(得分:0)
我能够使用this示例中的想法进行操作,并根据我的需要进行修改。
我现在设置的方式是加载页面,用户必须单击开始按钮才能获得第一个问题。所以我不再在ngOnInt中调用我的第一个问题了,但是在我的按钮内单击事件方法就像这样:
getFirstQuestion() {
//hide the begin survey button once clicked
this.clicked = true;
this.service.getQuestion()
.subscribe(
q => {
this.questions = q
});
}
我的服务中的getQuestion()如下所示:
getQuestion() {
return this.http.get(this._questionApiBaseUrl + 'getFirstQuestion' + '?' 'questionId=' + this.questionId)
.map(res => [res.json()])
.map(data => {
this.data = data;
return this.buildQuestions(data);
})
}
返回此buildQuestions()方法:
buildQuestions(data: any[]) {
let questions: any[] = [];
data.forEach((item: QuestionsGroup) => {
console.log("my item control in buildQuestion: " + item.controlType);
if (item.controlType == 'group') {
let group = new QUESTION_MODELS[item.controlType](item);
group.questions = this.buildQuestions(item.questions);
questions.push(group);
}
else if (item.controlType == 'RadioButtons') {
item.controlType = 'radio';
questions.push(new QUESTION_MODELS[item.controlType](item));
}
else if (item.controlType == 'TextBox'){
item.controlType = 'textbox';
item.type = 'text'
questions.push(new QUESTION_MODELS[item.controlType](item));
}
else if (item.controlType == 'Datepicker') {
item.controlType = 'textbox';
item.type = 'date'
questions.push(new QUESTION_MODELS[item.controlType](item));
}
//TODO add any remaining question types
else {
questions.push(new QUESTION_MODELS[item.controlType](item));
}
});
return questions;
}
上面的buildQuestions()方法稍后会被重构,因为当前存在从api到客户端的属性值不匹配。
我之前尝试做事的方式是在从我的通话中返回数据后操纵数据,但是对“questons”的绑定已经发生了。现在,当我订阅它时,从http get调用返回“问题”时,它已经以正确的格式返回。从这里开始,用户将根据他们的答案动态提交答案并获得另一个问题。
以下是我发布答案并提出下一个问题的服务方法(不得不留下一些细节,但你明白了):
submitAnswer(answer: any) {
//Interface to formulate my answer
var questionAnswer: IAnswer = <any>{};
//set answer properties interface here
let headers = new Headers({ 'Content-Type': 'application/json; charset=utf-8' });
let options = new RequestOptions({ headers: headers });
let myAnswerObj = JSON.stringify(questionAnswer);
return this.http.post(this._questionApiBaseUrl + 'submitanswer', myAnswerObj, options)
.map(res => [res.json()])
.map(data => {
this.data = data;
//return the next question to the user
return this.buildQuestions(data);
});
}
答案 2 :(得分:0)
dynamic-form.component.ts 的 ngOnInit()函数在我们收到来自api的响应之前运行。
下面是文件
<强>动态form.component.ts 强>
import { Component, Input, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { QuestionBase } from './question-base';
import { QuestionControlService } from './question-control.service';
@Component({
selector: 'dynamic-form',
templateUrl: './dynamic-form.component.html',
providers: [ QuestionControlService ]
})
export class DynamicFormComponent implements OnInit {
@Input() questions: QuestionBase<any>[] = [];
form: FormGroup;
payLoad = '';
constructor(private qcs: QuestionControlService) { }
ngOnInit() {
this.form = this.qcs.toFormGroup(this.questions);
}
onSubmit() {
this.payLoad = JSON.stringify(this.form.value);
}
}
&#13;