2016-03-20 15 views
17

Wie soll ich gehen, bedingt ein Formularfeld erforderlich? Ich habe einen benutzerdefinierten Validator erstellt, aber die bedingten Variablen, die ich an den benutzerdefinierten Validator übergebe, sind statisch und bleiben ihre Anfangswerte. Wie sollte mein benutzerdefinierter Validator aussehen, um aktualisierte bedingte Werte zu erhalten? Vielleicht gibt es eine Möglichkeit, dies mit Validators.required anstelle eines benutzerdefinierten Validators zu tun?Angular 2 conditional Validators.required?

private foo: boolean = false; 
private bar: boolean = true; 

constructor(private _fb: FormBuilder) { 
    function conditionalRequired(...conditions: boolean[]) { 
     return (control: Control): { [s: string]: boolean } => { 
     let required: boolean = true; 
     for (var i = 0; i < conditions.length; i++) { 
      if (conditions[i] === false) { 
      required = false; 
      } 
     } 
     if (required && !control.value) { 
      return { required: true } 
     } 
     } 
    } 
    this.applyForm = _fb.group({ 
      'firstName': ['', Validators.compose([ 
      conditionalRequired(this.foo, !this.bar) 
      ])], 
      ... 
    }); 
} 

Update (17. Mai 2016)

Es gewesen eine lange Zeit, das seit der Veröffentlichung, aber ich mag die .include() und .exclude() Methoden, die auf der ControlGroup Klasse für jemanden da draußen referenzieren Wer versucht, diese Funktionalität zu erstellen. (docs) Während es wahrscheinlich Anwendungsfälle für einen bedingten Validator wie oben gibt, habe ich die Aufnahme und den Ausschluss von Steuerelementen, Steuerelementgruppen und Steuerelementarrays als eine gute Möglichkeit gefunden, um damit umzugehen. Setzen Sie einfach den required Validator auf das Steuerelement, das Sie möchten, und schließen Sie es nach Belieben ein oder aus. Hoffe das hilft jemandem!

+1

ich sieht gut aus ;-) –

+0

Aber wenn ich 'foo' true an anderer Stelle zu ändern, der benutzerdefinierten Validator zeigt "foo" immer noch als falsch an. Ein 'console.log (conditions)' im Validator zu werfen, gibt '[false, false]' zurück, wenn ich weiß, dass 'foo' tatsächlich wahr ist. Ich muss "[wahr, falsch]" bekommen. Ich nehme an, dass das ist, weil die Bedingungen einmal festgelegt werden, da es innerhalb des Konstruktors ist. Irgendeine Möglichkeit, dieses Verhalten zu umgehen? –

+1

Der Link zu den Dokumenten im Update ist gebrochen – surfbird0713

Antwort

5

Nach Ihrem Kommentar kann ich ein potenzielles Problem sehen. Da Sie der Funktion, die die Validatorfunktion erstellt, Bedingungen als primitive Typen bereitstellen, werden die Werte beim Aufrufen der ersten verwendet. Selbst wenn sie sich danach ändern, werden die neuen Werte nicht berücksichtigt.

zu archivieren, dass Sie ein Objekt für Bedingungen verwenden müssen, wie unten beschrieben:

private foo: boolean = false; 
private bar: boolean = true; 

private conditions: any = { 
    condition1: foo, 
    condition2: !bar 
}; 

constructor(private _fb: FormBuilder) { 
    function conditionalRequired(conditions: any) { 
     return (control: Control): { [s: string]: boolean } => { 
     let required: boolean = true; 
     for (var elt in conditions) { 
      var condition = conditions[elt]; 
      if (conditions === false) { 
      required = false; 
      } 
     } 
     if (required && !control.value) { 
      return { required: true }; 
     } 
     } 
    } 
    this.applyForm = _fb.group({ 
      'firstName': ['', Validators.compose([ 
      conditionalRequired(conditions) 
      ])], 
      ... 
    }); 
} 

Auf diese Weise die Bedingungen Parameter verwendet werden kann/durch Bezugnahme aktualisiert. Um Ihre Bedingungen zu aktualisieren, müssen Sie folgendes tun:

updateConditions() { 
    this.conditions.condition1 = true; 
    this.conditions.condition2 = true; 
} 

Hier ist ein plunkr: https://plnkr.co/edit/bnX7p0?p=preview.

bearbeiten

den Validator ausgeführt werden, wenn die Bedingungen zu aktualisieren, müssen Sie explizit die updateValueAndValidity Methode der Steuerung nennen.

updateConditions() { 
    this.conditions.condition1 = true; 
    this.conditions.condition2 = true; 
    this.applyForm.controls.firstName.updateValueAndValidity(); 
} 
+0

Leider bekomme ich das gleiche Problem: die erste Bedingung ist falsch, und die zweite ist wahr. –

+0

Die Werte ändern sich immer noch nicht, wenn sich der "foo" -Wert ändert –

+1

Tatsächlich müssen Sie das Bedingungsobjekt aktualisieren. Ich aktualisierte meine Antwort und bietet eine funktionierende Plunkr ... –

2

ich einen Validator erstellt, die eine Callback-Funktion übernimmt, die mir den Validator wiederverwendbar machen können, aber ich kann nicht scheinen zu finden: In diesem Fall wird das valid Attribut sowohl Steuer- und Form entsprechend aktualisiert werden eine gute Möglichkeit, um sicherzustellen, dass ich updateValueAndValidity() auf dem Steuerelement mit diesem Validator aufrufen, ohne es manuell aufrufen zu müssen, wenn sich der Wert des anderen Steuerelements ändert.

