【问题标题】:How to dynamically add and remove form fields in Angular 2如何在 Angular 2 中动态添加和删除表单字段
【发布时间】:2016-10-26 17:01:51
【问题描述】:

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

我尝试过的

我创建了一组字段(3 个选择框和 1 个文本框),创建了一个名为添加字段的按钮,但我在 angular 1.x 中尝试过它工作正常,但在 angular 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: ''
                };

【问题讨论】:

  • 你可以使用ControlGroup来实现这个stackoverflow.com/questions/36627573/…
  • @A_Singh 你知道为什么 Angular 2 没有加载模板 html 内部脚本代码
  • 您的意思是使用[innerHTML] 注入脚本不起作用吗?这是因为 Angular 不允许以这种方式注入脚本
  • 没有脚本标签,你曾经使用过 jquery 查询生成器吗?..
  • query builder ?不,但它也在发生,因为 Angular 不允许模板中的任何脚本。见this issue

标签: angular


【解决方案1】:

这晚了几个月,但我想我会根据this here tutorial 提供我的解决方案。它的要点是,一旦你改变了处理表单的方式,管理起来就会容易得多。

首先,使用ReactiveFormsModule 代替或补充普通的FormsModule。使用反应式表单,您可以在组件/服务中创建表单,然后将它们插入您的页面,而不是由您的页面生成表单本身。它的代码多一些,但更易于测试,更灵活,而且据我所知,这是制作大量非平凡表单的最佳方法。

从概念上讲,最终结果会有点像这样:

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

  • 在该FormGroup 实例中将有一个或多个FormArray 实例。 FormArray 基本上是一种将多个控件组合在一起并对其进行迭代的方法。您还可以在数组中放置多个 FormGroup 实例,并将它们用作嵌套在较大表单中的“迷你表单”。

  • 通过在动态FormArray 中嵌套多个FormGroup 和/或FormControl 实例,您可以控制有效性并将表单作为一个由多个动态部分组成的大型反应部分来管理。例如,如果您想在允许用户提交之前检查每个输入是否有效,则一个子表单的有效性会“冒泡”到顶层表单,整个表单变得无效,从而很容易管理动态输入。

  • FormArray 本质上是一个数组接口的包装器,但对于表单片段,您可以随时推送、弹出、插入和删除控件,而无需重新创建表单或进行复杂的交互。

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

基础组件代码:

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

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

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
}

基础组件 HTML

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

在上面的代码中,我基本上有一个表示表单基础的组件,然后每个子组件在位于基础FormGroup 内的FormArray 内管理自己的FormGroup 实例。基本模板将子组传递给子组件,然后您可以动态处理整个表单的验证。

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

如果您有任何问题或想指出一些错误,请继续。我只是根据上周自己所做的更改名称和其他杂项的事情输入了上面的代码。属性省略了,但它应该是简单的。上面的代码和我自己的唯一主要区别是我将所有的表单构建移到了一个从组件调用的单独服务中,所以它不那么混乱了。

【讨论】:

  • 如何实现 ngModel 到那个表单?
  • @elporfirio 您不使用这些表单的 ngModel。 ngModel 主要用于处理与 JavaScript 对象的双向数据绑定。这些反应式表单本质上是它们自己的对象,具有各种有用的字段和属性,例如验证。相反,您从表单中获取“.value”属性,这就是您的结果集。您可以随意操作它并将其映射到您拥有的任何类或接口。
  • 你保存了我的约会
  • @Faradox:你的意思是“一天”,不是吗? ;)
【解决方案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);

}

【讨论】:

    【解决方案3】:

    动态添加和删除文本输入元素任何人都可以使用这将工作 联系方式 平衡基金 股票基金 分配 分配百分比是必需的! 消除 添加联系人

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

    【讨论】:

    • 不知道为什么您发布了两个答案而不是一个完整的答案。为了使您的答案尽可能有用,您能否确保您有一个很好的解释,说明您的答案如何比 2016 年接受的答案增加价值?
    • 请使用帖子下方的“编辑”按钮进行更新。不要在两个单独的帖子中发布您的答案
    【解决方案4】:

    那是 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>
    

    【讨论】:

      猜你喜欢
      • 2019-04-19
      • 2017-05-25
      • 1970-01-01
      • 1970-01-01
      • 2016-10-20
      • 2011-05-16
      • 2020-09-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多