【问题标题】:ability to dynamically add and delete rows in a table in angular reactive forms能够以角度反应形式在表格中动态添加和删除行
【发布时间】:2019-02-19 19:47:20
【问题描述】:

我正在 Angular 7 中创建一个响应式表单,能够动态添加和删除表中的行。

我需要的是

用户点击添加按钮后,我需要将这些控件中输入的值添加到表格中。

表格行应包含这些值以及最后一列中的删除按钮。

我如何使用响应式表单来实现这一点。

目前我的用户界面是这样的

 <form [formGroup]="frmFirm" (ngSubmit)="onSubmit()">
       <div *ngIf="FirmDetails && FirmDetails.Firm" class="card-body scrollClass" style="width:100%">
        <div class="form-group row">
                        <label for="inputEmail" class="col-md-1 col-form-label header">Websites</label>
                        <div class="col-md-3">
                            <div *ngIf="!EditMode">{{FirmDetails.Websites}}</div>
                      </div>
             </div>
        </div>
  </form>

组件

import { Component, Injectable, NgZone, ViewEncapsulation, ViewChild, Input } from '@angular/core';
import { OnInit } from '@angular/core';
import { FirmService } from '../services/firm.service';
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import { CommonDataService} from '../services/common.data.service';
import {FormGroup, FormControl, FormBuilder} from '@angular/forms';


@Component({
    selector: 'mgr-firm',
    templateUrl: './firm.component.html'
})

export class FirmComponent implements OnInit {
    private Error: string;
    public FirmDetails: any;
    public EditMode: boolean;
    public Editor = ClassicEditor;
    public EditorConfig : string;
    public events: string[] = [];
    @Input() FirmId: number;
    DateFoundedDate: Date;
    public frmFirm: FormGroup;

    constructor( private _fb: FormBuilder, private firmService: FirmService, private commonDataService: CommonDataService) {

    }

    ngOnInit() {
        this.getFirmDetails();
        this.initializeFormModel();
    }


    initializeFormModel() {
        this.frmFirm = this._fb.group({
             firmName: [''],
             shortName: [''],
             alternateName: [''],
             dateFounded: [''],
             firmHistory: [''],
             People: [''],

        });
    }

    setFormValues(FirmDetails: any) {
            this.frmFirm.patchValue({
                firmName: FirmDetails.Firm.NAME,
                shortName: FirmDetails.Firm.SHORT_NAME,
                alternateName: FirmDetails.Firm.ALTERNATE_NAME,
                dateFounded: FirmDetails.Firm.DATE_FOUNDED,
                firmHistory: FirmDetails.Firm.HISTORY_HTML,
                People: FirmDetails.People


            });
    }



    getFirmDetails() {
        if (this.FirmId != null) {
        this.firmService.getFirmDetails(this.FirmId)
            .subscribe(data => {
                this.FirmDetails = data;
                this.setFormValues(this.FirmDetails);
            },
            err => {
                this.Error = 'An error has occurred. Please contact BSG';
            },
            () => {
            });
        }
    }

