it-swarm.com.de

Angular 2 - Verwenden von ngControl mit optionalen Feldern

Es fällt mir schwer, sowohl * ngIf in einem Formular als auch ngFormModel zur Validierung dieses Formulars zu verwenden.

Der Anwendungsfall ist der folgende: Blenden Sie basierend auf Benutzereingaben bestimmte Felder im Formular aus oder deaktivieren Sie sie. Falls diese Eingaben angezeigt werden, müssen sie validiert werden.

Wenn nur eine grundlegende Validierung erforderlich ist, kann ich Folgendes tun:

  • Wenn die Eingabe nur erforderlich ist, funktioniert sie mit A-OK unter Verwendung von ngControl und required.
  • Wenn ein bestimmtes Format erforderlich ist, kann das Attribut pattern verwendet werden. Es ist nicht eckig, aber es funktioniert.

Um komplexere Validierungen zu implementieren, habe ich versucht, ngControl in Verbindung mit ngFormModel zu verwenden, um benutzerdefinierte Prüfungen zu verwenden. Ich habe Code-Teile verwendet, die auf den folgenden Seiten zu finden sind:

Wie man ein Formular-Validierungsmuster in angle2 hinzufügt (und die Links, auf die dort verwiesen wird)

Angular2-Formulare: Validierungen, ngControl, ngModel usw.

Mein Code lautet wie folgt:

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

Auch wenn die Eingabe mit * ngIf aus der Vorlage entfernt wird, verweist der Konstruktor weiterhin auf das Steuerelement. Was wiederum verhindert, dass ich [disabled] = "! FormState.form.valid" verwende, da myForm verständlicherweise UNGÜLTIG ist.

Ist das, was ich anstrebe, mit Angular 2 möglich? Ich bin mir sicher, dass dies kein ungewöhnlicher Anwendungsfall ist, aber nach meinem derzeitigen Kenntnisstand kann ich nicht erkennen, wie ich ihn zum Laufen bringen könnte.

Vielen Dank !

12
phl

Sie können versuchen, die Prüfer auf Ihrem Steuerelement zurückzusetzen. Das heißt, wenn Sie möchten, dass eine neue Gruppe von Validatoren aufgrund einer Statusänderung an ein Steuerelement gebunden wird, definieren Sie die Validatorfunktion neu.

Wenn in Ihrem Fall das Kontrollkästchen aktiviert/deaktiviert ist, soll Folgendes geschehen:

  1. Legen Sie die Eingabe als optional fest (nicht erforderlich), überprüfen Sie sie jedoch anhand Ihres benutzerdefinierten Validators.
  2. Setzen Sie die Prüfer des Steuerelements in ihren ursprünglichen Zustand zurück, wenn das Kontrollkästchen deaktiviert ist.
  3. Überprüfen Sie das Steuerelement erneut, damit der form.valid aktualisiert wird.

Siehe mein plnkr-Beispiel basierend auf 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();
8
Alex

Ich bin gerade auf genau dasselbe Problem gestoßen und habe eine Problemumgehung gefunden, die auf manuelles Ein- und Ausschließen der Steuerelemente beruht:

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

Damit dies funktioniert, müssen zunächst alle dynamischen Steuerelemente aus dem Formular ausgeschlossen werden. Einzelheiten finden Sie im plunkr .

2
Christian Zosel

Hier ist eine aktualisierte Version für RC4. Ich habe es für meine Zwecke auch in npControl umbenannt.

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;
  }
}
0
Jesse Sanders