如何使用来自 http 请求的数组将动态输入字段添加到 angular 表单

How to add dynamic input fields to angular form using array from http request

我已经尝试了所有方法并检查了相关的Whosebug问题,但没有任何效果所以在这里问。我必须创建 ui,在下拉列表的 select 上进行 api 调用并以数组形式接收数据。基于这个数组,我想在我的表单中添加与数组中元素数量一样多的字段。我尝试使用 FormArray 但它没有用。因为我不断收到与无法找到具有指定名称 'name' 的控件相关的错误。我尝试使用使 formcontrol 字段动态化,但仍然出现相同的错误。

这是我试过的代码。

.ts 文件

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormControl, FormArray } from '@angular/forms';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-calculator',
  templateUrl: './calculator.component.html',
  styleUrls: ['./calculator.component.scss']
})
export class CalculatorComponent implements OnInit {

  title = 'Formula Calculation Page';
  configUrlProducts = 'http://localhost:3000/data/products';
  configUrlProductFieldsData = 'http://localhost:3000/data/getproductfields/';

  angForm: FormGroup;
  ruleset_data=[];
  ruleset_data_length=0;
  products: any = [];
  selected_product_id;
  syntax_error=false
  show_form=false;
  arr=[{'name':''},{'class':''},{'tree':''}];
  temp;

 //  products=[{"id":1,"name":'Product 1'},{"id":2,"name":'Product 2'},{"id":3,"name":'Product 3'}]
  constructor(private fb: FormBuilder,private http: HttpClient) {
   console.log("inside constructor")
  //  this.createForm();
   // this.fetch_products();
  }
     // convenience getters for easy access to form fields
     get f() { return this.angForm.controls; }
     get t() { return this.f.fields as FormArray; }


 ngOnInit() {
   console.log("inside ngonit");
  this.angForm = this.fb.group({
    fields: new FormArray([])
  });

  for(var i=0;i<3;i++){
    this.temp=this.arr[i];
    this.t.push(this.fb.group({'name':''}))
  }

  // this.arr.forEach(item=>{
  //   this.t.push(this.fb.group(item))
  // })
  console.log(this.angForm)
   this.getProducts();
 }

 getProducts() {
   console.log("inside get products");
   this.products = [];
   this.http.get(this.configUrlProducts).subscribe((res)=>{
     console.log(res);
     this.products = res["data"];
   });
 }


 fetch_ruleset_data(value: string){
   this.selected_product_id=value;
   console.log("inside get rule data");
   this.ruleset_data = [];
   this.http.get(this.configUrlProductFieldsData+value).subscribe((res)=>{
     console.log(res);
     this.ruleset_data = res["data"];

     this.show_form=true;
      console.log(this.angForm);
     this.ruleset_data_length=res["data"].length;
   });
 }

 onSubmit(value){
   console.log(value);
  //  var data=value;
  //  data["no_of_variables"]=this.ruleset_data_length;
  //  data["product_id"]=this.selected_product_id;
  //  // value=value+this.ruleset_data_length;
  //  // var formula=JSON.parse(value);
  //  console.log("final data before sending to api",data);
  //  return this.http.post('http://localhost:3000/formula/addformula', data)
  //  .subscribe((res)=>{
  //    console.log(res);
  //  })
 }

 validateFormula=(c: FormControl)=> {
   console.log(c.value,typeof c.value);
   var data={};
   data['formula']=c.value;
   data["no_of_variables"]=this.ruleset_data_length;
   data["product_id"]=this.selected_product_id;

   return this.http.post('http://localhost:3000/formula/validateformula', data)
   .subscribe((res)=>{
     // console.log("inside validation after api call",res)
     // console.log(res["error"])
     if(res["error"]){
       console.log("inside error block",res);
       this.syntax_error=true;
       // this.angForm.setErrors({ 'invalid': true });
       // return true;

       return {
         validateFormula: {
           valid: true
         }
       }
     }else{
       this.syntax_error=false;
       // this.angForm.setErrors({ 'invalid': false });
       // return true;
       return {
         validateFormula: {
           valid: true
         }
       }
     }
   })

   // let EMAIL_REGEXP;

   // return EMAIL_REGEXP.test(c.value) ? null : {
   //   validateEmail: {
   //     valid: false
   //   }
   // };
 }


 // fetch_ruleset_data(value: string){
 //   console.log(value);
 // }




}

.html 文件

<!-- app.component.html -->

