1

When a user inputs a numeric date such as 03151989 into the input, I would like it to auto format into 03/15/1989 as they are typing.

I cannot for the life of me figure out how to do this. I've tried a regex but if you type more than one number at a time it breaks it.

I've tried changing the input type to date, but that causes issues with mat-datepicker and I get an error:

The specified value "3/15/1989" does not conform to the required format, "yyyy-MM-dd".

Does anyone know how to fix this error or how to correct the issue that I'm having?

I wish angular material would have a solution to this, but even on their documentation page it seems to not format, and also lets you type letters and other characters into the input.

Here is an example of the code I'm working with:

   <mat-form-field class="form-element span-1-2">
    <mat-label>Test *</mat-label>
    <input
      matInput
      [matDatepicker]="datePicker"
      formControlName="test"
      type="date"
    />
    <mat-datepicker-toggle
      matSuffix
      [for]="datePicker"
    ></mat-datepicker-toggle>
    <mat-datepicker #datePicker></mat-datepicker>
  </mat-form-field>

2 Answers 2

2

You could assign the input an ID, change the type back to text, and then use vanilla javascript to target the input, apply the desired format as you type, and restrict to only numbers.

    <input id="dateInput" matInput [matDatepicker]="datePicker" formControlName="test" 
type="text" />

Check out this codepen, I think it's achieving the format you're looking for: https://codepen.io/tutsplus/pen/KMWqRr

Sign up to request clarification or add additional context in comments.

Comments

0

I you use material date picker with moment you can use this directive that simulate behaviohr of mui x date picker. If you not use moment you can pass in the formcontrol this.formControl.setValue(valueOfInput.format("YOUR DATE FORMAT"));

import { Directive, ElementRef, AfterViewInit, HostListener, Renderer2, Input } from '@angular/core';
import { AbstractControl, NgControl } from '@angular/forms';
import moment from 'moment-timezone';


@Directive({
    selector: '[dateInputMaterialController]'
})
export class DateInputMaterialControllerDirective implements AfterViewInit {
    constructor(
        private el: ElementRef<HTMLInputElement>,
        private control: NgControl,
        private renderer: Renderer2) { }

    @Input('dateInputMaterialController')
    inputFormatDate: string;

    formatDate: string = "DD/MM/YYYY HH:mm";


    formControl: AbstractControl
    nativeInput: any;

    highlightedIndex = 0;
    charEventIndex = 0;

    highlightedOptions: {
        [x: string]: any;
        patternGroup: string;
        amountName: "days" | "months" | "years" | "hours" | "minutes";
        startIndex?: number;
        endIndex?: number
    }[] = [{
        patternGroup: "DD",
        amountName: "days",
        setName: "date",

        minValue: 1,
        maxValue: 31,

        startIndex: 0,
        endIndex: 2,
    },
    {
        patternGroup: "MM",
        amountName: "months",
        setName: "month",

        offset: -1,
        minValue: 0,
        maxValue: 11,

        startIndex: 3,
        endIndex: 5,
    },
    {
        patternGroup: "YYYY",
        amountName: "years",
        setName: "y",

        minValue: 0,
        maxValue: 9999,

        startIndex: 6,
        endIndex: 10,
    },
    {
        patternGroup: "HH",
        amountName: "hours",
        setName: "hour",

        minValue: 0,
        maxValue: 23,

        startIndex: 11,
        endIndex: 13,
    },
    {

        patternGroup: "mm",
        amountName: "minutes",
        setName: "minute",

        minValue: 0,
        maxValue: 59,

        startIndex: 14,
        endIndex: 16,
    },
        ];

    removeSelection() {
        if (window.getSelection) {
            if (window.getSelection().empty) {  // Chrome
                window.getSelection().empty();
            } else if (window.getSelection().removeAllRanges) {  // Firefox
                window.getSelection().removeAllRanges();
            }
        }/* else if (document?.selection) {  // IE?
            document?.selection?.empty();
          }*/
    }

    createSelection(start, end) {
        let field = this.nativeInput;
        if (field.createTextRange) {
            var selRange = field.createTextRange();
            selRange.collapse(true);
            selRange.moveStart('character', start);
            selRange.moveEnd('character', end);
            selRange.select();
        } else if (field.setSelectionRange) {
            field.setSelectionRange(start, end);
        } else if (field.selectionStart) {
            field.selectionStart = start;
            field.selectionEnd = end;
        }
        field.focus();
    }




    @HostListener('focus')
    onFocusDate() {
        //console.log("focus")

        //se la data non è valida setta con quella odierna e ore 00:00 
        let valueOfInput = moment(this.nativeInput.value, this.formatDate);
        if(!valueOfInput.isValid()){
            valueOfInput = moment();
            valueOfInput.set({hour:0, minute:0});
            this.formControl.setValue(valueOfInput);
        }

        this.highlightedIndex = 0;
        this.charEventIndex = 0;
        this.highlightGroup(this.highlightedIndex);
    }

