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

fix(datepicker): double tap required to select a date with the datepicker on ios #2810

Open
Frank84 opened this issue Oct 6, 2017 · 17 comments
Open

Comments

@Frank84
Copy link

@Frank84 Frank84 commented Oct 6, 2017

Is there a config option we can pass with the datepicker so it automatically closes the popup when the user touch a date on ios? At the moment it triggers the isHover state, adding the hover css on the date. An additional touch is then require to select the date and close the modal.

Versions:
ngx-bootstrap: 1.9.3
Angular: 4.3.3
Bootstrap: 3.3.7

@valorkin

This comment has been minimized.

Copy link
Member

@valorkin valorkin commented Oct 10, 2017

Thanks for an issue, it's interesting.

@praveen33in

This comment has been minimized.

Copy link

@praveen33in praveen33in commented Nov 2, 2017

Issue reported by Frank84 is happening to me as well.
Could we expect fix for the same any time soon?

@valorkin

This comment has been minimized.

Copy link
Member

@valorkin valorkin commented Nov 2, 2017

First touch on iOS treated as hover, second as a click. So I have not much options: remove hover effect and replace with mouse enter/leave and manipulate selection class from js. Which, of course, will affect performance

@praveen33in

This comment has been minimized.

Copy link

@praveen33in praveen33in commented Nov 3, 2017

Will there be an update with the mentioned change or ignore the issue to not affect performance?

@IlyaSurmay IlyaSurmay added the issue label Nov 6, 2017
@YevheniiaMazur YevheniiaMazur changed the title Double tap required to select a date with the datepicker on ios fix(datepicker): double tap required to select a date with the datepicker on ios Jan 17, 2018
@EvilAlexei EvilAlexei mentioned this issue Jan 17, 2018
11 of 61 tasks complete
@ktsangop

This comment has been minimized.

Copy link

@ktsangop ktsangop commented Mar 27, 2018

A dirty hack is to "remove" the hover event listener when the device is "recognized" to be an iPhone/iPad etc (FYI i did this for the RatingModule but it should work for datepicker also):

@ViewChild('ngxRating') rating: any;

ngAfterViewInit() {
    if ( (!!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform)) && 'ontouchstart' in window && this.rating ) {
      this.rating.enter = () => {};
    }
  }

Where ngxRating is a template reference to the rating component :
<rating #ngxRating ... ></rating>

@danitt

This comment has been minimized.

Copy link

@danitt danitt commented Apr 11, 2018

if it helps anyone, you can also capture and extend the hover listener, to manually update the value or close the picker - hacky but works until a formalised solution is implemented

// component.html
<input type="text"
    class="form-control"
    #datePicker="bsDatepicker"
    (onShown)="onShowPicker($event)"
    bsDatepicker>
// component.ts
...
onShowPicker(container) {
    const dayHoverHandler = container.dayHoverHandler;
    const hoverWrapper = function($event) {
        const { cell, isHovered } = $event;
        // do whatever with hovered cell/event
        return dayHoverHandler($event);
    };
    container.dayHoverHandler = hoverWrapper;
}
@iaguilarmartin

This comment has been minimized.

Copy link

@iaguilarmartin iaguilarmartin commented Jul 5, 2018

Thanks to the suggestions from @danitt and @ktsangop I managed to solve that issue on iOS devices. Here is the solution. It works for BsDatePicker and BsDateRangePicker:

// component.html
<input
      bsDatepicker
      #datepicker="bsDatepicker"
      type="text"
      class="form-control"
      (onShown)="onShowPicker($event)"
>

// component.ts
@ViewChild('datepicker')
private _picker: BsDatepickerDirective;

onShowPicker(event) {
    const dayHoverHandler = event.dayHoverHandler;
    const hoverWrapper = (hoverEvent) => {
        const { cell, isHovered } = hoverEvent;

        if ((isHovered &&
          !!navigator.platform &&
          /iPad|iPhone|iPod/.test(navigator.platform)) &&
          'ontouchstart' in window
        ) {
            (this._picker as any)._datepickerRef.instance.daySelectHandler(cell);
        }

        return dayHoverHandler(hoverEvent);
    };
    event.dayHoverHandler = hoverWrapper;
}
@heavyrick

This comment has been minimized.

Copy link

@heavyrick heavyrick commented Feb 1, 2019