<div class="container">
        <h1>
          Welcome to {{title}}!!
        </h1>

        <select class="custom-select" (change)="fetch_ruleset_data($event.target.value)">
                <option value="" disabled>Choose your product</option>
                <option *ngFor="let product of products; let i = index" [value]="product.id">
                  {{product.name}}
                </option>
        </select>



        <div *ngIf="show_form==true">
        <form [formGroup]="angForm" (ngSubmit)="onSubmit(angForm.value)">
                <div *ngFor="let item of t.controls; let i = index">
                  <div [formGroup]="item">
                    <input [formControlName]='item'>
                  </div>
                </div>
                <button type="submit">Submit</button>
        </form>


        </div>
        <br />

</div>

过去 2 天一直在努力解决这个问题,非常感谢您的帮助。我是 angular 的新手,目前正在学习它可能是一件简单的事情,但我目前无法弄清楚,需要您的帮助。

我不知道你收到的数据想象一下你收到的数据,你的例子显示有点像

[{'name':''},{'class':''},{'tree':''}];

真是一个糟糕的数据。更 "natural" 收到一些赞

[{col:'name',value:''},{col:'class',value:''},{col:'tree',value:''}];

但我想是另一个问题。 "key" 是在收到数据时创建一个 FormGroup 并且(在您的情况下,创建一个具有字段名称的数组)遍历数组

   this.http.get(this.configUrlProducts).subscribe((res:any[])=>{
     this.formGroup=new FormGroup({}) //<--create a formGroup empty
     this.fields=[] //<--create an array empty
     res.forEach(x=>{
       const key=Object.keys(x)[0]
       const value=x[key]
       this.formGroup.addControl(key,new FormControl(value))
       this.fields.push(key)
     })
   });

所以,你的 .html 可以像

<form [formGroup]="formGroup">
   <div *ngFor="let col of fields">
       <input [formControlName]="col">
   </div>
</form>

如果您收到的数组类似于 "my natural" 方式,函数和 .html 将更改为

   this.http.get(this.configUrlProducts).subscribe((res:any[])=>{
     this.formGroup=new FormGroup({}) //<--create a formGroup empty
     res.forEach(x=>{
       const key=x.col
       const value=x.value
       this.formGroup.addControl(key,new FormControl(value))
     })
     this.fields=res;
   });

<form [formGroup]="formGroup">
   <div *ngFor="let field of fields">
       <input [formControlName]="field.col">
   </div>
</form>

如果你收到一个包含名称、值、验证器、标签的对象数组,这个想法可以扩展......因为你可以使用,例如{{field.label}} 显示标签

如果您收到 ['a','b','c'],您可以使用

   this.http.get(this.configUrlProducts).subscribe((res:any[])=>{
     this.formGroup=new FormGroup({}) //<--create a formGroup empty
     res.forEach(x=>{
       this.formGroup.addControl(x,new FormControl(''))
     })
     this.fields=res;
   });

<form [formGroup]="formGroup">
   <div *ngFor="let field of fields">
       <input [formControlName]="field">
   </div>
</form>

您可以使用 FormArrayFormGroup 来获得想要的结果。

可以在 stackblitz 查看示例。

HTML:

<form [formGroup]="myForm">
    <ng-container *ngFor="let group of myForm.controls |keyvalue">

    <div [formGroup]="group.value">        
        <button type="button" (click)="onAddProduct(group.value)">Add Product</button>
        <div formArrayName="productList">
            <div *ngFor="let item of productArray(group.value).controls; let i = index">
                  <label for="">Your row</label>
                  <input [formControlName]="i">
                  <button (click)="removeProduct(group.value,i)">remove</button>
            </div>
        </div> 
    </div>
<pre>
{{myForm?.value|json}}
</pre>
    </ng-container>
</form>

打字稿:

name = 'Angular';
public myForm: FormGroup;

ngOnInit() {
    this.myForm = new FormGroup({});
    for(let item of ['item1']) {
        this.myForm.addControl(item,
            new FormGroup({
                name: new FormControl(),
                productList: new FormArray([])
            })
         )
     }  
  } 

onAddProduct(group:FormGroup) {
    (group.get('productList') as FormArray).push(new FormControl())
}

productArray(group:FormGroup):FormArray
{
    return group.get('productList') as FormArray;
}

removeProduct(group:FormGroup,index:number)
{
    (group.get('productList') as FormArray).removeAt(index)
}