Angular 6 动态表单问题

Angular 6 Dynamic Forms issue

我正在尝试按照官方文档掌握动态表单,但我自己的情况。

我有 api 试图从 https://demo7782009.mockable.io/form 获取数据并在相应的表单字段中打印出来。 每个字段都有自己的验证要求。

但是出现错误

Error: formGroup expects a FormGroup instance. Please pass one in.

   Example:


<div [formGroup]="myGroup">
  <input formControlName="firstName">
</div>

In your class:

this.myGroup = new FormGroup({
   firstName: new FormControl()
});

我尝试用我的应用制作 plunker,但没有 bootstrap

这是我的代码(抱歉)

App.component.ts

import { FetchFormService } from './fetch-form.service';
import { QuestionControlService } from './question-control.service';
import { Component, OnInit } from '@angular/core';
import { QuestionBase } from './Models/question-base';
import { FormGroup } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  questions: QuestionBase<any>[];
  form: FormGroup;

  constructor(
    private qcs: QuestionControlService,
    private ffs: FetchFormService
  ) {
  }

  ngOnInit() {
    this.ffs.getData().subscribe(data => {
      this.questions = data.data;
      let formQuestions: [QuestionBase<any>];
      this.questions.forEach(e => {
        formQuestions.push(new QuestionBase(e));
      });
      this.form = this.qcs.toFormGroup(formQuestions);
    });
  }

}

App.component.html

<form [formGroup]="form">
  <div *ngFor="let question of questions; let i = index">
    <app-question-item [question]="question" [index]='i' [form]="form"></app-question-item>
  </div>

  <button [disabled]="!form.valid">Submit</button>
</form>

问题基础模型

export class QuestionBase<T> {
  value: T;
  values?: string[];
  label: string;
  type: string;

  constructor(options: {
    value: T,
    values?: string[],
    label: string,
    type: string
  }) {
    this.value = options.value;
    this.label = options.label;
    this.type = options.type;
    this.values = options.values;
  }
}

问题-item.component.ts

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

@Component({
  selector: 'app-question-item',
  templateUrl: './question-item.component.html',
  styleUrls: ['./question-item.component.css']
})
export class QuestionItemComponent implements OnInit {

  @Input() question: QuestionBase<any>;
  @Input() form: FormGroup;
  @Input() index;

  constructor() { }

  ngOnInit() {
  }

}

问题-item.component.html

<div [formGroup]="form">
  <label>
    {{ question.label }}

    <ng-container [ngSwitch]="question.type">
      <input [formControlName]="'field' + index" *ngSwitchCase="'string' || 'integer' || 'double'" type="text">

      <textarea [formControlName]="'field' + index" *ngSwitchCase="'text'" cols="30" rows="10"></textarea>

      <ul *ngSwitchCase="'list'">
        <li *ngFor="let item of question.values; let subindex = index">
          <input [formControlName]="'field' + index + subindex" type="text">
        </li>
      </ul>
    </ng-container>

  </label>
</div>

获取-form.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

interface Form {
  data: {
    type: string,
    label: string,
    value: string,
    values?: string[]
  }[];
}

@Injectable({
  providedIn: 'root'
})
export class FetchFormService {

  constructor(
    private http: HttpClient
  ) { }

  getData() {
    return this.http.get<Form>('https://demo7782009.mockable.io/form');
  }
}

问题-control.service.ts

import { QuestionBase } from './Models/question-base';
import { Injectable } from '@angular/core';
import { FormControl, Validators, FormGroup } from '@angular/forms';

@Injectable({
  providedIn: 'root'
})
export class QuestionControlService {
  constructor() { }

  toFormGroup(questsions: QuestionBase<any>[]) {
    let group: any = {};

    questsions.forEach((e, i) => {
      if (e.type === 'string' || e.type === 'text' || e.type === 'list') {
        group[i] = new FormControl(e.value, Validators.required);
      } else if (e.type === 'integer') {
        group[i] = new FormControl(e.value, [Validators.required, Validators.pattern(/\d+/)]);
      } else if (e.type === 'double') {
        group[i] = new FormControl(e.value, [Validators.required, Validators.pattern(/\d+\.\d+/)]);
      } else {}
    });

    return new FormGroup(group);
  }
}

App.module.ts

import { QuestionItemComponent } from './question-item/question-item.component';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
    QuestionItemComponent
  ],
  imports: [
    BrowserModule,
    ReactiveFormsModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

这里是这个问题的延续

您宣布成立

form: FormGroup;

但是忘记实例化了。你做到了

this.form = this.qcs.toFormGroup(...);

但是您正在等待 HTTP 调用完成,这意味着在调用完成之前它是未定义的。

考虑调节您的 HTML :

<form [formGroup]="form" *ngIf="form">

顺便说一下,对于沙箱,考虑使用 http://stackblitz.io

你的错误很常见,与动态表单无关。当您初始化组件时,将加载 html 模板。在此之前,您的 ngOnInit 已被触发,但它仍在等待后端的响应。因此,您的 "form" 变量未定义。因为您的模板正在加载 [formGroup]="undefined",所以您会收到该错误。

<form [formGroup]="form" *ngIf="form">

ngIf 解决了这个问题,因为它会确保您的表单模板在您确定表单组已创建之前不会尝试加载。