Date Validations in Ionic 2


#1

Hi,

I am trying to create a form related to an event. I want to put start data, start time, end date, end time of an event and want to apply some validations. My validations have to be in below form:

  1. Event start data and time can only be greater than today and current time.
  2. Event end data and time can not be before start date and time.

Please suggest the validators which can be used to achieve the same. Here is my code:

HTML File:

<ion-header>
    <ion-navbar>
        <button ion-button menuToggle>
            <ion-icon name="menu"></ion-icon>
        </button>
        <ion-title>Add an Event</ion-title>
    </ion-navbar>
</ion-header>
<ion-content padding>
   <form [formGroup]="eventForm">
    <ion-list>
        <ion-item>
            <ion-label floating>Event Name</ion-label>
            <ion-input type="text" [(ngModel)]="name" formControlName="name" [class.invalid]="!eventForm.controls.name.valid && (eventForm.controls.name.dirty|| submitAttempt)"></ion-input>
        </ion-item>
        <ion-item *ngIf="!eventForm.controls.name.valid  && (eventForm.controls.name.dirty || submitAttempt)">
    <p>Please enter a valid Event Name.</p>
</ion-item>
        <ion-item>
            <ion-label floating>Description</ion-label>
            <ion-input type="text" [(ngModel)]="description" formControlName="description" [class.invalid]="!eventForm.controls.description.valid && (eventForm.controls.description.dirty|| submitAttempt)"></ion-input>
        </ion-item>
        <ion-item *ngIf="!eventForm.controls.description.valid  && (eventForm.controls.description.dirty || submitAttempt)">
    <p>Please enter a valid Description.</p>
</ion-item>
        <ion-item>
            <ion-label floating>Start Date</ion-label>
            <ion-datetime displayFormat="DD MMM YYYY" pickerFormat="DD MMM YYYY" [(ngModel)]="startdate" formControlName="startdate" [class.invalid]="!eventForm.controls.startdate.valid && (eventForm.controls.startdate.dirty|| submitAttempt)"></ion-datetime>
        </ion-item>
        <ion-item *ngIf="!eventForm.controls.startdate.valid  && (eventForm.controls.startdate.dirty || submitAttempt)">
    <p>Please enter a valid Start Date.</p>
</ion-item>
        <ion-item>
            <ion-label floating>Start Time</ion-label>
            <ion-datetime displayFormat="HH:mm" pickerFormat="HH mm" [(ngModel)]="starttime" formControlName="starttime" [class.invalid]="!eventForm.controls.starttime.valid && (eventForm.controls.starttime.dirty|| submitAttempt)"></ion-datetime>
        </ion-item>
        <ion-item *ngIf="!eventForm.controls.starttime.valid  && (eventForm.controls.starttime.dirty || submitAttempt)">
    <p>Please enter a valid Start Time.</p>
</ion-item>
        <ion-item>
            <ion-label floating>End Date</ion-label>
            <ion-datetime displayFormat="D MMM, YY" pickerFormat="DD MMM YYYY" [(ngModel)]="enddate" formControlName="enddate" [class.invalid]="!eventForm.controls.enddate.valid && (eventForm.controls.enddate.dirty|| submitAttempt)"></ion-datetime>
        </ion-item>
        <ion-item *ngIf="!eventForm.controls.enddate.valid  && (eventForm.controls.enddate.dirty || submitAttempt)">
    <p>Please enter a valid End Date.</p>
</ion-item>
        <ion-item>
            <ion-label floating>End Time</ion-label>
            <ion-datetime displayFormat="HH:mm" pickerFormat="HH mm" [(ngModel)]="endtime" formControlName="endtime" [class.invalid]="!eventForm.controls.endtime.valid && (eventForm.controls.endtime.dirty|| submitAttempt)"></ion-datetime>
        </ion-item>
        <ion-item *ngIf="!eventForm.controls.endtime.valid  && (eventForm.controls.endtime.dirty || submitAttempt)">
    <p>Please enter a valid End Time.</p>
</ion-item>
        <ion-item>
            <ion-label floating>Location</ion-label>
            <ion-input type="text" [(ngModel)]="location" formControlName="location" [class.invalid]="!eventForm.controls.location.valid && (eventForm.controls.location.dirty|| submitAttempt)"></ion-input>
        </ion-item>
        <ion-item *ngIf="!eventForm.controls.location.valid  && (eventForm.controls.location.dirty || submitAttempt)">
    <p>Please enter a valid Location.</p>
</ion-item>
    <ion-row>
        <ion-col width=50>
            <button full ion-button>Cancel</button>
        </ion-col>
        <ion-col width=50>
            <button [disabled]="!eventForm.valid" full ion-button (click)="saveEventDetails( name, description, startdate, starttime, enddate, endtime, location)">Save</button>
        </ion-col>
    </ion-row>
    </ion-list>
    </form>
</ion-content>

TypeScript:

