如何在Angular 2中动态添加和删除表单字段

时间:2016-06-24 06:50:30

标签: angular

我尝试在用户点击添加按钮时动态添加输入字段,并且每个表单字段必须有一个删除按钮,当用户点击表单字段必须删除时,我需要实现此目的使用Angular 2,因为我是Angular 2的新手,请帮我完成它

我尝试了什么

我已经创建了一组字段(3个选择框和1个文本框),创建了一个名为add fields的按钮,但是我已经在角度1.x中尝试了它的工作正常但是角度为2我不会知道如何完成它,这是我完整工作的link

app/app.component.ts
 import {
    Component
  }
from '@angular/core';
  @Component({
    selector: 'my-app',
    template: `
    <h1>{{title}}</h1>
    <div class="container">
    <button class="btn btn-success bt-sm">add</button>
    <form role="form" calss="form-inline">
    <div class="form-group col-xs-3">
    <label>Select State:</label>
    <select class="form-control" [(ngModel)]="rules.State" id="sel1">
            <option>State1</option>
            <option>State2</option>
            <option>State3</option>
            <option>State4</option>
</select>
     </div>
    <div class="form-group col-xs-3">
<label>Rule:</label>
     <input type="text" data-toggle="modal" data-target="#myModal" class="form-                   control">
    </div>
<div class="form-group col-xs-3">
<label>Pass State :</label>
    <select class="form-control" [(ngModel)]="rules.pass">
    <option>State1</option>
    <option>State2</option>
    <option>State3</option>
    <option>State4</option>
</select>
 </div>
 <div class="form-group col-xs-3">
    <label>Fail State:</label>
        <select class="form-control" [(ngModel)]="rules.fail">
        <option>State1</option>
        <option>State2</option>
        <option>State3</option>
     <option>State4</option>
     </select>
         </div>
    </form>
     </div>
 <div class="modal fade" id="myModal" role="dialog">
      <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                     <button type="button" class="close" data-dismiss="modal">&times    </button>
                    <h4 class="modal-title">Rules Configuration</h4>
                </div>
                <div class="modal-body">
                 <p>Rules</p>
                </div>
                 <div class="modal-footer">
                 <button type="button" class="btn btn-default" data-  dismiss="modal">Close</button>
                </div>
             </div>

                </div>
                 </div>
`
    })
    export class AppComponent {
            title = 'Rule Engine Demo';
          rules: Rules = {
                  State: '',
                  pass: '',
                 fail: ''
                };

4 个答案:

答案 0 :(得分:134)

这已经晚了几个月,但我想我会根据this here tutorial提供我的解决方案。它的要点是,一旦改变你接近表格的方式,它就会更容易管理。

首先,使用ReactiveFormsModule代替普通FormsModule或除了FormGroup之外。使用反应式表单,您可以在组件/服务中创建表单,然后将其插入页面而不是生成表单本身的页面。这需要更多的代码,但它更易于测试,更灵活,而且我可以说出制作大量非平凡形式的最佳方法。

最终结果在概念上看起来有点像这样:

  • 您有一个基础FormControl,其中包含整个表单所需的FormGroup个实例。例如,在我链接到的教程中,假设您需要一个表单,用户可以在其中输入一次名称,然后输入任意数量的地址。所有一次性字段输入都将在此基本表单组中。

  • FormArray个实例中,会有一个或多个FormArray个实例。 FormGroup基本上是一种将多个控件组合在一起并迭代它们的方法。您还可以在数组中放置多个FormGroup实例,并将它们用作#34;迷你形式&#34;嵌套在较大的表格中。

  • 通过在动态FormControl中嵌套多个FormArray和/或FormArray个实例,您可以控制有效性并将表单管理为由一个大的,反应性的部分组成的几个动态部分。例如,如果您想在允许用户提交之前检查每个输入是否有效,则一个子表单的有效性将会出现&#34;冒泡&#34;到顶级表单,整个表单变得无效,便于管理动态输入。

  • 由于import { Component, Input, OnInit } from '@angular/core'; import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'my-form-component', templateUrl: './my-form.component.html' }) export class MyFormComponent implements OnInit { @Input() inputArray: ArrayType[]; myForm: FormGroup; constructor(private fb: FormBuilder) {} ngOnInit(): void { let newForm = this.fb.group({ appearsOnce: ['InitialValue', [Validators.required, Validators.maxLength(25)]], formArray: this.fb.array([]) }); const arrayControl = <FormArray>newForm.controls['formArray']; this.inputArray.forEach(item => { let newGroup = this.fb.group({ itemPropertyOne: ['InitialValue', [Validators.required]], itemPropertyTwo: ['InitialValue', [Validators.minLength(5), Validators.maxLength(20)]] }); arrayControl.push(newGroup); }); this.myForm = newForm; } addInput(): void { const arrayControl = <FormArray>this.myForm.controls['formArray']; let newGroup = this.fb.group({ /* Fill this in identically to the one in ngOnInit */ }); arrayControl.push(newGroup); } delInput(index: number): void { const arrayControl = <FormArray>this.myForm.controls['formArray']; arrayControl.removeAt(index); } onSubmit(): void { console.log(this.myForm.value); // Your form value is outputted as a JavaScript object. // Parse it as JSON or take the values necessary to use as you like } } 本质上是数组接口的包装器,但对于表单块,您可以随时推送,弹出,插入和删除控件,而无需重新创建表单或进行复杂的交互

如果我链接到的教程发生故障,这里有一些示例代码可以自己实现(我的示例使用TypeScript)来说明基本的想法:

基本组件代码:

import { Component, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
    selector: 'my-form-sub-component',
    templateUrl: './my-form-sub-component.html'
})
export class MyFormSubComponent {
    @Input() myForm: FormGroup; // This component is passed a FormGroup from the base component template
}

子组件代码 :(每个新输入字段一个,以保持清洁)

<form [formGroup]="myForm" (ngSubmit)="onSubmit()" novalidate>
    <label>Appears Once:</label>
    <input type="text" formControlName="appearsOnce" />

    <div formArrayName="formArray">
        <div *ngFor="let control of myForm.controls['formArray'].controls; let i = index">
            <button type="button" (click)="delInput(i)">Delete</button>
            <my-form-sub-component [myForm]="myForm.controls.formArray.controls[i]"></my-form-sub-component>
        </div>
    </div>
    <button type="button" (click)="addInput()">Add</button>
    <button type="submit" [disabled]="!myForm.valid">Save</button>
</form>

基本组件HTML

<div [formGroup]="form">
    <label>Property One: </label>
    <input type="text" formControlName="propertyOne"/>

    <label >Property Two: </label>
    <input type="number" formControlName="propertyTwo"/>
</div>

子组件HTML

FormGroup

在上面的代码中,我基本上有一个代表表单基础的组件,然后每个子组件在位于基础FormArray内的FormGroup内管理自己的<!-- Remarks Textarea Begin --><td><textarea id="REMARKSTEXTAREA" rows="10" cols="100">***CCSC*** </textarea></td> <!-- Remarks Textarea End --> <!-- BEGIN ACCESS DROPDOWN MENU --> <td style="text-align: center; width: 638px;"> <select id="accessdropdownmenu"> <option value="">MAKE A SELECTION</option> <option value="1">ACCESSED CLLI @ XXX IN "A" DIRECTION…</option> <option value="2">ACCESSEDCLLI @ XXX IN "Z" DIRECTION…</option> <option value="3">ACCESSED CLLI @ XXX (FULLY IMBEDDED) IN "A" DIRECTION…</option> <option value="4">ACCESSED CLLI @ XXX (FULLY IMBEDDED) IN "Z" DIRECTION…</option> <option value=" GETTING IMMEDIATE AIS ALARM…">GETTING IMMEDIATE AIS ALARM…</option> <option value=" GETTING IMMEDIATE RAI ALARM…">GETTING IMMEDIATE RAI ALARM…</option> <option value="5">XXX MAPPED CORRECTLY TO XXX PER CLR/DLR…</option> <option value=" CROSS CONNECTS DO NOT MATCH PER CLR/DLR INFO...">CROSS CONNECTS DO NOT MATCH PER CLR/DLR INFO...</option> <option value=" NO DACS TEST POINTS PER CLR/DLR…">NO DACS TEST POINTS PER CLR/DLR…</option> <option value=" NO REMOTE TEST ACCESS POINTS PER CLR/DLR…">NO REMOTE TEST ACCESS POINTS PER CLR/DLR…</option> <option value="6">UNABLE TO ACCESS CLLI @ XXX...</option> </select> <script> var mytextboxaccess = document.getElementById('REMARKSTEXTAREA'); var mydropdownaccess = document.getElementById('accessdropdownmenu'); mydropdownaccess.onchange = function(){ if (mydropdownaccess.value == 1) { var CLLI = prompt("WHAT CLLI CODE ARE YOU ACCESSING?"); var ACCESSPOINT = prompt("WHAT ACCESS POINT ARE YOU USING?"); mytextboxaccess.value = mytextboxaccess.value + " ACCESSED " + CLLI + " @ " + ACCESSPOINT + " IN 'A' DIRECTION.."; this.value = "";} if (mydropdownaccess.value == 2) { var CLLI = prompt("WHAT CLLI CODE ARE YOU ACCESSING?"); var ACCESSPOINT = prompt("WHAT ACCESS POINT ARE YOU USING?"); mytextboxaccess.value = mytextboxaccess.value + " ACCESSED " + CLLI + " @ " + ACCESSPOINT + " IN 'Z' DIRECTION.."; this.value = "";} if (mydropdownaccess.value == 3) { var CLLI = prompt("WHAT CLLI CODE ARE YOU ACCESSING?"); var ACCESSPOINT = prompt("WHAT ACCESS POINT ARE YOU USING?"); mytextboxaccess.value = mytextboxaccess.value + " ACCESSED " + CLLI + " @ " + ACCESSPOINT + " (FULLY IMBEDDED) IN 'A' DIRECTION.."; this.value = "";} if (mydropdownaccess.value == 4) { var CLLI = prompt("WHAT CLLI CODE ARE YOU ACCESSING?"); var ACCESSPOINT = prompt("WHAT ACCESS POINT ARE YOU USING?"); mytextboxaccess.value = mytextboxaccess.value + " ACCESSED " + CLLI + " @ " + ACCESSPOINT + " (FULLY IMBEDDED) IN 'Z' DIRECTION.."; this.value = "";} if (mydropdownaccess.value == 5) { var ACCESSPOINT1 = prompt("WHAT ACCESS POINT DID YOU ACCESS?"); var ACCESSPOINT2 = prompt("WHAT ACCESS POINT IS IT MAPPED TO?"); mytextboxaccess.value = mytextboxaccess.value + " " + ACCESSPOINT1 + " MAPPED CORRECTLY TO " + ACCESSPOINT2 + " PER CLR/DLR.."; this.value = "";} if (mydropdownaccess.value == 6) { var CLLI = prompt("WHAT CLLI CODE WERE YOU ATTEMPTING TO ACCESS?"); var ACCESSPOINT = prompt("WHAT ACCESS POINT WERE YOU USING?"); mytextboxaccess.value = mytextboxaccess.value + " UNABLE TO ACCESS " + CLLI + " @ " + ACCESSPOINT + ".."; this.value = "";} else { mytextboxaccess.value = mytextboxaccess.value + mydropdownaccess.value + ".."; this.value = "";} } </script> </td> <!-- END ACCESS DROPDOWN MENU --> 实例。 。基本模板将子组传递给子组件,然后您可以动态处理整个表单的验证。

此外,这使得通过从表单中策略性地插入和删除组件来重新排序组件变得微不足道。它(似乎)与任何数量的输入一起工作,因为它们不会与名称冲突(就我所知,模板驱动形式的一大缺点)并且您仍然保留了相当多的自动验证。唯一的缺点是#34;这种方法除了编写更多代码之外,还必须重新学习表单的工作方式。但是,随着您的继续,这将为更大,更动态的形式提供可能性。

如果您有任何疑问或想要指出一些错误,请继续。我刚刚根据我上周所做的事情输入了上面的代码,其名称已更改,其他misc也是如此。属性遗漏,但它应该是直截了当的。上述代码与我自己的代码之间唯一的主要区别在于,我将所有表单构建移动到一个单独的服务,该服务从组件中调用,因此它不那么混乱。

答案 1 :(得分:0)

动态添加和删除文本输入元素,任何人都可以使用,这将起作用 接触类型 余额基金 股票基金 分配 必须分配%! 去掉 添加联系人

userForm: FormGroup;
  public contactList: FormArray;
  // returns all form groups under contacts
  get contactFormGroup() {
    return this.userForm.get('funds') as FormArray;
  }
  ngOnInit() {
    this.submitUser();
  }
  constructor(public fb: FormBuilder,private router: Router,private ngZone: NgZone,private userApi: ApiService) { }
  // contact formgroup
  createContact(): FormGroup {
    return this.fb.group({
      fundName: ['', Validators.compose([Validators.required])], // i.e Email, Phone
      allocation: [null, Validators.compose([Validators.required])]
    });
  }


  // triggered to change validation of value field type
  changedFieldType(index) {
    let validators = null;

    validators = Validators.compose([
      Validators.required,
      Validators.pattern(new RegExp('^\\+[0-9]?()[0-9](\\d[0-9]{9})$')) // pattern for validating international phone number
    ]);

    this.getContactsFormGroup(index).controls['allocation'].setValidators(
      validators
    );

    this.getContactsFormGroup(index).controls['allocation'].updateValueAndValidity();
  }

  // get the formgroup under contacts form array
  getContactsFormGroup(index): FormGroup {
    // this.contactList = this.form.get('contacts') as FormArray;
    const formGroup = this.contactList.controls[index] as FormGroup;
    return formGroup;
  }

  submitUser() {
    this.userForm = this.fb.group({
      first_name: ['', [Validators.required]],
      last_name: [''],
      email: ['', [Validators.required]],
      company_name: ['', [Validators.required]],
      license_start_date: ['', [Validators.required]],
      license_end_date: ['', [Validators.required]],
      gender: ['Male'],
      funds: this.fb.array([this.createContact()])
    })
    this.contactList = this.userForm.get('funds') as FormArray;
  }
  addContact() {
    this.contactList.push(this.createContact());
  }
  removeContact(index) {
    this.contactList.removeAt(index);
  }

答案 2 :(得分:0)

那是HTML代码。任何人都可以使用:

<div class="card-header">Contact Information</div>
          <div class="card-body" formArrayName="funds">
            <div class="row">
              <div class="col-6" *ngFor="let contact of contactFormGroup.controls; let i = index;">
                <div [formGroupName]="i" class="row">
                  <div class="form-group col-6">
                    <label>Type of Contact</label>
                    <select class="form-control" formControlName="fundName" type="text">
                      <option value="01">Balance Fund</option>
                      <option value="02">Equity Fund</option>
                    </select> 
                  </div>
                  <div class="form-group col-12">
                    <label>Allocation</label>
                    <input class="form-control" formControlName="allocation" type="number">
                    <span class="text-danger" *ngIf="getContactsFormGroup(i).controls['allocation'].touched && 
                    getContactsFormGroup(i).controls['allocation'].hasError('required')">
                        Allocation % is required! </span>
                  </div>
                  <div class="form-group col-12 text-right">
                    <button class="btn btn-danger" type="button" (click)="removeContact(i)"> Remove </button>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <button class="btn btn-primary m-1" type="button" (click)="addContact()"> Add Contact </button>

答案 3 :(得分:-2)

addAccordian(类型,数据){     console.log(类型,数据);

let form = this.form;

if (!form.controls[type]) {
  let ownerAccordian = new FormArray([]);
  const group = new FormGroup({});
  ownerAccordian.push(
    this.applicationService.createControlWithGroup(data, group)
  );
  form.controls[type] = ownerAccordian;
} else {
  const group = new FormGroup({});
  (<FormArray>form.get(type)).push(
    this.applicationService.createControlWithGroup(data, group)
  );
}
console.log(this.form);

}