i'm having the same issue. The first click shows the form input error (required), and the second click on the datepicker fills the input correctly. I tried the suggestions above, but none of them worked. I'm using angular 7.

component.html

<input class="form-control" placeholder="Data início"
  name="validation_start_at"
  formControlName="validation_start_at"
  bsDatepicker
  (onShown)="onShowPicker($event)"
  [bsConfig]="{ dateInputFormat: 'DD/MM/YYYY', containerClass: 'theme-red' }"
  [ngClass]="displayFieldCssError('validation_start_at')"
  required>
<div class="help-block c-red-500" *ngIf="form.validation_start_at.invalid && (form.validation_start_at.dirty || form.validation_start_at.touched)">
  <div *ngIf="form.validation_start_at.errors.required"><i class="fa fa-close"></i> Campo obrigatório</div>
</div>

component.ts

  displayFieldCssError(field: string) {
    return {
      'is-invalid': !this.postData.get(field).valid && this.postData.get(field).touched
    };
  }

  onShowPicker(container) {
    const dayHoverHandler = container.dayHoverHandler;
    const hoverWrapper = function($event) {
      const { cell, isHovered } = $event;
      // do whatever with hovered cell/event
      return dayHoverHandler($event);
    };
    container.dayHoverHandler = hoverWrapper;
  }
@ketsune

This comment has been minimized.

Copy link

@ketsune ketsune commented Feb 26, 2019

i'm having the same issue. The first click shows the form input error (required), and the second click on the datepicker fills the input correctly. I tried the suggestions above, but none of them worked. I'm using angular 7.

component.html

<input class="form-control" placeholder="Data início"
  name="validation_start_at"
  formControlName="validation_start_at"
  bsDatepicker
  (onShown)="onShowPicker($event)"
  [bsConfig]="{ dateInputFormat: 'DD/MM/YYYY', containerClass: 'theme-red' }"
  [ngClass]="displayFieldCssError('validation_start_at')"
  required>
<div class="help-block c-red-500" *ngIf="form.validation_start_at.invalid && (form.validation_start_at.dirty || form.validation_start_at.touched)">
  <div *ngIf="form.validation_start_at.errors.required"><i class="fa fa-close"></i> Campo obrigatório</div>
</div>

component.ts

  displayFieldCssError(field: string) {
    return {
      'is-invalid': !this.postData.get(field).valid && this.postData.get(field).touched
    };
  }

  onShowPicker(container) {
    const dayHoverHandler = container.dayHoverHandler;
    const hoverWrapper = function($event) {
      const { cell, isHovered } = $event;
      // do whatever with hovered cell/event
      return dayHoverHandler($event);
    };
    container.dayHoverHandler = hoverWrapper;
  }

I have a same issue here, Angular 7 with Reactive Form input which is bsDatePicker
1st click => it's warn me that this input is required field (Note that: formControl doesn't has any value yet.)

2nd click => Everything is going well, get the value, no required field waring.

@marie-dk

This comment has been minimized.

Copy link

@marie-dk marie-dk commented Mar 19, 2019

I'm also facing this with Angular 7.2.6. Strangely it was enough for me to just add the hover handler. Then the double-click issue on day selection disappeared in Safari 12 / iOS 12.1.

onOpenCalendar(container) {
 //Note: I also have selecthandlers for both year, month and day
  container.dayHoverHandler = (event: any): void => {
    console.log(event);
  }
}
@aniketdeshbhratar

This comment has been minimized.

Copy link

@aniketdeshbhratar aniketdeshbhratar commented May 2, 2019

Thanks to the suggestions from @danitt and @ktsangop I managed to solve that issue on iOS devices. Here is the solution. It works for BsDatePicker and BsDateRangePicker:

// component.html
<input
      bsDatepicker
      #datepicker="bsDatepicker"
      type="text"
      class="form-control"
      (onShown)="onShowPicker($event)"
>

// component.ts
@ViewChild('datepicker')
private _picker: BsDatepickerDirective;

onShowPicker(event) {
    const dayHoverHandler = event.dayHoverHandler;
    const hoverWrapper = (hoverEvent) => {
        const { cell, isHovered } = hoverEvent;

        if ((isHovered &&
          !!navigator.platform &&
          /iPad|iPhone|iPod/.test(navigator.platform)) &&
          'ontouchstart' in window
        ) {
            (this._picker as any)._datepickerRef.instance.daySelectHandler(cell);
        }

        return dayHoverHandler(hoverEvent);
    };
    event.dayHoverHandler = hoverWrapper;
}

