Angular Dynamic Forms, Async does not work. ERROR

2019-06-13 03:34发布

问题:

I'm trying to generate form with data from api, I did everithing how it is writen in documentation: https://angular.io/guide/dynamic-form

andit works with array of question, but when questions coming from api it does'n work, I get this error. I changed a code according to this answer: https://stackoverflow.com/a/48391041 but it still doesn't work. Can you help me out ? :)

DynamicFormQuestionComponent.html:9 ERROR Error: Cannot find control 
    with name: 'previousMessage'

DynamicFormQuestionComponent.html:9 ERROR CONTEXT 
     DebugContext_ {view: {…}, nodeIndex: 3, nodeDef: {…}, elDef: {…}, elView: {…}}

question.component.html

<app-dynamic-form [questions]="(questions | async)"></app-dynamic-form>

question.component.ts

import { Component, OnInit } from '@angular/core';
import { QuestionService } from '../../question/question.service';
import { QuestionBase } from '../../question/question-base';
import { Observable } from 'rxjs/Observable';

@Component({
  selector: 'app-qna',
  templateUrl: './qna.component.html',
  styleUrls: ['./qna.component.scss'],
  providers: [QuestionService]
})
export class QnaComponent implements OnInit {
  questions: Observable<any>;

  constructor(private questionService: QuestionService) {
    this.questions = this.questionService.getQuest()
  }

  ngOnInit() {
  }
}

dynamic-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: 'app-dynamic-form',
  templateUrl: './dynamic-form.component.html',
  styleUrls: ['./dynamic-form.component.scss'],
  providers: [QuestionControlService]
})
export class DynamicFormComponent implements OnInit {
  private _questions = [];
  @Input()
  set questions(value: any[]) {
    this._questions = value || [];
  }

  get questions(): any[] {
    return this._questions;
  }
  form: FormGroup;
  payLoad = '';

  constructor(private qcs: QuestionControlService) { }

  ngOnInit() {
    this.form = this.qcs.toFormGroup(this.questions);
  }

  onSubmit() {
    this.payLoad = JSON.stringify(this.form.value);
  }

}

dynamic-form-question.component.html

<div class="form-group" [formGroup]="form">
  <label [attr.for]="question.key">{{question.label}}</label>

  <div [ngSwitch]="question.controlType">

    <input class="form-control" *ngSwitchCase="'textbox'" [formControlName]="question.key"
            [id]="question.key" [type]="question.type">

    <select size="2" class="form-control" [id]="question.key" *ngSwitchCase="'dropdown'" [formControlName]="question.key">
      <option *ngFor="let opt of question.options" [value]="opt.key">{{opt.value}}</option>
    </select>

    <input class="form-control" *ngSwitchCase="'date'" [formControlName]="question.key"
            [id]="question.key" [type]="question.type">


    <input class="form-control" *ngSwitchCase="'number'" [formControlName]="question.key"
            [id]="question.key" [type]="question.type">

    <textarea class="form-control" *ngSwitchCase="'textarea'" [formControlName]="question.key"
            [id]="question.key"></textarea>

  </div> 

  <div class="errorMessage" *ngIf="!isValid">{{question.label}} is required</div>
</div>

dynamic-form-question.component.ts

import { Component, Input, OnInit } from '@angular/core';
import { FormGroup }        from '@angular/forms';
import { QuestionBase }     from '../question-base';

@Component({
    selector: 'app-dynamic-form-question',
  templateUrl: './dynamic-form-question.component.html',
  styleUrls: ['./dynamic-form-question.component.scss']
})
export class DynamicFormQuestionComponent {
  @Input() question: QuestionBase<any>;
  @Input() form: FormGroup;
  get isValid() { return this.form.controls[this.question.key] 
    ? this.form.controls[this.question.key].valid : true; }
}

question.service.ts

import { Injectable } from '@angular/core';
import { QuestionDropdown } from './question-dropdown';
import { QuestionBase }     from './question-base';
import { QuestionTextbox }  from './question-textbox';
import { QuestionDate }  from './question-date';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { catchError, map, tap } from 'rxjs/operators';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';

@Injectable()
export class QuestionService {
  url = '';
  questions: QuestionBase<any>[];

  constructor(private http: HttpClient) { }

  getQuest(): Observable<QuestionBase<any>[]>{
      this.url = `http://localhost:3000/`;
      return this.http.get(this.url, { withCredentials: true })
      .map((questionMetadata) => this.metadataToQuestions(questionMetadata))
      .map(questions => questions.sort((a, b) => a.order - b.order))
    }

  private metadataToQuestions(questionMetadata) {
    console.log(questionMetadata['QnA'])
    return questionMetadata['QnA'].map(this.toQuestion)
  }

  private toQuestion(elementData) {
    let inputs = {
    'textarea': QuestionTextbox,
    'date': QuestionDate,
    'dropdown': QuestionDropdown,
    'number': QuestionTextbox,
    'textbox': QuestionTextbox
  }
    let element = elementData.type
    //that works :)
    return new (inputs[element])({...elementData})
  }