Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mask support for Calendar #1426

Open
vadjs opened this issue Nov 25, 2016 · 53 comments
Open

Mask support for Calendar #1426

vadjs opened this issue Nov 25, 2016 · 53 comments
Labels
Type: New Feature Issue contains a new feature or new component request
Milestone

Comments

@vadjs
Copy link

vadjs commented Nov 25, 2016

I'm submitting a ... (check one with "x")

[ ] bug report => Search github for a similar issue or PR before submitting
[x] feature request => Please check if request is not on the roadmap already https://github.com/primefaces/primeng/wiki/Roadmap
[ ] support request => Please do not submit support request here, instead see http://forum.primefaces.org/viewforum.php?f=35

Current behavior

Now InputMask is not possible to be used in calendar input.

Expected behavior

Will be great if calendar will have option that will allow to use InputMask based on the dateFormat option.

What is the motivation / use case for changing the behavior?

That feature will improve user experience when he inputs data from the keyboard.

@yfain
Copy link
Contributor

yfain commented Dec 16, 2016

After this issue will be resolved, we'll consider switching from Wijmo to PrimeNG. As of now we use both.

@cagataycivici cagataycivici added the Type: New Feature Issue contains a new feature or new component request label Jun 7, 2017
@cagataycivici cagataycivici changed the title Support InputMask in calendar Mask support for Calendar Jun 7, 2017
@Da13Harris
Copy link

In our enterprise environment, we currently use another third-party mask plugin (angular2-text-mask) in tandem with the PrimeNG calendar control. While we've managed to make this work, the solution is not ideal and often introduces bugs that are difficult to resolve.

An option on the PrimeNG calendar to apply an input mask would be much appreciated. It might be even more ideal if the PrimeNG input mask was just a dynamic directive that could be applied to either native inputs or other PrimeNG components, including the calendar/datepicker.

@sousajunior
Copy link
Contributor

sousajunior commented Oct 9, 2017

@Da13Harris I added a issue that describe your request.
Change InputMask component to a directive

@UlricW
Copy link

UlricW commented Apr 5, 2018

anyone found a solution for this issue ?

@leonardolessa
Copy link

👍

@sandeepGhotra
Copy link

sandeepGhotra commented May 8, 2018

if anyone found solution for this issue then please share it with me .

@thenninger
Copy link

👍

@3em
Copy link

3em commented May 29, 2018

so noth no changed?

@Choubal
Copy link

Choubal commented Jun 1, 2018

👍

@vinothbabu
Copy link

Is this planned for any release?

@thenninger
Copy link

I'm already getting negative feedback from my company about the calendars' lack of an input mask. Please add this feature!

@UlricW
Copy link

UlricW commented Aug 17, 2018

Is it possible to have a feedback to know if this "new feature" is planned or not ?
cause it's an important feature for a lot of people..

@icarodebarros
Copy link

I did a paleative solution without a third-party mask plugin using the event the component send. My code is fresh, so it needs to be worked out. I did the algoritm by intercepting the input element (event.path[0]):

<p-calendar
    ...
    dateFormat="dd/mm/yy"
    (onInput)="onInputDate($event)" (onBlur)="onBlurDate()"
    placeholder="dd/mm/aaaa" [locale]="ptBR"
    ...
></p-calendar>
    onInputDate(event): void {
        let cursorPosition = event.path[0].selectionEnd;

        if (event.inputType === 'deleteContentBackward' && (cursorPosition === 2 || cursorPosition === 5)) {
            event.path[0].value = event.path[0].value.substring(0, cursorPosition - 1) + event.path[0].value.substring(cursorPosition);
            cursorPosition --;
        }
        if (event.inputType === 'insertText' && (event.path[0].value.length > 10)) {
            event.path[0].value = event.path[0].value.substring(0, event.path[0].value.length - 1);
        }

        this.dateMask = event.path[0].value.toString();
        this.dateMask = this.dateMask.replace(/\D/g, '');

        let mask = '';
        for (let i = 0; i < this.dateMask.length; i++) {
            mask += this.dateMask[i];
            if (i === 1 || i === 3) {
                mask += '/';
                if (cursorPosition === 2 || cursorPosition === 5) { cursorPosition++; }
            }
        }
        event.path[0].value = mask.toString();
        event.path[0].selectionStart = cursorPosition;
        event.path[0].selectionEnd = cursorPosition;

        if (event.path[0].value.length === 10) {
            const dt = this.stringToDate(event.path[0].value);
            if (this.isValidDate(dt)) {
                this.value = dt;
            }
        }
    }

    onBlurDate(): void {
        if (!!this.value && !this.isValidDate(this.value)) {
            this.value = null;
        }
    }

