Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 form validations start date <= end date

I'm trying to add validations such that the end date can't be before the start date. Unfortunately I have no idea how to do that, and I didn't find any helpful advice in the internet so far. My form looks like this:

editAndUpdateForm(tageler: Tageler) {
    this.tageler = tageler;
    this.tagelerForm = this.fb.group({
      title: [this.tageler.title, Validators.required],
      text: this.tageler.text,
      group: [[this.tageler.group], Validators.required],
      date_start: new Date(this.tageler.start).toISOString().slice(0, 10),
      date_end: new Date(this.tageler.end).toISOString().slice(0, 10),
      ...
    });
    this.tagelerForm.valueChanges
       .subscribe(data => this.onValueChanged(data));
    }

My validations so far:

onValueChanged(data?: any) {
    if (!this.tagelerForm) {
      return;
    }
    const form = this.tagelerForm;

    for (const field in this.formErrors) {

    // clear previous error message (if any)
      this.formErrors[field] = '';
      const control = form.get(field);

      if (control && control.dirty && !control.valid) {
        const messages = this.validationMessages[field];
        for (const key in control.errors) {
          this.formErrors[field] += messages[key] + ' ';
        }
      }
    }
}

validationMessages = {
    'title': {
      'required': 'Geben Sie bitte einen Namen ein.',
    },
    'group': {
      'required': 'Wählen Sie bitte eine Gruppe aus.'
    },
    'bringAlong': {
      'required': 'Bitte Feld ausfüllen.'
    },
    'uniform': {
      'required': 'Bitte Feld ausfüllen.'
    },
};

formErrors = {
    'title': 'Geben Sie bitte einen Namen ein.',
    'group': 'Wählen Sie bitte eine Gruppe aus.',
    'bringAlong': 'Bitte Feld ausfüllen',
    'uniform': 'Bitte Feld ausfüllen',
};

The the form-controls 'date_start' & 'date_end' contain a date-string of the form 'dd.MM.yyyy', and I want 'date_end' to be bigger or equal 'date_start'.

I'd like to directly display the error message (my html code looks like this:)

<label for="formControlName_date_end" class="col-3 col-form-label">Ende:</label>
      <div class="col-5">
        <input id="formControlName_date_end" class="form-control" formControlName="date_end" type="date" value="{{tageler.end | date: 'yyyy-MM-dd'}}">
      </div>
      <div *ngIf="formErrors.date_end" class="alert alert-danger">
        {{ formErrors.date_end }}
      </div>

Could someone help me?

Thanks!

like image 828
Ramona Avatar asked May 04 '17 09:05

Ramona


2 Answers

Based on the answer of santiagomaldonado I have created a generic ValidatorFn that can be used in multiple Reactive Forms with a dynamic return value.

export class DateValidators {
    static dateLessThan(dateField1: string, dateField2: string, validatorField: { [key: string]: boolean }): ValidatorFn {
        return (c: AbstractControl): { [key: string]: boolean } | null => {
            const date1 = c.get(dateField1).value;
            const date2 = c.get(dateField2).value;
            if ((date1 !== null && date2 !== null) && date1 > date2) {
                return validatorField;
            }
            return null;
        };
    }
}

Import the validator and use it like this in your formgroup validators.

    this.form = this.fb.group({
        loadDate: null,
        deliveryDate: null,
    }, { validator: Validators.compose([
        DateValidators.dateLessThan('loadDate', 'deliveryDate', { 'loaddate': true }),
        DateValidators.dateLessThan('cargoLoadDate', 'cargoDeliveryDate', { 'cargoloaddate': true })
    ])});

Now you can use the validation in HTML.

<md-error *ngIf="form.hasError('loaddate')">Load date must be before delivery date</md-error>
like image 70
Sander Oosterwijk Avatar answered Oct 30 '22 18:10

Sander Oosterwijk


You can also do it with Reactive Forms. The FormBuilder API lets you add custom validators.

Valid keys for the extra parameter map are validator and asyncValidator

Example:

import { Component }  from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'reactive-form',
  templateUrl: './reactive-form.html'
})
export class ReactiveFormComponent {
  
  form: FormGroup

  constructor(private fb: FormBuilder){
    this.createForm();
  }

  createForm() {
    this.form = this.fb.group({
      dateTo: ['', Validators.required ],
      dateFrom: ['', Validators.required ]
    }, {validator: this.dateLessThan('dateFrom', 'dateTo')});
  }
  
  dateLessThan(from: string, to: string) {
   return (group: FormGroup): {[key: string]: any} => {
    let f = group.controls[from];
    let t = group.controls[to];
    if (f.value > t.value) {
      return {
        dates: "Date from should be less than Date to"
      };
    }
    return {};
   }
 }
      
}

Note that I'm comparing the values of the inputs date and from with >, but by default this would be comparing strings. In the live example I'm using angular-date-value-accessor and importing the directive useValueAsDate.

<input formControlName="dateFrom" type="date" useValueAsDate />

With this directive group.controls[from].value and group.controls[to].value returns Date and then I can compare them with <.

Live example in plunkr

Credits to Dave's answer

like image 20
gabrielgiussi Avatar answered Oct 30 '22 18:10

gabrielgiussi