Menu

Create DatePicker input component from Vue Js

Each date picker component out there is created for a specific need and in some cases, you find some components are overwhelming, has unnecessary features (for you) or does not follow pest user experience (again as per your project)

One of the requirements I came across on the project that I am working on is to enable the user to enter the date, also format the date according to the user geo location, etc. Again this is tightly coupled with my requirements and might/might not serve your purpose.

But will give you a good starting point.

I am using few dependencies here

  1. bootstrap-datepicker
  2. vue-the-mask
  3. Moment.js

My back end is ASP.NET Core, therefore every date I send to server need to be YYYY-MM-DD format, and client display date can be according to the client, it can be DD/MM/YYYY .

Below is the code for the date picker.

<template>
    <div class="input-date" :class="{'invalid': hasError}" >
        <div class="input-group" :id="`${name}_datepicker_holder`">  
            <input type="text" 
            v-model="visualDate"             
            class="form-control" 
            :class="{'is-invalid': hasError}" 
            :id="name" 
            :name="name" 
            v-validate="validators"
            :autocomplete="autocomplete"
            :placeholder="placeholder"  
            v-on:blur="onblur()">
            <input type="text" :id="`${name}_datepicker`" class="hiddenDatePicker">
            <div class="input-group-append">
                <button type="button" class="btn btn-light" v-on:click='onShow()'><i class="ti-calendar"></i></button>
            </div>        
        </div>         
        <div class="invalid-feedback" v-show="vErrors.has(name)">{{ vErrors.first(name) }}</div>
    </div>
</template>
<script lang="ts">
import Vue from "vue";
import Component from "vue-class-component";
import { datePickerFormatHelper } from "../../shared/DatePickerFormatHelper"
import moment from "moment";
declare var $: any;
@Component({
   inject: ['$validator'],
   props:{
       name: String,
       value: String,
       placeholder: String,
       autocomplete: String,
       validators: String,
   },
   components: {     
   },
   watch:{
    value:"customValueChanged",
    visualDate:"visualValueChanged"
   }
})
export default class InputDateComponent extends Vue {
    serverFormat: string = "";
    visualFormats: any = [];
    visualDate: string = "";
    pickerName: string = "";
    show: boolean = false;   

    created(){
        this.serverFormat = "YYYY-MM-DD";
        this.visualFormats = datePickerFormatHelper.format();
        this.pickerName = `#${this.$props.name}_datepicker`;       
    }

    mounted(){   
        var self = this;       
        $(this.pickerName)
            .datepicker({ 
                autoclose: true,
                showOnFocus: false
            })
            .on("changeDate", function(e: any) {
                let date: Date = e.date;
                self.visualDate = moment(date).format(self.visualFormats[0]);
            })
            .on("hide", function(e: any) {
                self.show = false; 
            });
    }

    beforeDestroy() {
        $(this.pickerName).datepicker('hide').datepicker('destroy');
    }

    onShow()
    {
        if(!this.show)            
            $(this.pickerName).datepicker("show");          
        else
            $(this.pickerName).datepicker("hide");   

        this.show = !this.show; 
    }

    customValueChanged(value: any){ 
        var object:any = moment(value, this.serverFormat, true);
        if(object.isValid()){    
            this.visualDate = moment(object).format(this.visualFormats[0]);            
            const date: any = <Date>object["_d"];
            $(this.pickerName).datepicker('setDate', date);           
        } 
    }

    visualValueChanged()
    {
        if(this.visualDate.length == 0)
        {
            this.updateParent('');
            return;
        }       
    }    

onblur()
    {
        var object = moment(this.visualDate, this.visualFormats, true);
        if(object.isValid())
            this.updateParent(moment(object).format(this.serverFormat));
        else
            this.updateParent('-');  
    }
     updateParent(serverDate: string){
        this.$  emit('input', serverDate);
    }
}
</script>
<style scope>
.hiddenDatePicker{width: 0px; border: #fff;}
.input-group input[type="text"]{z-index: auto;border-right: none;}
.input-date .invalid-feedback{display: block;}
.input-date.invalid .btn-light{border-color: #f96868;color: #f96868;}
</style>
export default class DatePickerFormatHelper {
    public format(): string[]{
        const countryCode: string = // get the user country from location service, from server, from token or any other way - I use my jwt token ;
        switch(countryCode){
            case "US":
                return ["MM/DD/YYYY", "M/D/YYYY"] ;  //United States
            case "CN":
                return ["YYYY/MM/DD", "YYYY/M/D"];  //China                           
            default:
                return ["DD/MM/YYYY", "D/M/YYYY"]; // Australia, Mexico, Canada, New Zealand, Singapore, Indonesia, United Kingdom
        }
    }    
}

export const datePickerFormatHelper: DatePickerFormatHelper = new DatePickerFormatHelper();

 

Usage

<InputDate v-model="account.ProjectStartDate"                                          
           :autocomplete="'off'"
           :placeholder="'Project start date'"                                             
           :validators="'isValidDateFormat'"
           :name="'ProjectStartDate'"></InputDate>   
<div class="invalid-feedback" v-show="vErrors.has('ProjectStartDate')">{{ vErrors.first('ProjectStartDate') }}</div>

 

Leave a comment