I know that is not a good looking solution, but maybe it could help some one.

@xandecf
Copy link

xandecf commented Oct 19, 2018

Any updated on this issue? @cagataycivici

@anafreitas
Copy link

👍

@thenninger
Copy link

thenninger commented Nov 27, 2018

I did a paleative solution without a third-party mask plugin using the event the component send. My code is fresh, so it needs to be worked out. I did the algoritm by intercepting the input element (event.path[0]):

<p-calendar
    ...
    dateFormat="dd/mm/yy"
    (onInput)="onInputDate($event)" (onBlur)="onBlurDate()"
    placeholder="dd/mm/aaaa" [locale]="ptBR"
    ...
></p-calendar>
    onInputDate(event): void {
        let cursorPosition = event.path[0].selectionEnd;

        if (event.inputType === 'deleteContentBackward' && (cursorPosition === 2 || cursorPosition === 5)) {
            event.path[0].value = event.path[0].value.substring(0, cursorPosition - 1) + event.path[0].value.substring(cursorPosition);
            cursorPosition --;
        }
        if (event.inputType === 'insertText' && (event.path[0].value.length > 10)) {
            event.path[0].value = event.path[0].value.substring(0, event.path[0].value.length - 1);
        }

        this.dateMask = event.path[0].value.toString();
        this.dateMask = this.dateMask.replace(/\D/g, '');

        let mask = '';
        for (let i = 0; i < this.dateMask.length; i++) {
            mask += this.dateMask[i];
            if (i === 1 || i === 3) {
                mask += '/';
                if (cursorPosition === 2 || cursorPosition === 5) { cursorPosition++; }
            }
        }
        event.path[0].value = mask.toString();
        event.path[0].selectionStart = cursorPosition;
        event.path[0].selectionEnd = cursorPosition;

        if (event.path[0].value.length === 10) {
            const dt = this.stringToDate(event.path[0].value);
            if (this.isValidDate(dt)) {
                this.value = dt;
            }
        }
    }

    onBlurDate(): void {
        if (!!this.value && !this.isValidDate(this.value)) {
            this.value = null;
        }
    }

I know that is not a good looking solution, but maybe it could help some one.

@icarodebarros This did help me - I just had to change event.path[0] to event.target because only Chrome supports path currently as it is not standard. I also did not need to use the blur event.

@fdezdam
Copy link

fdezdam commented Dec 18, 2018

Any news about this?

@mikaelboff
Copy link

Nothing about this yet??

@tayloraharry
Copy link

this is so annoying. should be a standard.

@edumrodrigues
Copy link

I'm missing this feature. It really needs to be standard.

@shogogan
Copy link

shogogan commented Feb 20, 2019

found out why it's so hard to set a calendar mask. got to build a directive based on the inputMask to use on the pCalendar.
One of the main problems to solve this problem is the DateFormat options, and the possible functionalities(range, multiple and whatsoever functionality).
If set just for numeric values. the mask can be pretty simple to make.

@leonetosoft
Copy link

leonetosoft commented Feb 27, 2019

Many users find it impractical to have to select a date in the calendar, they want to type on the keyboard when using the mouse, and this is really true. I think it would be a question of usability to think (@cagataycivici), for now I have created a method to use the inputMask with the validation of the user input:

Component:

import { Component, OnInit, Input, forwardRef, ChangeDetectorRef, Output, EventEmitter, Directive } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, NG_VALIDATORS, Validator, AbstractControl } from '@angular/forms';
import * as moment from 'moment';

export const LNDATE_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => AppLnDateComponent),
  multi: true
};

@Component({
  selector: 'app-app-ln-date',
  templateUrl: './app-ln-date.component.html',
  styleUrls: ['./app-ln-date.component.css'],
  providers: [LNDATE_VALUE_ACCESSOR]
})
export class AppLnDateComponent implements OnInit, ControlValueAccessor {
  @Input()
  mask = '99/99/9999';
  @Input()
  inputMask = 'YYYY-MM-DD';
  @Input()
  displayMask = 'DD/MM/YYYY';

  @Output() onChange: EventEmitter<any> = new EventEmitter();
  @Input() disabled: boolean;
  @Input() url: boolean;
  onModelChange: Function = () => { };

  onModelTouched: Function = () => { };
  focused: boolean = false;
  value: string;
  valueAcessor: string;
  keySelected: number;

  completed = false;

  constructor(private cd: ChangeDetectorRef) { }

  ngOnInit() {
  }