Validator

export class ValidationService { 
    static conditionalRequired = (isRequiredFunction: Function) => { 
     return (control: Control) => { 
      if (!control.value && isRequiredFunction()) 
      ... 

Komponente Seite

private myControl1: Control = 
    new Control("", ValidationService.conditionalRequired(() => 
     { return this && this.model && this.model.SomeProperty === 'SomeValue' })); 

private myControl2: Control = 
    new Control("", 
    ValidationService.conditionalRequired(this.isControl2Required.bind(this))); 

isControl2Required() { 
    return someCondition; 
} 
9

wollte ich eine generische Version von ihm so habe ich eine zusätzliche Validator für sie geschrieben, die zusammen mit anderen Validatoren zusammengesetzt werden können, . Ich fange gerade noch an, das Forms-Modul zu betrachten, also erwarte nicht, dass dies der effizienteste Code ist, der jemals in Edge Fällen funktioniert hat, aber es ist ein guter Anfang. Es funktioniert für normale Anwendungsfälle und könnte als guter Ausgangspunkt für andere dienen.

Gemacht für rc.4 und das neue Formenmodul, kann der revalidateOnChanges Teil schrecklich sein (nicht sicher von der besten Weise, dieses Verhalten zu verursachen), verwenden Sie auf eigene Gefahr! :)

Wie man verwendet es

Der Validator nimmt zwei Argumente, eine bedingte Funktion, die die formGroup gegeben ist und dass erwartet wird true zurück, wenn die Validierung anders anzuwenden und falsch ist, und ein Validator (was eine Zusammensetzung sein kann). Es wird das Feld revalidieren, wenn die formGroup aktualisiert wird, und es kann derzeit nur Dinge in derselben formGroup überprüfen, aber das sollte einfach zu beheben sein.

this.formBuilder.group({ 
    vehicleType: ['', Validators.required], 
    licencePlate: [ 
     '', 
     ExtraValidators.conditional(
      group => group.controls.vehicleType.value === 'car', 
      Validators.compose([ 
       Validators.required, 
       Validators.minLength(6) 
      ]) 
     ), 
    ] 
}); 

In diesem Beispiel haben Sie zwei Felder, vehicleType und licencePlate. Die bedingte Anweisung wendet den zusammengesetzten Validator (erforderlich und minLength) an, wenn der Fahrzeugtyp "Auto" ist.

Sie können verfassen verwenden, um mehrere verschiedene Bedingungen anzuwenden, die möglicherweise gleichzeitig gelten oder nicht. Hier ist ein etwas komplexeres Beispiel:

this.formBuilder.group({ 
    country: ['', Validators.required], 
    vehicleType: ['', Validators.required], 
    licencePlate: [ 
     '', 
     Validators.compose([ 
      ExtraValidators.conditional(
       group => group.controls.vehicleType.value === 'car', 
       Validators.required 
      ), 
      ExtraValidators.conditional(
       group => group.controls.country.value === 'sweden', 
       Validators.minLength(6) 
      ), 
     ]) 
    ] 
}); 

In diesem Fall wir anwenden, wenn Typ „Auto“ und wir minLength gelten, wenn Land ist „Schweden“. Wenn nur eine Bedingung zutrifft, gilt diese Validierung nur, wenn beide Bedingungen zutreffen, dann gelten beide Validierungen.

Der Validator selbst

Beachten Sie, dass der Objektvergleich ist nur eine einfache Brute-Force weil wir mit kleinem Gegenstand arbeiten, wenn Sie Ramda verwenden oder etwas, das Sie eine Menge Code schneiden könnten.

export class ExtraValidators { 
    static conditional(conditional, validator) { 
     return function(control) { 
      revalidateOnChanges(control); 

      if (control && control._parent) { 
       if (conditional(control._parent)) { 
        return validator(control); 
       } 
      } 
     }; 
    } 
} 
function revalidateOnChanges(control): void { 
    if (control && control._parent && !control._revalidateOnChanges) { 
     control._revalidateOnChanges = true; 
     control._parent 
      .valueChanges 
      .distinctUntilChanged((a, b) => { 
       // These will always be plain objects coming from the form, do a simple comparison 
       if(a && !b || !a && b) { 
        return false; 
       } else if (a && b && Object.keys(a).length !== Object.keys(b).length) { 
        return false; 
       } else if (a && b) { 
        for (let i in a) { 
         if(a[i] !== b[i]) { 
          return false; 
         } 
        } 
       } 
       return true; 
      }) 
      .subscribe(() => { 
       control.updateValueAndValidity(); 
      }); 

     control.updateValueAndValidity(); 
    } 
    return; 
} 

HINWEIS: erinnere mich an den Betreiber zu importieren:
Import 'rxjs/add/Operator/distinctUntilChanged';

1

Wir setValidators verwenden Methode() dynamische Validierung setzen für die Zeit nach der Suche nach den Kontrollen erforderlich -

this.applyForm.get('firstName').setValidators(setRequired()); 

setRequired() { 
     if(1==1) { 
      return [Validators.required]; 
     } else { 
      return []; 
     } 
    }