2016-03-20 6 views
17

Как мне следует условно требовать поля формы? Я сделал специальный валидатор, но условные переменные, которые я передаю в пользовательский валидатор, являются статическими и остаются их начальными значениями. Каким должен быть мой пользовательский валидатор для получения обновленных условных значений? Возможно, есть способ сделать это с помощью Validators.required вместо пользовательского валидатора?Угловые 2 условные валидаторы.

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 мая 2016)

Прошло много времени с тех пор отправляю это, но я хотел бы сослаться на .include() и .exclude() методы, доступные на ControlGroup класс для тех, кто там который пытается создать эту функциональность. (docs) Несмотря на то, что существуют, вероятно, варианты использования условного валидатора, как указано выше, я обнаружил, что включение и исключение элементов управления, групп управления и массивов управления - отличный способ справиться с этим. Просто установите валидатор required на элемент управления, который вы хотите, и включите/исключите его, как вам будет угодно. Надеюсь, это поможет кому-то!

+1

выглядит хорошо для меня ;-) –

+0

Но когда я изменяю 'Foo' к подлинному в другом месте, валидатор все еще показывает 'foo' как false. Выбрасывание 'console.log (условия)' в валидаторе возвращает '[false, false]', когда я знаю, что 'foo' действительно истинно. Мне нужно получить '[true, false]'. Я предполагаю, что это потому, что условия задаются один раз, потому что они находятся внутри конструктора. Любой способ обойти это поведение? –

+1

Ошибка связи с документами в обновлении – surfbird0713

ответ

5

После вашего комментария я вижу потенциальную проблему. Поскольку вы предоставляете условия как примитивные типы функции, которая создает функцию валидаторов, будут использоваться значения при вызове первого. Даже если они меняются после, новые значения не будут учитываться.

Чтобы заархивировать, что вам нужно использовать объект для условий, как описано ниже:

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

Таким образом, параметр условия могут быть использованы/обновлены ссылки. Чтобы обновить условия, вам нужно сделать следующее:

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

Вот plunkr: https://plnkr.co/edit/bnX7p0?p=preview.

Редактировать

Чтобы запустить валидатор при обновлении условий, вам необходимо явно вызвать updateValueAndValidity метод контроля. В этом случае атрибут как контроль и формы valid будут обновлены соответствующим образом:

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

К сожалению, я получаю ту же проблему: первое условие ложно, а второе - true. –

+0

Значения по-прежнему не изменяются при изменении значения 'foo' –

+1

На самом деле вам необходимо обновить объект условий. Я обновил свой ответ и предоставил рабочий планк ... –

2

Я создал валидатор, который принимает функцию обратного вызова, которая позволяет мне сделать валидатор для повторного использования, но я не могу найти хороший способ убедиться, что я вызываю updateValueAndValidity() в элементе управления с помощью этого валидатора без необходимости вручную вызывать его при изменении значения другого элемента управления.

Validator

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

Компонент Page

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

Я хотел более общий вариант, так что я написал дополнительную валидатор для него, которые могут быть в составе вместе с другими валидаторов , Я все еще только начинаю смотреть на модуль форм, поэтому не ожидайте, что это будет самый эффективный код когда-либо или для работы в крайних случаях, но это хороший старт. Он работает для нормальных случаев использования и может служить хорошей отправной точкой для других.

Сделано для rc.4 и модуля новых форм, часть revalidateOnChanges может быть ужасной (не уверена в лучшем способе вызвать это поведение), используйте на свой страх и риск! :)

Как пользоваться

валидатор принимает два аргумента, условную функцию, которая дается formGroup и что, как ожидается, возвращает истину, если проверка заключается в применении и ложь в противном случае, и валидатор (который может быть композицией). Он обновит поле, когда будет обновлена ​​группа form, и теперь она может проверять только вещи внутри одной группы форм, но это должно быть легко исправить.

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

В этом примере у вас есть два поля: vehicleType и licencePlate. В условном заявлении применяется составленный валидатор (требуется и minLength), если vehicleType является «автомобилем».

Вы можете использовать compose для применения нескольких разных условных обозначений, которые могут или не могут применяться одновременно. Вот несколько более сложный пример:

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

В этом случае мы применяем требуется, если тип «автомобиль» и применяем MINLENGTH, если страна «Швеция». Если только одно условие применяется только к тому, что будет применяться валидация, если оба условия применяются, применяются оба подтверждения.

Самого

Обратите внимание, что сравнение объектов является лишь простым перебором, потому что мы работаем с небольшим предметом, если вы используете Ramda или что-то можно были вырезать много кода валидатор.

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

ПРИМЕЧАНИЕ: не забудьте импортировать оператор:
импорт 'rxjs/добавить/оператор/distinctUntilChanged';

1

Мы можем использовать setValidators() метод для установки динамической проверки для необходимости после нахождения управления -

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

setRequired() { 
     if(1==1) { 
      return [Validators.required]; 
     } else { 
      return []; 
     } 
    } 
Смежные вопросы