5

basically, i have some form inputs whose validations are dependent on each other (i.e. if you're putting in a time range, the "from" time must be smaller than the "to" time) but i'm not exactly sure how to go about this.

Here is my form group:

this.form = this.fb.group({
  fromTime: ["", [Validators.required, CustomValidator.myValidationFunction(this.form.get("toTime").value)]],
  toTime: ["", [Validators.required]]
});

And here is my validator so far:

static myValidationFunction(testing) {
    const toTime = testing; // only comes here 1 time
    return control => {
      return toTime /*this value never changes*/  ? null : { test: {} };
    };
  }

but it seems as though the value x or toTime is only set the first time when the validator is created. is there a way to pass dynamic inputs to a custom validator?

I'm fairly new to angular, but have read the docs on custom form validation but can't seem to find my answer

philr
  • 1,860
  • 1
  • 21
  • 31
  • Did you try to return a function in your custom validation function ? – Nour Sep 18 '18 at 18:39
  • @Nour that's what i'm doing, but it seems the only thing i'm able to pass is the `FormControl` that's being validated – philr Sep 18 '18 at 18:42
  • 1
    Might this help you, where they use a validator on the entire form? https://medium.com/@realTomaszKula/angular-cross-field-validation-d94e0d063b61 – Jan B. Sep 18 '18 at 18:45
  • @Matt that link/example is very interesting and can help but it involves validating the entire form every time an input is changed as opposed to validation the input itself – philr Sep 18 '18 at 18:52
  • 1
    True, but there's nothing bad in pulling validation up to the FormGroup. It's actually quite similar considering domain driven design patterns, where entities (here: FormControl) sometimes cannot validate themselves and you need comprising logic in the service layer (here: FormGroup). Regarding the form-level validation, it's not less clean than field validation imo. The advantage is to have cross validation logic in one place and you'll able to access all containing `FormControls` and set ValidationErrors on both fields. – Jan B. Sep 18 '18 at 19:05
  • @Matt that makes sense. it seems i don't have a choice anyway so i will proceed with my validation at the FormGroup level. thanks for the help! – philr Sep 18 '18 at 19:10
  • 1
    I fully understand your concern. Your issue is quite easy in template-driven form design, but not straightforward in reactive forms. However, I think it's absolutely ok to go for that approach (which is pretty much the same that Buczkowski also suggested) – Jan B. Sep 18 '18 at 19:14

1 Answers1

2
static TimeValidator(formGroup) {
    const fromTime = formGroup.controls.fromTime;
    const toTime = formGroup.controls.toTime;

    if (fromTime.value && toTime.value) {
        if (toTime.value <= fromTime.value) {
            return {time: true};
        }

    }
    return null;
}

ngOnInit(): void {
    this.form = new FormGroup({
        fromTime: new FormControl('', Validators.required),
        toTime: new FormControl('', Validators.required)
    }, AppComponent.TimeValidator);

    this.form.controls.fromTime.setValue(2);
    this.form.controls.toTime.setValue(1);
}

and in html you can check by:

{{form.hasError('time')}}

Feel free to ask if you have some questions.

Buczkowski
  • 2,296
  • 12
  • 18
  • this will, in fact, solve my issue! one question: is there a way to know what input in particular has been changed to avoid validating my whole form every time any input has been modified? – philr Sep 18 '18 at 18:59
  • to be honest can't really help with that, I know that validators for certain ``FormControl`` get that form control reference in validator function as argument and in case of ``FormGroup`` the form group reference is passed. – Buczkowski Sep 18 '18 at 19:08
  • @philr Why do you want to vaoid running this Validator on every change? There shouldn't be any noticeable performance hit as soon as you don't trigger http calls or very expensive computations. – Jan B. Sep 18 '18 at 19:10