Hi @iaguilarmartin ,

I tried your code which is working properly but in console i am getting below error.
"undefined is not an object (evaluating '_this._picker._datepickerRef')"
Do you have any solution for this?

@sucotronic

This comment has been minimized.

Copy link

@sucotronic sucotronic commented Sep 25, 2019

Due to iOS 13, the navigator.platform for iPad has changed, and also, they're firing two events, so the code above has to be:

if ((isHovered && cell.isHovered &&
      Modernizr.touchEvents && vendor == 'Apple'
) {
@pavantripsolver

This comment has been minimized.

Copy link

@pavantripsolver pavantripsolver commented Nov 2, 2019

Due to iOS 13, the navigator.platform for iPad has changed, and also, they're firing two events, so the code above has to be:

if ((isHovered && cell.isHovered &&
      Modernizr.touchEvents && vendor == 'Apple'
) {

But Modernizr and vendor are displaying as an error. how to import these two in a component?

@sucotronic

This comment has been minimized.

Copy link

@sucotronic sucotronic commented Nov 6, 2019

@pavantripsolver I've downloaded Modernizer to the same folder as the .ts file (with only the touchevents feature). I also use Bowser to detect if it is an iOS device(just install it with npm).

import * as Bowser from 'bowser';
import './modernizr.js';
...
export class XXXX {
    bw = null;
    constructor() {
        this.bw = Bowser.getParser(window.navigator.userAgent);
    }
    public iosLessThan13() {
        if (this.bw.parsedResult.os.name == 'iOS' && parseFloat(this.bw.parsedResult.os.version) < 13) {
            return true;
        } else {
            return false;
        }
    }
    public iosMoreThan12() {
        if (this.bw.parsedResult.os.name == 'macOS' ||  // iPad OS ¬¬
            (this.bw.parsedResult.os.name == 'iOS' && parseFloat(this.bw.parsedResult.os.version) >= 13)) {
            return true;
        } else {
            return false;
        }
    }

    public isSafariTouch() {
        if (window['Modernizr']['touchevents'] && this.bw.parsedResult.platform.vendor == 'Apple') {
            return true;
        } else {
            return false;
        }
    }

onShowPicker(event: any) {
        const dayHoverHandler = event.dayHoverHandler;
        const hoverWrapper = (hoverEvent: any) => {
            const { cell, isHovered } = hoverEvent;

            if (isHovered && this.isSafariTouch() &&
                ((this.iosMoreThan12() && cell.isHovered) || this.iosLessThan13())
            ) {
                try {
                    (this._picker as any)._datepickerRef.instance.daySelectHandler(cell);
                } catch (e) {}
            }

            return dayHoverHandler(hoverEvent);
        };

        event.dayHoverHandler = hoverWrapper;
    }
@Acereddy

This comment has been minimized.

Copy link

@Acereddy Acereddy commented Nov 18, 2019

I'm getting same issue in angular 6, but not all the time. I am able reproduce it very few time. but I want to resolve it. Any solutions will be appreciated.

@lizlux

This comment has been minimized.

Copy link

@lizlux lizlux commented Feb 18, 2020

I'm having the same issue in angular 8. Will also try a workaround, though it would be great if this were fixed!

@Acereddy

This comment has been minimized.

Copy link

@Acereddy Acereddy commented Feb 18, 2020

Issue was due to .touched which I was using in HTML for validation. This worked for me :)

I added validateDate() which will dynamically add .touched validation which we are using for required field.

date.component.html

<input type="text" formControlName="" (focusout)="validateDate()"
(click)="validateDate()" class="form-control datepicker"
bsDatepicker [bsConfig]="{ containerClass: 'theme-default', dateInputFormat: 'MM/DD/YYYY'}"/>

<ng-container *ngFor="let error of config.validations">
<p *ngIf="group.get(config.name).hasError(error.name) && (group.get(config.name).dirty || addValidation)">{{ error.message }}


date.component.ts

validateDate(): void {
this.addValidation = 'group.get(config.name).touched';
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.