2016-03-20 16 views
17

W jaki sposób powinieneś wymagać warunkowo pola formularza? Zrobiłem własny weryfikator, ale zmienne warunkowe, które przekazuję do walidatora niestandardowego, są statyczne i pozostają ich wartościami początkowymi. Jak powinien wyglądać mój niestandardowy weryfikator, aby uzyskać zaktualizowane wartości warunkowe? Być może istnieje sposób, aby to zrobić z Validators.required zamiast niestandardowego weryfikatora?Angular 2 Waritional 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) 
      ])], 
      ... 
    }); 
} 

Aktualizacja (17 maja, 2016)

Minęło dużo czasu od opublikowania tego, ale chciałbym odwołać się do .include() i .exclude() metody dostępne w klasie ControlGroup dla nikogo tam kto próbuje stworzyć tę funkcjonalność. (docs) Chociaż istnieją prawdopodobnie przypadki użycia warunkowego walidatora, jak wyżej, znalazłem włączenie i wyłączenie kontroli, grup kontrolnych i tablic kontrolnych, aby było to świetnym sposobem radzenia sobie z tym. Po prostu ustaw walidator required na wybranej kontrolce i dołącz/wyklucz to, jak chcesz. Mam nadzieję, że to pomoże komuś!

+1

wygląda dobrze do mnie ;-) –

+0

Ale kiedy zmienić 'foo' do prawdziwego gdzie indziej, zwyczaj walidator nadal pokazuje 'foo' jako fałsz. Rzucanie 'console.log (warunkami)' w walidatorze zwraca '[false, false]' gdy wiem, że 'foo' faktycznie jest prawdziwe. Muszę uzyskać '[true, false]'. Zakładam, że tak jest, ponieważ warunki są ustawione raz, ponieważ są wewnątrz konstruktora. Jakikolwiek sposób obejścia tego zachowania? –

+1

Link do dokumentów w aktualizacji jest zepsuty – surfbird0713

Odpowiedz

5

Po komentarzu widzę potencjalny problem. Ponieważ podajesz warunkom jako typom pierwotnym funkcję, która tworzy funkcję sprawdzania poprawności, zostaną użyte wartości przy wywołaniu pierwszej. Nawet jeśli później ulegną zmianie, nowe wartości nie będą brane pod uwagę.

celu archiwizacji, że trzeba użyć obiektu na warunkach opisanych poniżej:

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) 
      ])], 
      ... 
    }); 
} 

ten sposób parametr warunki mogą być wykorzystane/zaktualizowany przez odniesienie. Aby zaktualizować swoje warunki, trzeba wykonać następujące czynności:

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

Oto plunkr: https://plnkr.co/edit/bnX7p0?p=preview.

Edit

Aby uruchomić walidator gdy uaktualnienie warunków, trzeba jawnie wywołać metodę kontroli updateValueAndValidity. W tym przypadku atrybut zarówno kontroli i formy valid zostaną odpowiednio zaktualizowane:

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

Niestety, pojawia się ten sam problem: pierwszy warunek jest fałszywy, a drugi jest prawdziwy. –

+0

Wartości nadal nie zostaną zmienione, gdy zmieni się wartość 'foo'. –

+1

W rzeczywistości należy zaktualizować obiekt warunków. Zaktualizowałem swoją odpowiedź i zapewniłem działającą grę ... –

2

stworzyłem walidator, która akceptuje wywołania zwrotnego funkcję, która pozwala mi dokonać wielokrotnego użytku walidatora, ale nie wydaje się znaleźć dobrym sposobem, aby upewnić się, że wywołasz funkcję updateValueAndValidity() na kontrole za pomocą tego weryfikatora, bez konieczności ręcznego wywoływania go, gdy wartość innej kontrolki ulegnie zmianie.

Validator

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

Komponent Strona

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

chciałem bardziej ogólną wersję nim tak pisałem dodatkowy walidator na to, że może być złożony wraz z innymi walidatorami . Wciąż dopiero zaczynam przyglądać się modułowi formularzy, więc nie spodziewaj się, że będzie to najbardziej efektywny kod lub działający w skrajnych przypadkach, ale to dobry początek. Działa w normalnych przypadkach i może służyć jako dobry punkt wyjścia dla innych.

Wykonane dla rc.4 i nowego modułu formularzy, część revalidateOnChanges może być okropna (nie jestem pewien najlepszego sposobu na spowodowanie tego zachowania), używaj na własne ryzyko! :)

Jak używać

Walidator przyjmuje dwa argumenty funkcji warunkowego, który jest podany na formGroup i że oczekuje się powrotu true jeżeli walidacja jest zastosowanie i false w przeciwnym wypadku, a walidator (która może być kompozycją). Uaktualni to pole, gdy grupa formularzy zostanie zaktualizowana, a obecnie można tylko sprawdzać rzeczy w tej samej grupie formularzy, ale powinno to być łatwe do naprawienia.

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

W tym przykładzie masz dwa pola, vehicleType i licencePlate. Instrukcja warunkowa zastosuje złożony weryfikator (wymagany i minLength), jeśli vehicleType jest "car".

Możesz użyć komponowania, aby zastosować kilka różnych warunków warunkowych, które mogą lub nie mogą obowiązywać w tym samym czasie. Tu jest nieco bardziej skomplikowany przykład:

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) 
      ), 
     ]) 
    ] 
}); 

W tym przypadku stosujemy wymagane jeśli typ jest „samochód” i stosujemy MINLENGTH jeśli kraj jest „Szwecja”. Jeśli tylko jeden warunek ma zastosowanie tylko wtedy, gdy zastosuje się oba warunki, wówczas obowiązują oba walidacje.

Sam

Należy zauważyć, że porównanie obiekt jest tylko proste brute force, ponieważ pracujemy z małego obiektu, jeśli używasz Ramda czy coś można wyciąć dużo kodu walidator.

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

UWAGA: pamiętaj, aby zaimportować operatora:
'rxjs/add/operator/distinctUntilChanged' przywóz;

1

Możemy użyć setValidators() aby ustawić dynamiczny walidacji wymagane po znalezieniu elementów sterujących -

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

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