  writeValue(obj: any): void {
    if (obj && !moment(obj, this.inputMask, true).isValid()) {
      throw new Error(`Data inválida deve estar no formato correto, ${obj}`);
    }
    this.value = moment(obj).format(this.inputMask);
    this.valueAcessor = moment(this.value, this.inputMask).format(this.displayMask);
    this.cd.markForCheck();
  }

  registerOnChange(fn: any): void {
    this.onModelChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onModelTouched = fn;
  }

  setDisabledState?(val: boolean): void {
    this.disabled = val;
  }

  onFocus(event: Event) {
    this.focused = true;
  }

  onBlur(event: Event) {
    this.focused = false;
    if(!this.isValidDate()) {
     /* this.valueAcessor = moment(this.value, this.inputMask).format(this.displayMask);*/
     setTimeout(() => { 
     /*  this.valueAcessor = moment(this.value, this.inputMask).format(this.displayMask);*/
     this.valueAcessor = '';
     this.value = undefined;
     this.updateModel(event, undefined);
     }, 100);
    }
  }

  isValidDate() {
    return moment(this.valueAcessor, this.displayMask, true).isValid();
  }

  onIputKey(evt) {
    this.value = evt;
    this.updateModel(evt, String(this.value));
  }

  onInputChange() {
    //console.log('digito');

    // let val = (<HTMLInputElement>event.target).value;
    let setVal = moment(this.valueAcessor, this.displayMask).format(this.inputMask);
    if (setVal !== 'Invalid date') {
      this.updateModel(event, setVal);
    }
  }

  onInput(event: KeyboardEvent) {
    this.value = (<HTMLInputElement>event.target).value;
    this.onModelChange(this.value);
  }

  updateModel(event: Event, value: string) {
    this.value = value;
    this.onModelChange(this.value);
    this.onChange.emit({
      originalEvent: event,
      value: value
    });
  }


}

@Directive({
  selector: '[appCheckDate]',
  providers: [{ provide: NG_VALIDATORS, useExisting: DateValidatorDirective, multi: true }]
})
export class DateValidatorDirective implements Validator {
  @Input('dateValue') dateValue: string;
  @Input('dateFormat') dateFormat: string;

  validate(control: AbstractControl): { [key: string]: any } | null {
    return this.dateValue && moment(control.value, this.dateFormat, true).isValid() ?
      null : {
        invalidDate: {
          valid: false
        }
      };
  }
}

HTML:

<p-inputMask [mask]="mask" (onFocus)="onFocus($event)" (onBlur)="onBlur($event)" appCheckDate (onComplete)="onInputChange()" [dateValue]="valueAcessor" [(ngModel)]="valueAcessor" [dateFormat]="displayMask" placeholder="DD/MM/YYYYY"></p-inputMask>

IN FORM OR USE NGMODULE:

<app-app-ln-date mask="99/99/9999" inputMask="YYYY-MM-DD" displayMask="DD/MM/YYYY" formControlName="data_internacao"></app-app-ln-date>

obs: use moment.js to format and valid dates ...

@shogogan
Copy link

shogogan commented Feb 27, 2019

I've made one lib for masking, one of the first things that i built was a directive for primeng calendar.
https://www.npmjs.com/package/racoon-mask

it's quite simple and solve most of the directive problems, if anyone find anything that could improve it, this is the github link :)

https://github.com/shogogan/racoon-mask

it has two directives, but the main one here is the [rPCalendarMask], no inputs, no outputs, it just gets the dateformat and builds a mask on it (with time included if wanted)

usage:
HTML:
<p-calendar rPCalendarMask></p-calendar>

@shogogan
Copy link

shogogan commented Mar 3, 2019

Just informing that i changed the mask lib name (racoon-lib to racoon-mask).
And now it can show the placeholder like __/__/____

and it is working pretty better (with a early stage of a showcase to try on while testing)

And I put it on version 1.0.0 right now. 😄

still needs some improvements, but by now it's working on android/ios chrome/ie10

Any issue just report or open a PR :)

@iwashungry1992
Copy link

iwashungry1992 commented May 13, 2019

Just informing that i changed the mask lib name (racoon-lib to racoon-mask).
And now it can show the placeholder like __/__/____

and it is working pretty better (with a early stage of a showcase to try on while testing)

And I put it on version 1.0.0 right now.

still needs some improvements, but by now it's working on android/ios chrome/ie10

Any issue just report or open a PR :)

Thank you so much for your work. I'm using the rPCalendarMask directive but cannot get __/__/____ to show while the user is inputting the date.

