【问题标题】:Angular 6 Dynamic Forms issueAngular 6 动态表单问题
【发布时间】:2019-01-26 21:00:36
【问题描述】:

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

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

但出现错误

错误:formGroup 需要一个 FormGroup 实例。请传一个。

   Example:


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

In your class:

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

我试图用我的应用程序创建一个plunker,但它不能引导

所以这是我的代码(抱歉)

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>

QuestionBase 模型

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;
  }
}

Question-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() {
  }

}

Question-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>

Fetch-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');
  }
}

Question-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 { }

这个问题的继续在这里

Angular 6 Dynamic Forms issue [part 2]

【问题讨论】:

    标签: angular typescript dynamic-forms


    【解决方案1】:

    你宣布你形成了

    form: FormGroup;
    

    但忘了实例化它。你这样做了

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

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

    考虑调整您的 HTML:

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

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

    【讨论】:

    • 谢谢。这解决了一个问题,但我还有另一个问题。 TypeError: undefined is not an object (evaluating 'formQuestions.push') 虽然不应该是这样,因为它是在分配接收到的数据后放入订阅中的
    • 同样的问题:你声明它let formQuestions: [QuestionBase&lt;any&gt;],但不要实例化它let formQuestions: [QuestionBase&lt;any&gt;] = [];
    • 我犯了这么简单的错误......认为声明类型就足够了......现在它打印出一个错误的字段Cannot find control with name: 'field0'(我在HTML模板中生成它们,因为我没有'由于每次的字段数量不同,因此没有任何字段名称
    • 你不应该。与你解决的问题相比,这是一个相当高级的问题,我建议你为它提出一个新问题,以免它污染这个问题。
    • 好的。谢谢您的帮助。与 plunker 相比,Stackblitz 看起来更高级。我会尝试将代码放在那里。
    【解决方案2】:

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

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

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

    【讨论】:

    • 如果您的答案与页面上的答案相同,并且没有更多内容,请考虑对上述答案进行投票,而不是重复。这将通过减少答案数量并显示最受好评的答案来帮助未来的用户。
    猜你喜欢
    • 2019-07-06
    • 2019-03-17
    • 2019-06-12
    • 1970-01-01
    • 2019-04-04
    • 1970-01-01
    • 1970-01-01
    • 2018-06-09
    • 2017-01-11
    相关资源
    最近更新 更多