12

フォーム内で *ngIf と ngFormModel の両方を使用してフォームを検証するのに苦労しています。

ユースケースは次のとおりです。ユーザー入力に基づいて、フォーム内の特定のフィールドを非表示または無効にします。ただし、これらの入力が表示される場合は、検証する必要があります。

基本的な検証のみが必要な場合は、何らかの方法で行うことができます:

  • 入力のみが必要な場合は、 と を使用して A-OK で動作しngControlますrequired
  • 特定の形式が必要な場合は、pattern属性を使用できます。Angular ではありませんが、機能します。

しかし、より複雑な検証を実装するために、カスタム チェックを使用するようにngControl結合を使用しようとしています。ngFormModel次のページにあるコードを使用しました。

angular2(およびそこで参照されているリンク)にフォーム検証パターンを追加する方法

Angular2 フォーム: 検証、ngControl、ngModel など

私のコードは次のとおりです。

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>

タイプスクリプト

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 を使用して入力がテンプレートから削除された場合でも、コンストラクターは引き続きコントロールを参照しています。当然のことながら INVALID であるため、[disabled]="!formState.form.valid" を使用できなくなりmyFormます。

Angular 2 を使用して私が目指していることは可能ですか? これは珍しいユースケースではないと確信していますが、私の現在の知識では、どうすればそれを機能させることができるかわかりません。

ありがとう !

4

3 に答える 3

8

あなたがしようとすることができるのは、コントロールのバリデーターをリセットすることです。つまり、状態が変化したためにバリデーターの新しいセットをコントロールにバインドする場合は、バリデーター関数を再定義します。

あなたの場合、チェックボックスをオン/オフにすると、次のことが発生します。

  1. 入力をオプション (必須ではない) に設定しますが、カスタム バリデータに対して検証します。
  2. チェックボックスがオフの場合、コントロールのバリデーターを元の状態に戻します。
  3. form.valid更新されるように、コントロールを再度検証します。

Angular.io の Forms Guide に基づいた私の plnkr サンプルを参照してください。

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();
于 2016-03-11T03:47:56.597 に答える
2

まったく同じ問題に遭遇し、コントロールを手動で含めたり除外したりすることに依存する回避策を見つけました。

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を参照してください。

于 2016-04-13T16:58:05.153 に答える
0

これは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;
  }
}
于 2016-07-27T20:53:53.270 に答える