As this sample shows for phone number, when the user selects the input, I would like for the __/__/____ to be in place of ___-___-____, but for p-calendar element.

image

I'm using racoon-mask-primeng version 1.1.11. It would be very helpful if you can create a demo of this.

Thank you again.

@shogogan
Copy link

Hey @iwashungry1992, to show the placeholder, just set [showPlaceholder]="true"
it should work correctly then, it will pick the dateformat informed on the p-calendar and set the placeholder accordingly.

If not then it may be a bug, and i need to fix it

@iwashungry1992
Copy link

Hey @iwashungry1992, to show the placeholder, just set [showPlaceholder]="true"
it should work correctly then, it will pick the dateformat informed on the p-calendar and set the placeholder accordingly.

If not then it may be a bug, and i need to fix it

This made it work, thank you!

@nandowalter
Copy link

Just another workaround suggestion...

It seems it is possible to use Cleave alongside the Calendar component.

After

npm i --save cleave.js @types/cleave.js

you can configure your component something like this:

import Cleave from 'cleave.js';
import { Calendar } from 'primeng/calendar';

@ViewChild('calendarDataInicio')
calendarDataInicio: Calendar;

cleaveDataInicio: Cleave;

ngAfterViewInit() {
  this.cleaveDataInicio = new Cleave(this.calendarDataInicio.inputfieldViewChild.nativeElement,
    {
      date: true,
      datePattern: ['d', 'm', 'Y'],
    }
  );
}

The @ligiane solution was the best for my case. Thank you!!!

@Suleiman-Moraes
Copy link

Eu fiz uma lib para mascarar, uma das primeiras coisas que eu criei foi uma diretiva para o calendário primeng.
https://www.npmjs.com/package/racoon-mask

é bem simples e resolve a maioria dos problemas de diretiva, se alguém encontrar algo que possa melhorá-lo, este é o link do github :)

https://github.com/shogogan/racoon-mask

tem duas diretivas, mas a principal aqui é [rPCalendarMask], sem entradas, sem saídas, apenas obtém o formato da data e cria uma máscara (com tempo incluído, se desejado)

uso:
HTML:
<p-calendar rPCalendarMask></p-calendar>

Very very good, thank you.

@shogogan
Copy link

hello i tried to install it and used it but i don't really think that this is working here is the stackblitz i generated to test ur lib
https://stackblitz.com/edit/primeng-calendar-demo-tpw1hb?file=src%2Fapp%2Fapp.component.ts

usage: <p-calendar rPCalendarMask inputId="basic" [(ngModel)]="date1">

I tried your stackblitz, but seems like stackblitz can't find primeng 10.0.0, so I downgraded to 8.0.0, and tested with the direct:
import { PrimeNgCalendarMaskModule } from 'racoon-mask-primeng';

from the racoon-mask-primeng directly, and it worked as expected

maybe it is a version problem, need to check, but here is a functional stackblitz:

https://stackblitz.com/edit/primeng-calendar-demo-fxcvu2?file=package.json

@Wandmalfarbe
Copy link
Contributor

This is currently the most requested feature with 70 reactions. If you combine it with #3782 is has over 100 thumbs up. Some official response from Primefaces would be nice... @cagataycivici @yigitfindikli @mertsincan

@Tr1monster
Copy link

Including this feature in the primeng library would be awsome. It is a common feature in the business world with massive keyboard usage. @cagataycivici @yigitfindikli @mertsincan

@lsfgrd
Copy link

lsfgrd commented Nov 26, 2020

It's been 3 years, but @Da13Harris's suggestion is just too good.

It might be even more ideal if the PrimeNG input mask was just a dynamic directive that could be applied to either native inputs or other PrimeNG components, including the calendar/datepicker.

@ryzott
Copy link

ryzott commented Jan 20, 2021

racoon-mask-primeng works, but it is not supported well by all the formats. Also there is some issues with the 12 hours time format. I'm still looking forward to see a solution from PrimeNG.
Please make it happen.

@adessilly
Copy link

adessilly commented Jan 31, 2021

I made my own directive, and it seems to work fine :
https://stackblitz.com/edit/primeng11-calendar-with-mask
You need to install "inputmask" :
npm i inputmask

The directive :

import { AfterViewInit, Directive } from '@angular/core';
import { Calendar } from 'primeng/calendar';
import Inputmask from 'inputmask';

@Directive({
  selector: '[dateMask]'
})
export class DateMaskDirective implements AfterViewInit {
  constructor(private primeCalendar: Calendar) { }

