动态添加和删除表单元素的表单验证

Form Validation for Dynamically added and removed form Elements

我有一个表单元素,我可以在其中动态添加和删除值。

例如,在下面的代码中,可以使用 addDomain 和 removeDomain 添加和删除域

    <div *ngFor="let item of company.domains">
                    <div class="row">
                        <div class="col-lg-9">
                            <input type="text" class="form-control" ngControl="domainC"  [(ngModel)]="company.domains[i]">
                        </div>
                        <div class="col-lg-3 pull-left">
                            <a (click)="removeDomain(i)"> <i  class="fa fa-times"></i></a>
                            <a (click)="addDomain()"> <i  class="fa fa-plus"></i></a>
                        </div>
                    </div>
                </div>

我需要在提交时验证这些,因为所有附加值都是必需的。我在 angular 2 中使用 Control,但无法弄清楚如何对此类元素应用验证,我们将不胜感激。

Plunker example

您可以将表单绑定到表单模型

<form [ngFormModel]="form">
  <div *ngFor="let item of controls let idx=index">
    <div class="row">
      <div class="col-lg-9">
        <label>{{item.name}}</label><input type="text" class="form-control" [ngControl]="item.name"  [(ngModel)]="values[item.name]">
        {{item.control.errors | json}}
      </div>
      <div class="col-lg-3 pull-left">
        <div><button (click)="removeDomain(i)">remove</button></div>
      </div>
    </div>
</div>

<hr>

<div>
  <label>control name</label><input #nameInput>
  <label>validator</label><select #validatorInput>
    <option>required</option>
    <option>minLength</option>
    <option>maxLength</option>
  </select>
  <label>length</label><input type="number" #lengthInput>
  <button (click)="addDomain(nameInput.value, validatorInput.value, lengthInput.value)">add</button>
</div>

export class App {
  form:ControlGroup;
  controls [
    { name: 'name', control: new Control('', Validators.required) },
    { name: 'password', control: new Control('', Validators.minLength(3))}
  ];
  values = {
    name: '',
    password: '',
  }

  constructor(fb:FormBuilder) {
    this.form = fb.group();
    this.name = 'Angular2 (Release Candidate!)'
    this.controls.map((item) => {
      console.log('map item', item);
      this.form.addControl(item.name, item.control);
    });
  }

  removeDomain(i) {
    this.values[this.controls[i].name]=undefined;
    this.form.removeControl(this.controls[i].name);
    this.controls.splice(i);
    this.form.controls.forEach(c => c.updateValueAndValidity());
  }

  addDomain(name, validator, length) {
    this.values[name] = '';
    var validator;
    if(length) {
      validator = Validators[validator](length);
    } else {
      validator = Validators[validator];
    }

    let newCtl = new Control('', validator);
    this.controls.push({name: name, control: newCtl});
    this.form.addControl(name, newCtl);
    //this.controls.forEach(c => this.form.controls[c.name].updateValueAndValidity());
  }
}

如果您想在您的视图控制器中以编程方式执行验证,您可以在您的视图中引用您的控件并在更改时将其传递给控制器​​。类似于:

 <input type="text" class="form-control" ngControl="domainC"  #domainCControl="ngForm" [(ngModel)]="company.domains[i]" (change)="onDomainInputChanged(domainCControl)">
 <div [hidden]="domainCControl.valid">Control invalid</div>

控制器:

onDomainInputChanged(control: NgControlName) {
  // You have access to control.control.validatorFn here
}

但是您可能希望在可以在其他地方重用的专用指令中实现验证器:

<input type="text" class="form-control" ngControl="domainC"  [(ngModel)]="company.domains[i]" validate-domain-control >

和指令:

@Directive({
    selector: '[validate-domain-control][ngControl],[validate-domain-control][ngModel],[validate-domain-control][ngFormControl]',
    providers: [{provide: NG_VALIDATORS, useExisting: forwardRef(()=>DomainValidator), multi: true}]
})
export class DomainValidator implements Validator {

    constructor() {}

    validate(control:AbstractControl) {
        if (isValid(control.value)) {
           return null;
        }
        return {'myValidityClass': false};
    }
}

我找到了解决办法。该问题与控件的命名有关。当从中间删除一行时,table 索引将重新启动并添加具有重复控件名称的另一行。

Plunker link

添加了一个始终递增的计数器并将其分配给详细信息对象。

addRow() {
    this.details.push(<IDetail>{controlIndex:this.iRow});

    this.rowValidateForm(this.iRow++, 'add');        
}

删除时从详细信息对象中获取计数器并从具有计数器名称的组中删除控件。

removeRow(index) {
  let controlIndex = this.details[index].controlIndex;

  this.details.splice(index, 1);
  this.rowValidateForm(controlIndex, 'remove');
}

在视图中,将 FormControlName 中的行索引替换为 countrolIndex。