0

I am working on creating a validator for a set of inputs in my reactive form.

this.transitionForm = this.fb.group({
  effectiveStartDate: [this.utils.dateToISO(startDate), Validators.compose([Validators.required, this.validateDates])],
  effectiveEndDate: [(endDate ? endDate : ''), Validators.compose([Validators.required, this.validateDates])],
});

/**
 * Checks to make sure the start and end date selected
 * are in an acceptable format.
 *
 * @param formGroup
 */
validateDates(formgroup: FormGroup) {

    const start = formgroup.controls["effectiveStartDate"].value;
    const end = formgroup.controls["effectiveEndDate"].value;

    /**
     *   Validation Logic
     * - End date cannot come before start date
     * - Start date cannot come after end date
     * - Start date cannot be empty
     * - End Date cannot be empty
     */
    if ((end < start) || (start > end) || !start || !end) {
        return { invalidDates: true };
    } else {
        return null;
    }
}

HTML:

<div *ngIf="!transitionForm.controls['effectiveEndDate'].valid">
 <p *ngIf="transitionForm.controls['effectiveEndDate'].errors.invalidDates">
  Invalid End Dates
 </p>
</div>

For some reason, my error isn't showing up when I leave my end date empty for example. I feel like maybe I am calling the validator incorrectly? Compose was the only method I could find for chaining together multiple validators, but I am not sure if I need to pass anything with it?

Update:

Here is my full existing form, removing the validator from the individual controls. It also shows that i currently have a validator in place for this form, perhaps incorrectly.

How could I include multiple?

this.transitionForm = this.fb.group({
    changeType: ['', Validators.required],
    effectiveStartDate: [this.utils.dateToISO(startDate), Validators.required],
    effectiveEndDate: [(endDate ? endDate : ''), Validators.required],

},
    {
        // Validate to make sure we selected at least one transition field
        validator: (formGroup: FormGroup) => {
            return this.validateFilter(formGroup);
        }
    });
1
  • If startDate is entered endDate is mandatory one ? need such type of validations ? Commented Jul 27, 2017 at 15:11

2 Answers 2

1

I agree with Deborah, on having a nested formgroup for your dates. Why? Performance wise, especially if you have many (other) fields in your form, setting it validating whenever any field in the form changes, it would be fired unnecessary many times, so definitely do something like Deborah presented:

...
dates: this.fb.group({
  effectiveStartDate: [this.utils.dateToISO(startDate), Validators.required],
  effectiveEndDate: [(endDate ? endDate : ''), Validators.required],
},{validator: this.validateDates})
...

where it will be fired only when changes happen to the dates group.

As for your custom validator, you do not need to check if the dates are present, since you already have Validators.required, so change your custom validator to:

validateDates(dates: FormGroup) { // here is the 'dates' formgroup (only)
  const start = dates.controls["effectiveStartDate"].value;
  const end = dates.controls["effectiveEndDate"].value;

  if ((end < start) || (start > end)) {
     return { invalidDates: true };
  } else {
     return null;
  }
}

And to the question for trouble with showing the error message in template. It will not work with errors.invalidDates, you could use hasError. In this case you can quite neatly present the error with:

<p *ngIf="transitionForm.hasError('invalidDates', 'dates')">
Sign up to request clarification or add additional context in comments.

Comments

1

When building a validator for a control group, you need to add the validator to the group, not to the individual controls.

    this.customerForm = this.fb.group({
        firstName: ['', [Validators.required, Validators.minLength(3)]],
        lastName: ['', [Validators.required, Validators.maxLength(50)]],
        emailGroup: this.fb.group({
            email: ['', [Validators.required, Validators.pattern('[a-z0-9._%+-]+@[a-z0-9.-]+')]],
            confirmEmail: ['', Validators.required],
        }, {validator: emailMatcher}),
        phone: '',
        notification: 'email',
        rating: ['', ratingRange(1, 5)],
        sendCatalog: true,
        addresses: this.fb.array([this.buildAddress()])
    });

Also, you no longer need the compose ... That was for an earlier version of Angular.

3 Comments

I updated my original post with the full form where I had a validator in place already. It looks quite different than yours as well. How could I include both?
I'm not sure I understand? To include two validators, include them in an array. I have an example in my code above where the email is required and must match a pattern.
I updated my example to show my entire form as well. Notice that the two fields (email in my example) are in their own form group. That group has the cross-field validation associated with it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.