    @HostListener('click')
    onClickDate() {
        //console.log("clicked")
        let input: any = this.el.nativeElement;
        if (input.selectionStart || input.selectionStart == '0') {
            let carPos = input.selectionStart;
            let h = 0;
            for (let i = 0; i < this.highlightedOptions.length; i++) {
                let g = this.highlightedOptions[i];
                h = i;
                if ((g.endIndex) >= carPos) {
                    break;
                }

            }
            this.highlightedIndex = h;
            this.charEventIndex = 0;
        }

        this.highlightGroup(this.highlightedIndex);
    }
    highlightGroup(index) {
        let g = this.highlightedOptions[index];
        this.createSelection(g.startIndex, g.endIndex);
    }


    @HostListener('keydown', ['$event'])
    onkeyDownDate(event: KeyboardEvent) {

        

        let nextPatternGroup = false;

        // se premi tab vai al successivo gurppo 
        if (event.key == 'Tab') {
            nextPatternGroup = true;
            if (this.highlightedIndex == (this.highlightedOptions.length - 1)) { return; }
        }
        event.preventDefault();

        const valueOfInput = moment((event.target as any).value, this.formatDate);
        let g = this.highlightedOptions[this.highlightedIndex];

        // in caso di numerici
        if (event.key.match(/[0-9]/)) {
            const isLastCharOfTheGroup = this.charEventIndex == (g.endIndex - g.startIndex) - 1;
            // prendi la data dal input e sostituisci carattere indice charEventIndex
            let dataGroup: any = valueOfInput.format(g.patternGroup);
            let replaceIndex = this.charEventIndex; //<-- partiamo dalle cifre più significative (da destra a sinistra)
            // se invece vuoi partire dalle cifre unitarie (da sinistra a destra) decommenta questo
            //replaceIndex = g.endIndex-g.startIndex-this.charEventIndex-1;
            dataGroup = parseInt(dataGroup.slice(0, replaceIndex) + "" + event.key + "" + dataGroup.slice(replaceIndex + 1)) + (g.offset ?? 0)
            if (dataGroup < g.minValue) {
                dataGroup = g.minValue
            }
            const maxVal = g.patternGroup == "DD" ? valueOfInput.daysInMonth() : g.maxValue ;
            if (dataGroup > maxVal) {
                if (isLastCharOfTheGroup) {
                    dataGroup = maxVal
                } else {
                    //padding 0 a sinistra
                    dataGroup = dataGroup.toString();
                    let zeros = "0".repeat(g.endIndex - g.startIndex - this.charEventIndex - 1);
                    dataGroup = parseInt(zeros + "" + event.key + "") + (g.offset ?? 0)
                    //prossimo gruppo 
                    this.charEventIndex = 0;
                    nextPatternGroup = true;
                }
            }

            valueOfInput.set(g.setName, dataGroup);
            //valueOfInput.date(dataGroup)
            //console.log(valueOfInput.format(this.fomatDate));
            this.formControl.setValue(valueOfInput);

            if (isLastCharOfTheGroup) {
                this.charEventIndex = 0;
                nextPatternGroup = true;

            } else {
                this.charEventIndex++;

            }
            this.highlightGroup(this.highlightedIndex)
            //return;
        }




        if (event.key === 'ArrowLeft' || event.key == 'Backspace') {
            if (this.highlightedIndex > 0) {
                this.charEventIndex = 0;
                this.highlightGroup(--this.highlightedIndex)
            }
        } else if (event.key === 'ArrowRight' || nextPatternGroup) {

            if (this.highlightedIndex < (this.highlightedOptions.length - 1)) {
                this.charEventIndex = 0;
                this.highlightGroup(++this.highlightedIndex)
            }
        } else if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
            let momentVal = valueOfInput.add(event.key === 'ArrowUp' ? 1 : -1, g.amountName);
            this.formControl.setValue(momentVal);
            this.charEventIndex = 0;
            this.highlightGroup(this.highlightedIndex)
        }
    }


    ngAfterViewInit() {
        this.formControl = this.control?.control;
        this.nativeInput = this.el.nativeElement;

        if (this.inputFormatDate) {
            this.formatDate = this.inputFormatDate;
            this.highlightedOptions.forEach(opt => {
                opt.startIndex = this.formatDate.indexOf(opt.patternGroup);
                opt.endIndex = opt.patternGroup.length + opt.startIndex
            })
            this.highlightedOptions = this.highlightedOptions.filter(opt => opt.startIndex != 1).sort((a, b) => a.startIndex - b.startIndex);
        }
    }
}

Usage:

<mat-form-field floatLabel="never" [class.mat-form-field-invalid]="form.hasError('dateRangeError')">
                                            <input matInput [matDatepicker]="searchFormPickerFrom" **dateInputMaterialController="DD/MM/YYYY"**
                                                placeholder="DD/MM/YYYY" formControlName ="startDate">
                                            <mat-datepicker-toggle matSuffix [for]="searchFormPickerFrom">
                                            </mat-datepicker-toggle>
                                            <mat-datepicker #searchFormPickerFrom></mat-datepicker>
                                        </mat-form-field>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.