   get  dateFoundedDate(): Date {
         var dateString = this.FirmDetails.Firm.DATE_FOUNDED;
         var seconds = parseInt(dateString.replace(/\/Date\(([0-9]+)[^+]\//i, "$1"));
         var date = new Date(seconds);
         return date;
   }

    saveManager() {
        this.firmService.createFirm(this.FirmDetails)
            .subscribe(data => {
                this.getFirmDetails();
                this.EditMode = !this.EditMode;
            },
            err => {
                this.Error = 'An error has occurred. Please contact BSG';
            },
            () => {
            });
    }




    dateFoundedChanged(dateFoundedDate: Date) {
        this.DateFoundedDate = dateFoundedDate;
    }
}

更新的用户界面

  <div class="form-group row">
                    <label for="inputEmail" class="col-md-1 col-form-label header">Websites</label>
                    <div class="col-md-3">
                        <!-- <div *ngIf="!EditMode">{{FirmDetails.Websites[0].WEBSITE_URL}}</div> -->
                        <!-- <input *ngIf="EditMode" kendoTextBox [readonly]="false" class="form-control"
                             /> -->
                             <div formArrayName="websites"
                             *ngFor="let item of frmFirm.get('websites').controls; let i = index; let last = last">
                             <div [formGroupName]="i">
                               <input formControlName="websiteUrl" placeholder="Website Url">
                               <input formControlName="username" placeholder="User Name">
                               <input formControlName="password" placeholder="Password">

                               <button (click)="removeWebsite()">Remove Website</button>
                               <button (click)="addWebsite()" *ngIf="last">Add Website</button>
                             </div>
                           </div>   
                    </div>
                </div>

更新的组件

 import { Component, Injectable, NgZone, ViewEncapsulation, ViewChild, Input } from '@angular/core';
import { OnInit } from '@angular/core';
import { FirmService } from '../services/firm.service';
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import { CommonDataService} from '../services/common.data.service';
import {FormGroup, FormControl, FormBuilder, FormArray} from '@angular/forms';


@Component({
    selector: 'mgr-firm',
    templateUrl: './firm.component.html'
})

export class FirmComponent implements OnInit {
    private Error: string;
    public FirmDetails: any;
    public EditMode: boolean;
    public Editor = ClassicEditor;
    public EditorConfig : string;
    public events: string[] = [];
    @Input() FirmId: number;
    DateFoundedDate: Date;
    public frmFirm: FormGroup;

    constructor( private _fb: FormBuilder, private firmService: FirmService, private commonDataService: CommonDataService) {

    }

    ngOnInit() {
        this.getFirmDetails();
        this.initializeFormModel();
    }


    initializeFormModel() {
        this.frmFirm = this._fb.group({
             firmName: [''],
             shortName: [''],
             alternateName: [''],
             dateFounded: [''],
             firmHistory: [''],
             People: [''],
             websites: this._fb.array([
                this.createWebsite()
            ])
        });
    }

    // public addWebsite(): void {
    //     const websites = this.frmFirm.get('websites') as FormArray;
    //     websites.push(this._fb.control(''));
    //   }


      public addWebsite(): void {
        this.websites.push(this.createWebsite());
      }

    public removeWebsite(index: number): void {
        const websites = this.frmFirm.get('websites') as FormArray;
        websites.removeAt(index);
      }

      private createWebsite(): FormGroup {
        return this._fb.group({
          websiteUrl: [''],
          username: [''],
          password: ['']
        });
      }


      get websites(): FormArray {
        return <FormArray>this.frmFirm.get('websites');
      }

    setFormValues(FirmDetails: any) {
            this.frmFirm.patchValue({
                firmName: FirmDetails.Firm.NAME,
                shortName: FirmDetails.Firm.SHORT_NAME,
                alternateName: FirmDetails.Firm.ALTERNATE_NAME,
                dateFounded: FirmDetails.Firm.DATE_FOUNDED,
                firmHistory: FirmDetails.Firm.HISTORY_HTML,
                People: FirmDetails.People
            });
            const websiteGroup = this._fb.group({
                  websiteUrl: FirmDetails.Websites[0].WEBSITE_URL,
                  username: FirmDetails.Websites[0].USERNAME,
                  password: FirmDetails.Websites[0].PASSWORD
              });
              this.frmFirm.setControl('websites', this._fb.array([websiteGroup]));
    }



    getFirmDetails() {
        if (this.FirmId != null) {
        this.firmService.getFirmDetails(this.FirmId)
            .subscribe(data => {
                this.FirmDetails = data;
                this.setFormValues(this.FirmDetails);
            },
            err => {
                this.Error = 'An error has occurred. Please contact BSG';
            },
            () => {
            });
        }
    }

   get  dateFoundedDate(): Date {
         var dateString = this.FirmDetails.Firm.DATE_FOUNDED;
         var seconds = parseInt(dateString.replace(/\/Date\(([0-9]+)[^+]\//i, "$1"));
         var date = new Date(seconds);
         return date;
   }

    saveManager() {
        this.firmService.createFirm(this.FirmDetails)
            .subscribe(data => {
                this.getFirmDetails();
                this.EditMode = !this.EditMode;
            },
            err => {
                this.Error = 'An error has occurred. Please contact BSG';
            },
            () => {
            });
    }




    dateFoundedChanged(dateFoundedDate: Date) {
        this.DateFoundedDate = dateFoundedDate;
    }
}

【问题讨论】:

    标签: angular


    【解决方案1】:

    你要找的是FormArray

    Angular 文档通过示例很好地解释了它,这些示例涵盖了您需要添加和删除表单组的用例(在您的情况下,这将是带有公司详细信息的行)。

    https://angular.io/guide/reactive-forms#dynamic-controls-using-form-arrays

    适用于您的情况的示例。

    模板

    <div formArrayName="companies"
      *ngFor="let item of form.get('companies').controls; let i = index; let last = last">
      <div [formGroupName]="i">
        <input formControlName="firmName" placeholder="Firm name">
        <input formControlName="shortName" placeholder="Short name">
        <input formControlName="alternateName" placeholder="Alternate name">
        <input formControlName="dateFounded" placeholder="Founded">
        <input formControlName="firmHistory" placeholder="History">
        <input formControlName="People" placeholder="People">
        <button (click)="removeCompany()">Remove company</button>
        <button (click)="addCompany()" *ngIf="last">Add company</button>
      </div>
    </div>
    

    组件

    ...
    
    public form: FormGroup;
    
    constructor(private formBuilder: FormBuilder) {}
    
    ngOnInit(): void {
      this.form = this.formBuilder.group({
        companies: this.formBuilder.array([ this.createCompany() ])
      });
    }
    
    public addCompany(): void {
      const companies = this.form.get('companies') as FormArray;
      companies.push(this.createItem());
    }
    
    public removeCompany(index: number): void {
      const companies = this.form.get('companies') as FormArray;
      companies.removeAt(index);
    }
    
    private createCompany(): FormGroup {
      return this.formBuilder.group({
        firmName: [''],
        shortName: [''],
        alternateName: [''],
        dateFounded: [''],
        firmHistory: [''],
        People: [''],
      });
    }
    ...
    

    【讨论】:

    • 感谢 Bruno 我现在能够添加但不确定如何在第一次加载表单时绑定控件。我正在将我到目前为止所做的事情添加到帖子中
    • 我通过添加附加信息更新了帖子,即更新的组件和更新的 UI。我目前收到错误找不到路径控制:'网站-> 0->用户名'
    • 如果我取消注释这些行,错误就会消失,但我看不到现有的值绑定
    • 我还注意到添加两次后添加按钮消失。我应该能够添加任意数量的网站。所以我认为删除 ngif 条件应该没问题。 forloop也会改变吗
    • 我在您更新的代码中看到的几个问题是: * 当您向数组添加新项目时,您不是在添加组,而是在添加控制 * websites.push(this._fb.control('')); * 我不是 100% 确定是什么你得到了响应,但是你“补丁”需要匹配你的表单模型->记住websites是一个数组,你正在向它添加一个对象,所以一定要这样做
    【解决方案2】:

    我有类似的东西,但我将它们添加为“块”而不是“行”。单击“添加另一个地址”按钮以添加另一个块。

    我确信格式只是对布局进行一些修改。

    更具挑战性的部分是代码。要拥有可重复的字段集,您需要使用 FormArray,其中每个元素都是其自己的 FormGroup。

    我这里有一个完整的例子:https://github.com/DeborahK/Angular-ReactiveForms/tree/master/Demo-Final

    好吧,之前的答案让我失望了......但我要离开这个,并且链接提供了一个完整的工作示例。

    【讨论】:

    • 谢谢黛博拉。我将看看您的工作解决方案,因为我面临着布鲁诺回复中提到的一些问题。我没有看到下载按钮或 git clone
    • 我希望您的解决方案涵盖了我的问题的答案
    • 你需要在树中上去 git clone:github.com/DeborahK/Angular-ReactiveForms
    猜你喜欢
    • 2020-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-03
    • 1970-01-01
    • 2013-02-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多