  ngAfterViewInit() {
    const calendar = this.getHTMLInput();
    const im = new Inputmask( this.getDateMask() );
    im.mask(calendar);
  }

  getHTMLInput(): HTMLInputElement {
    return this.primeCalendar.el.nativeElement.querySelector('input');
  }

  getDateMask(): string {
    if(this.primeCalendar.timeOnly) {
      return '99:99';
    } else if(this.primeCalendar.showTime) {
      return '99/99/9999 99:99';
    } else {
      return '99/99/9999';
    }
  }
}

Usage :
<p-calendar dateMask></p-calendar>

Hope it helps

@jermylucas
Copy link
Contributor

@icarodebarros This did help me - I just had to change event.path[0] to event.target because only Chrome supports path currently as it is not standard. I also did not need to use the blur event.
@thenninger

This solution has been the best method for me so far. However, the one flaw is that if using form validation, the validation will trigger after an input goes beyond the 10 characters of the date. For example, if entering 10/22/2021 , if you type "1" again, the form will become invalid, even though the 10/21/2021 is the only text shown inside of the input. If you log the value to the console, it shows 10/21/20211 note the extra 1. Otherwise works as expected

@harounchebbi
Copy link

Hello,

Any updates about this feature? it would be nice to have an input mask on the calendar.
Are there any plans to develop this in the future versions of primeng?

Thanks.

@thomasesh
Copy link

another vote for this feature

@Dexter5566
Copy link

I made my own directive, and it seems to work fine : https://stackblitz.com/edit/primeng11-calendar-with-mask You need to install "inputmask" : npm i inputmask

The directive :

import { AfterViewInit, Directive } from '@angular/core';
import { Calendar } from 'primeng/calendar';
import Inputmask from 'inputmask';

@Directive({
  selector: '[dateMask]'
})
export class DateMaskDirective implements AfterViewInit {
  constructor(private primeCalendar: Calendar) { }

  ngAfterViewInit() {
    const calendar = this.getHTMLInput();
    const im = new Inputmask( this.getDateMask() );
    im.mask(calendar);
  }

  getHTMLInput(): HTMLInputElement {
    return this.primeCalendar.el.nativeElement.querySelector('input');
  }

  getDateMask(): string {
    if(this.primeCalendar.timeOnly) {
      return '99:99';
    } else if(this.primeCalendar.showTime) {
      return '99/99/9999 99:99';
    } else {
      return '99/99/9999';
    }
  }
}

Usage : <p-calendar dateMask></p-calendar>

Hope it helps

you save my day, and I add autoUnmask: true for typing the input

@mertsincan
Copy link
Member

Hi,

So sorry for the delayed response! Improvements have been made to many components recently, both in terms of performance and enhancement. Therefore, this improvement may have been developed in another issue ticket without realizing it. You can check this in the documentation. If there is no improvement on this, can you reopen the issue so we can include it in our roadmap?
Please don't forget to add your feedback as a comment after reopening the issue. These will be taken into account by us and will contribute to the development of this feature. Thanks a lot for your understanding!

Best Regards,

@PriscilaFabreteCandido
Copy link

alguém encontrou uma solução para este problema?

by input date1, you can get the value of the input and change the value and put the mask, example:
document.getElementById("date1").value.replace(/^(\d{2})(\d{2})(\d{4})/, '$1-$2-$3');

@daspilker
Copy link

Is this really fixed? I can not find anything about setting an input mask on a calender component in https://primeng.org/calendar.

@cetincakiroglu cetincakiroglu reopened this Mar 8, 2024
@cetincakiroglu cetincakiroglu added this to the 17.Future milestone Mar 8, 2024
@github-actions github-actions bot added the Status: Needs Triage Issue will be reviewed by Core Team and a relevant label will be added as soon as possible label Mar 8, 2024
@joviga-dev
Copy link

up

2 similar comments
@evan-scales
Copy link

up

@epoiate
Copy link

epoiate commented Jun 4, 2024

up

@kaellandrade
Copy link

Up

@UltimatumGamer
Copy link

+1

1 similar comment
@SanderAtIndy
Copy link

+1

@Keirz
Copy link

Keirz commented Oct 21, 2024

Up.

@ES-Six
Copy link

ES-Six commented Dec 12, 2024

I would say "Up" too 😁

@mertsincan mertsincan removed the Status: Needs Triage Issue will be reviewed by Core Team and a relevant label will be added as soon as possible label Dec 16, 2024
@mertsincan mertsincan moved this to Review in PrimeNG Dec 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: New Feature Issue contains a new feature or new component request
Projects
Status: Review
Development

No branches or pull requests