john
john

Reputation: 11709

How to validate start date and end date in angular?

I am building component in angular in which I want start date to be greater than end date. I am wondering what changes I need to make in my HTML and TS codes to get it done. The snippets of HTML and TS codes which I am using are:

HTML:

<form class="unavailability-form" [formGroup]="unavailabilityForm" *ngIf="loaded">
<div class="component-title" matDialogTitle>
   {{'PORTAL.TEXTUNAVAILABILITY' | translate}}
</div>
<mat-toolbar>
   <div class="container"
      fxLayout="row"
      fxLayout.xs="column"
      fxLayoutGap.xs="0">
      <div>
         <h1>{{componentTitle}}</h1>
      </div>
   </div>
</mat-toolbar>
<mat-dialog-content>
   <div class="container" fxLayout="row" fxLayout.xs="column" fxLayoutGap.xs="0" fxLayoutGap="10px">
      <div class="item item-1" fxFlex="50%" fxFlexOrder="1">
         <mat-form-field>
            <input matInput [matDatepicker]="picker" placeholder="{{'PORTALSTARTDATE' | translate}}"  type="text" formControlName="startDate" [(ngModel)]="availability.startDate" [readonly]="!componentPermission.writePermission">
            <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
            <mat-datepicker #picker></mat-datepicker>
         </mat-form-field>
      </div>
      <div class="item item-2" fxFlex="50%" fxFlexOrder="1">
         <mat-form-field>
            <input matInput [matDatepicker]="picker" placeholder="{{'PORTAL.ENDDATE' | translate}}"  type="text" formControlName="endDate" [(ngModel)]="availability.endDate" [readonly]="!componentPermission.writePermission">
            <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
            <mat-datepicker #picker></mat-datepicker>
         </mat-form-field>
      </div>
   </div>
   <div class="item item-1" fxFlex="50%" fxFlexOrder="1">
      <mat-form-field>
         <input matInput placeholder="{{'PORTAL.UNAVAILABILITYREASON' | translate}}" type="text" formControlName="unavailabilityReason" [(ngModel)]="availability.unavailabilityReason" [readonly]="!componentPermission.writePermission">
      </mat-form-field>
   </div>
</mat-dialog-content>
<mat-dialog-actions>
   <div class="container" fxLayout="row" fxLayout.xs="column" fxLayoutGap.xs="0">
      <div class="item item-1" fxFlex="100%">
         <button mat-raised-button color="primary" [disabled]="!locationForm.valid || !componentPermission.writePermission" (click)="onSave()">{{'PORTAL.CONFIRM' | translate}}</button>
         <button mat-raised-button [matDialogClose]="canceled" color="primary">{{'PORTAL.CANCEL' | translate}}</button>
      </div>
   </div>
</mat-dialog-actions>
</form>

TS:

  validateForm() {
    this.unavailabilityForm = this.formBuilder.group({
      'startDate': [''],
      'endDate': [''],
      'unavailabilityReason': ['']
    });
  }

In the above code, {{PORTAL.___}} text refers to the value from the database.

Upvotes: 2

Views: 20936

Answers (3)

Nick
Nick

Reputation: 36

you can check out my library ng-swiss-army-knife which has this form validator inside (and many other useful javascript/typescript/angular stuff) https://github.com/nickwinger/ng-swiss-army-knife

Upvotes: 0

Habeeb
Habeeb

Reputation: 41

With reactive forms, here is how i do it.

  deploymentSignOffDatesValidator(g: FormGroup) {
      return g.get('dateOfSignOff').value > g.get('dateOfDeployment').value ? null : {'unsequencial': true};
    }

  createEditUserForm(item: SaveUser) {
    if (!item) {
      item = {
        dateOfDeployment: '',
        dateOfSignOff: '',
      }
    }
this.userEditForm = this.fb.group({
      dateOfDeployment: [item.dateOfDeployment, Validators.required],
      dateOfSignOff: [item.dateOfSignOff]
    }, {validator: this.deploymentSignOffDatesValidator});

<div class="col-xl-3 col-lg-6 col-md-12">
                                <fieldset class="form-group">
                                    <label for="dateOfSignOff">Date of SignOff:</label>
                                    <input
                                        class="form-control"
                                        placeholder="Date of SignOff"
                                        bsDatepicker
                                        formControlName="dateOfSignOff"
                                        [bsConfig]="{ dateInputFormat: 'YYYY-MM-DD', containerClass: 'theme-dark-blue' }"
                                        [ngClass]="{'is-invalid': userEditForm.get('dateOfSignOff').errors && (formDir.submitted || userEditForm.get('dateOfSignOff').touched) || userEditForm.hasError('unsequencial') && userEditForm.get('dateOfSignOff').touched}">
                                        <div class="" *ngIf="userEditForm.hasError('unsequencial') && userEditForm.get('dateOfSignOff').touched">The Deploymnet Date must be less than Sign Off Date</div>
                                </fieldset>                                
                            </div>

Upvotes: 0

kat1330
kat1330

Reputation: 5332

Please note that you cannot use ngModel within your form. You have to feed form from your component with data. Here is form with removed ngModel:

<mat-dialog-content>
   <form [formGroup]="unavailabilityForm " novalidate fxFlex>
      <div class="container" fxLayout="row" fxLayout.xs="column" fxLayoutGap.xs="0" fxLayoutGap="10px">
         <div class="item item-1" fxFlex="50%" fxFlexOrder="1">
            <mat-form-field>
               <input matInput [matDatepicker]="picker" placeholder="{{'PORTALSTARTDATE' | translate}}"  type="text" formControlName="startDate" [readonly]="!componentPermission.writePermission">
               <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
               <mat-datepicker #picker></mat-datepicker>
            </mat-form-field>
         </div>
         <div class="item item-2" fxFlex="50%" fxFlexOrder="1">
            <mat-form-field>
               <input matInput [matDatepicker]="picker" placeholder="{{'PORTAL.ENDDATE' | translate}}"  type="text" formControlName="endDate" [readonly]="!componentPermission.writePermission">
               <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
               <mat-datepicker #picker></mat-datepicker>
            </mat-form-field>
         </div>
      </div>
      <div class="item item-1" fxFlex="50%" fxFlexOrder="1">
         <mat-form-field>
            <input matInput placeholder="{{'PORTAL.UNAVAILABILITYREASON' | translate}}" type="text" formControlName="unavailabilityReason"  [readonly]="!componentPermission.writePermission">
         </mat-form-field>
      </div>
   </form>
</mat-dialog-content>

Then you need to initialize form and add FormGroup level validator to your group. Also I feed data to your form:

initForm() {
    this.unavailabilityForm = this.formBuilder.group({
      'startDate': [availability.startDate],
      'endDate': [availability.endDate],
      'unavailabilityReason': [availability.unavailabilityReason]
    }, validateDate);
  }

Your custom validator function in FormGroup will be validateDate.

Custom validator function should be like this:

function validateDate(group: FormGroup) {
  ///TODO: Implement some better validation logic
  const invalid = group.get('startDate').value > group.get('endDate').value;

  ///TODO: Implement some logic to mark controls dirty if is necessary.

  return invalid ? { 'invalidDate': true } : null;
}

In custom validator on FormGroup level you can you can access to all controls form that group and perform validation. Please note that value of your form will be string so, if you are working with Date you will need better logic to compare dates.

Also please not that group level validator maybe will not display validation feedback, so you will need manually to mark controls to be dirty if is necessary.

Upvotes: 2

Related Questions