首页 文章

Angular 2 - 使用带有可选字段的ngControl

提问于
浏览
11

我很难尝试在表单中使用* ngIf,而使用ngFormModel来验证表单 .

用例是这样的:根据用户输入,隐藏或停用表单中的某些特定字段 . 但是如果显示这些输入,则必须对它们进行验证 .

当只需要基本验证时,我可以这样或那样做:

  • 如果仅需要输入,则使用 ngControlrequired 进行A-OK工作

  • 如果需要特定格式,则可以使用 pattern 属性 . 它不是Angular,但它有效 .

但是为了实现更复杂的验证,我一直在尝试使用 ngControl 耦合到 ngFormModel 以便使用自定义检查 . 我使用了以下页面中的代码片段:

How to add form validation pattern in angular2(以及那里引用的链接)

Angular2 Forms :Validations, ngControl, ngModel etc

我的代码如下:

HTML

<div>
  <h1>Rundown of the problem</h1>
  <form (ngSubmit)="submitForm()" #formState="ngForm" [ngFormModel]="myForm">
    <div class="checkbox">
      <label>
        <input type="checkbox" [(ngModel)]="model.hideField" ngControl="hideField"> Is the input below useless for you ?
      </label>
    </div>

    <div *ngIf="!model.hideField">
      <div class="form-group">
        <label for="optionalField">Potentially irrelevant field </label>
        <input type="text" class="form-control" [(ngModel)]="model.optionalField" ngControl="optionalField" required #optionalField="ngForm">
        <div [hidden]="optionalField.valid || optionalField.pristine" class="alert alert-warning">
          This input must go through myCustomValidator(), so behave.
        </div>
      </div>
    </div>

    <button type="submit" class="btn btn-primary" [disabled]="!formState.form.valid">I can't be enabled without accessing the input :(</button>
    <button type="submit" class="btn btn-default">Submit without using form.valid (boo !)</button>
  </form>
</div>

Typescript

import {Component, ChangeDetectorRef, AfterViewInit } from 'angular2/core';
import {NgForm, FormBuilder, Validators, ControlGroup, FORM_DIRECTIVES}    from 'angular2/common';

@Component({
  selector: 'accueil',
  templateUrl: 'app/accueil.component.bak.html',
  directives:[FORM_DIRECTIVES],
  providers: [FormBuilder]
})
export class AccueilComponent implements AfterViewInit {

  private myForm: ControlGroup;
  model: any;

  constructor(fb: FormBuilder, cdr: ChangeDetectorRef) {
    this.cdr = cdr ;

    this.model = {} ;
    this.myForm = fb.group({
      "hideField": [false],
      "optionalField": [this.model.optionalField, Validators.compose([this.myCustomValidator])]
    });
  }

  ngAfterViewInit() {
    // Without this, I get the "Expression has changed after it was checked" exception.
    // See also : https://stackoverflow.com/questions/34364880/expression-has-changed-after-it-was-checked
    this.cdr.detectChanges();
  }

  submitForm(){
    alert("Submitted !");
  }

  myCustomValidator(optionalField){
    // Replace "true" by "_someService.someCheckRequiringLogicOrData()"
    if(true) {
      return null;
    }

    return { "ohNoes": true };
  }
}

即使使用* ngIf从模板中删除输入,构造函数仍然引用该控件 . 这反过来阻止我使用[disabled] = "!formState.form.valid",因为 myForm 可以理解为无效 .

我的目标是使用Angular 2吗?我确信这不是一个不常见的用例,但是再次根据我目前的知识,我无法看到我如何才能使它工作 .

谢谢 !

3 回答

  • 2

    这是RC4的更新版本 . 为了我的目的,我也将它重命名为npControl .

    import { Directive, Host, OnDestroy, Input, OnInit } from '@angular/core';
    import { ControlContainer } from '@angular/forms';
    
    @Directive({
      selector: '[npControl]'
    })
    export class NPControlDirective implements OnInit, OnDestroy {
      @Input() npControl: string;
    
      constructor(@Host() private _parent: ControlContainer
      ) { }
    
      ngOnInit(): void {
        console.log('include ', this.npControl);
        setTimeout(() => this.formDirective.form.include(this.npControl));
      }
    
      ngOnDestroy(): void {
        console.log('exclude ', this.npControl);
        this.formDirective.form.exclude(this.npControl);
      }
    
      get formDirective(): any {
        return this._parent;
      }
    }
    
  • 8

    我遇到了完全相同的问题,并找到了一个依赖于手动包含和排除控件的解决方法:

    import {Directive, Host, SkipSelf, OnDestroy, Input, OnInit} from 'angular2/core';
    import {ControlContainer} from 'angular2/common';
    
    @Directive({
      selector: '[ngControl]'
    })
    export class MyControl implements OnInit, OnDestroy {
      @Input() ngControl:string;
      constructor(@Host() @SkipSelf() private _parent:ControlContainer) {}
    
      ngOnInit():void {
        // see https://github.com/angular/angular/issues/6005
        setTimeout(() => this.formDirective.form.include(this.ngControl));
      }
    
      ngOnDestroy():void {
        this.formDirective.form.exclude(this.ngControl);
      }
    
      get formDirective():any {
        return this._parent.formDirective;
      }
    }
    

    为了使这项工作,最初必须从表单中排除所有动态控件;有关详细信息,请参阅plunkr .

  • 0

    您可以尝试做的是重置控件上的验证器 . 这意味着,当您希望由于状态更改而将一组新的验证器绑定到控件时,您将重新定义验证器函数 .

    在您的情况下,当选中/取消选中复选框时,您希望发生以下情况:

    • 将输入设置为可选(不是必需的),但仍然针对您的自定义验证器进行验证 .

    • 取消选中该复选框后,将控件的验证器恢复为原始状态 .

    • 再次验证控件以更新 form.valid .

    See my plnkr sample based on Angular.io's Forms Guide

    if (optional)
       this.heroFormModel.controls['name'].validator = Validators.minLength(3);
    else
       this.heroFormModel.controls['name'].validator =
            Validators.compose([Validators.minLength(3), Validators.required]);
    
    this.heroFormModel.controls['name'].updateValueAndValidity();
    

相关问题