import { Component } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { UserData } from '../../../providers/user-data';
import { AngularFire, FirebaseListObservable } from 'angularfire2';
import { EventListPage } from '../../../pages/event/event-list/event-list';
import { Validators, FormControl, FormGroup, FormBuilder} from '@angular/forms';

/*
  Generated class for the Addevent page.

  See http://ionicframework.com/docs/v2/components/#navigation for more info on
  Ionic pages and navigation.
*/
@Component({
  selector: 'page-addevent',
  templateUrl: 'addevent.html'
})
export class AddeventPage {
	classid: any;
	events: FirebaseListObservable<any[]>;
	eventForm: FormGroup;
	submitAttempt: boolean = false;

  newEvent = {
    name:'',
        description: '',
        startDate: '',
        startTime: '',
        endDate: '',
        endTime: '',
        location: '',
        placeid: '',
        classid: ''
  };
	role: any;


  constructor(public navCtrl: NavController, public navParams: NavParams, public userData:UserData, public af:AngularFire, public formBuilder: FormBuilder) {
  	this.classid=this.navParams.data;
  	this.events = this.af.database.list('/events');
		this.eventForm = formBuilder.group({
        id: [''],
				name:['', Validators.compose([Validators.maxLength(50), Validators.pattern('[a-zA-Z ]*'), Validators.required])],
  			description: ['', Validators.compose([Validators.maxLength(150), Validators.pattern('[a-zA-Z ]*'), Validators.required])],
  			startdate: ['', Validators.compose([Validators.required])],
  			starttime: ['', Validators.compose([Validators.required])],
  			enddate: ['', Validators.compose([Validators.required])],
  			endtime: ['', Validators.compose([Validators.required])],
  			location: ['', Validators.compose([Validators.maxLength(150), Validators.pattern('[a-zA-Z ]*'), Validators.required])]
    });
		this.userData.myRole$.subscribe( data => {
      this.role = data;
    });
  }

  saveEventDetails( name, description, startdate, starttime, enddate, endtime, location){
  	this.submitAttempt = true;
		var newEvent = {
  			name: name,
  			description: description,
  			startDate: startdate,
  			startTime: starttime,
  			endDate: enddate,
  			endTime: endtime,
  			location: location,
  			placeid: this.userData.placeid,
  			classid: this.classid
  		}
  		this.events.push (newEvent).then( data => {
  		console.log("New Event Created");
  			console.log(newEvent);
  			this.navCtrl.push(EventListPage);
  		}, error => {
  			console.log(error);
  		});
}
}

#2

Take a look at this library to see if it can help:


#3

Thanks. Would you be able to share a working example of the same.


#4

Hi! Yo actually don’t need validation here, you can restrict user to put in invalid date by adding [min] property to your ion-datetime elements. It should look like so:

<ion-datetime displayFormat="D MMM, YY" pickerFormat="DD MMM YYYY" [(ngModel)]="startdate" formControlName="enddate" [min]="minStartDate()"></ion-datetime>

<ion-datetime displayFormat="D MMM, YY" pickerFormat="DD MMM YYYY" [(ngModel)]="enddate" formControlName="enddate" [min]="minEndDate()"></ion-datetime>

and the functions should be like so:

minStartDate(): string {
  return moment().format('YYYY-MM-DD');
}

minEndDate(): string {
  return moment(this.startdate).add(1, 'day').format('YYYY-MM-DD');
}

#5

Incidentally, do not use constructs like form.controls.name. They will break in production. Use form.get('name') instead.


#6

Also you can add this to your startDate datetime-picker:

(ionChange)="changeEndDate()"

changeEndDate(): void {
  if (moment(this.startDate).isSameOrAfter(this.endDate)) {
      this.endDate = moment(this.startDate).add(1, 'day').format('YYYY-MM-DD');
    }
}

Just be sure that you’re setting the right format of the date.


#7

I would urge you to join the folks who have ditched moment in favor of date-fns. It is much more amenable to dead code reduction and does not require using special object types.


#8

Could you, please, be more specific? It’s very hard to understand what you mean


#9

Cool. It worked well. Do you suggest to use single field for data & time both? Can you suggest the similar logic for that as well.


#10

Actually, I don’t really know if it’s better to set date and time in one input or in separate inputs. It depends on your needs. if you decide to use one input then you just have to change format() argument for ‘YYYY-MM-DDTHH:mm’


#11

Thanks. Can you please share the code sample to achieve the desired result?


#12

Do we have a library like angular2-moment which has pipes for angular2 like amTimeAgo etc.


#13

TBH, I never saw the value in angular2-moment. It is trivially simple to roll your own pipes like that. For example, here’s amTimeAgo:

import {Injectable, Pipe, PipeTransform} from "@angular/core";
let distanceInWordsToNow = require('date-fns/distance_in_words_to_now');

@Pipe({
  name: 'timeago'
})
@Injectable()
export class TimeAgoPipe implements PipeTransform {
  transform(d: Date | string): string {
    return distanceInWordsToNow(d);
  }
}