angular / components

Component infrastructure and Material Design components for Angular
https://material.angular.io
MIT License
24.33k stars 6.73k forks source link

[Datepicker] Need affordance for open-on-click for inputs #8905

Open sometime opened 6 years ago

sometime commented 6 years ago

Bug, feature request, or proposal:

feature

What is the expected behavior?

When i click on input - i want to see calendar without click on icon and input must be focused

What is the current behavior?

To view calndar i need click on icon... and input not focused

What are the steps to reproduce?

Use datepicker and click on input (not icon) - calendar not opened... when click on icon - calendar opened, but input not focus

julianobrasil commented 6 years ago

You can use the (focus) and the (click) events to programmaticaly open the calendar. Also you can mark the input as readonly if you want to prevent users to type into the input field. If I remember right this approach needed a workaround in IE11, but it's not so difficult. I'll open another issue about IE11 behavior.

stackblitz: https://stackblitz.com/edit/open-calendar-on-focus

sometime commented 6 years ago

readonlyis not appropriate: if user want - he can write date on keyboard. And when focus on input, placholder must go to up, like default behavior, but this does not happen.

julianobrasil commented 6 years ago

I see... the calendar get the focus as soon as it opens, that's why the label doesn't animate. Well, as a workaround you can get rid of the readonly and force the focus back to the input:

<mat-form-field>
  <input matInput [matDatepicker]="picker"  
         placeholder="Choose a date" 
         (focus)="_openCalendar(picker)" 
         (click)="_openCalendar(picker)" 
         #elementToFocus>
  <mat-datepicker #picker="matDatepicker"></mat-datepicker>
</mat-form-field>
@ViewChild('elementToFocus') _input: ElementRef;

...

_openCalendar(picker: MatDatepicker<Date>) {
    picker.open();

    // use the setTimeout to push the focus capturing
    // into the event loop and put it off until the calendar 
    // routines finish. 
    // Otherwise the calendar will get the focus back
    setTimeout(() => this._input.nativeElement.focus());
}

I've updated the stackblitz with this.

If you use Datepicker like this very often, wrap it in a component so you doesn't need to do all this to achieve the UX you're looking for, everytime you need a calendar.

BTW, don't forget to build a date adapter if you need the user to type in the date in specific format.

sometime commented 6 years ago

Exactly what is needed! Thank you so much!

sometime commented 6 years ago

Found a bug. When i click on input and calendar opened - very good, but if i not select the date and just click on other body empty place (to focus out) - calendar is closed, but cursor again focused on input.. Very not friendly.

cephillips117 commented 6 years ago

@sometime since you are now triggering the events via the input you would then have to also account for the blur() I've looked at @julianobrasil stackblitz example and with some minor changes got it to work:

<mat-datepicker #picker="matDatepicker" (closed)="eventCloseHandler($event)"></mat-datepicker>

eventCloseHandler(e){ setTimeout(() => this._input.nativeElement.blur()); }

I also don't think it's a bug since what @julianobrasil provided was a work-around in itself.

I hope this helps.

sometime commented 6 years ago

No, this not help.

Example from real life: i add two elements to form - input for name and datepicker for birthdate. First, i focus cursor to datepicker and calendar is opened - good... but if i not select the date and mouse click in input with name - input with name not selected... i must click to input name second time.

andrewseguin commented 6 years ago

Can you provide a stackblitz or plunker showcasing your use case?

cephillips117 commented 6 years ago

Ah I see the issue now and you can see it with https://material.angular.io/components/datepicker/examples

When the user opens the calendar whether by using the mat-datepicker-toggle or the implementation above it requires 2 clicks to trigger the focus of another input box.

Steps to reproduce:

  1. Choose any Date Picker from the Examples page linked above open the Date Picker popup
  2. Click on any other input box, date picker, or button on the page. It require 2 clicks to focus.
sometime commented 6 years ago

Can you provide a stackblitz or plunker showcasing your use case?

yes, https://stackblitz.com/edit/angular-fxsfj2

It require 2 clicks to focus.

It is very not frendly... if i see user element in interface and click it - i expect that I will be in the context of this element after this click... not after second click

julianobrasil commented 6 years ago

@sometime, when you click outside the calendar panel, you're not hitting any element, but the backdrop built by the cdk-overlay. So, the first click just close the panel and dismiss the backdrop.

You will need to inspect the point that was clicked and, in the next moment, after the backdrop is dismissed, you can set the focus on your next input,

Something like this:

this._subcription = Observable.fromEvent(document, 'click')
      .subscribe(e => {
        if (e instanceof MouseEvent) {
          const x = e['pageX'];
          const y = e['pageY']
          const element = document.elementFromPoint(x, y);
          setTimeout(() => {
            if (element instanceof HTMLInputElement) {
              (<HTMLInputElement>element).focus();
            }
          });
        }
      });

Here is the stackblitz: https://stackblitz.com/edit/open-calendar-on-focus-v2

[Edited]: You'll also need to handle the event emitted when you leave the input (associated with the Datepiker) by hitting the TAB key - the "tab away event" - or the calendar will remain opened. I've updated the stackblitz code.

sometime commented 6 years ago

Your stackblitz not working. I think this method - wrong way and very difficult for simple expected behavior.

The problem in the datepicker core. When calendar is opened - he takes a lot of power about herself. Closing the calendar should not depend on the standard behavior of other user interface elements. For example: when input (matInput) mouseover - cursor is "text"... but if this form contains datepicker and i open calendar then if input (matInput) mouseover - cursor is "pointer". Why? I think - the mechanism of opening calendar must not depend of other user interface elements.

sometime commented 6 years ago

very good example, how must work datepicker: https://www.primefaces.org/primeng/#/calendar if i click one datepicker and not select date.. then click another datepicker - first calendar closing and open next calendar... by one click.. very friendly!

sometime commented 6 years ago

If i use one material element in form, for example - one datepicker, - this working very good. But I have a lot of material elements in the one form (two datepickers, two inputs, two autocompletes). And switching between these elements works very unfriendly.

julianobrasil commented 6 years ago

@sometime, as the feature you're looking for isn't out-of-the box, if you wrap up the datepicker in a component and hide all the complexity in this new reusable custom component, maybe it makes your life easier after it's ready. But I agree that it should be more simple to do what you're trying to.

I'll try out to build up something and put in that stackblitz.com example and let you know if I manage to do a basic reusable component (without the ControlValueAccessor implementation part). Maybe using a custom form field control. If I succeed I post it here (I really don't know when I'll be able to do it).

sometime commented 6 years ago

Will be great!

julianobrasil commented 6 years ago

@mmalerba, it looks like the backdrop turn the task of wraping up the datepicker in a custom component into a hard time (I was trying to to build up a workaround following @sometime's requisite: to be able to close the datepicker and focus other components with a single mouse click).

I've tried playing around with document.elementFromPoint(x,y) to find out which element was clicked to set the focus there after the backdrop was dismissed (by using a setTimout).

It looks like this kind of thing should be a feature of MatDatepicker itself. It takes too much effort to be setup by the developer in this valid use case IMO.

image

I don't think it's worth looking at it, but just in case you want to see what I've tried to do in order to checkout whether I haven't done anything wrong: https://stackblitz.com/edit/open-calendar-on-focus-v3

mmalerba commented 6 years ago

@julianobrasil Yeah currently the datepicker is not set up to easily allow this. I think your implementation is close to working though, there's was just a bug with the way the observables were set up. Here's a fixed version: https://stackblitz.com/edit/open-calendar-on-focus-v3-zkrepy?file=app/custom-datepicker/app-datepicker.component.ts

julianobrasil commented 6 years ago

Nice, as always!

sometime commented 6 years ago

Thank you! This works for me. But i think this must be as option in datepicker... options name for example "auto open on focus", i think it is must be in datepicker core.

mmalerba commented 6 years ago

Yep, will leave this open to track adding the feature at some point. For now thanks @julianobrasil for the workaround :)

julianobrasil commented 6 years ago

As a little contribution to the workaround, here it is with ControlValueAccessor implemented: https://stackblitz.com/edit/open-calendar-on-focus-mmalerba-v2

Furg commented 6 years ago

@julianobrasil Works really well except using Safari. When you erase an input value manually or using clear button and then select a new date value with the calendar, input placeholder overlaps text value.

Maybe it is related to this issue: #8679

julianobrasil commented 6 years ago

@Furg, unfortunately, I have no easy way to test it with safari 😃. The apps I deal with have to work primarily (I think I can say "only") in an environment where people use almost exclusively chrome (updated once a year, or less), so I haven't setup anything to test things against specific browsers other than Chrome, IE11 and Firefox.

Oh, and concerning this original issue I was just trying to help someone else. This is not a robust solution IMO (it lacks tests and further development). I think the behavior requested here reflects a better way for the Datepicker (and other components) to work. That being said, I have never heard an end user complaining about the need to perform two clicks in these situations. Of course, this is specific to my scenario.

julianobrasil commented 6 years ago

@Furg, I've updated all the dependencies in stackblitz sample. Could you check whether it behaves different in safari? https://stackblitz.com/edit/open-calendar-on-focus-mmalerba-v2

Furg commented 6 years ago

@julianobrasil The Safari problem still happens with all the dependencies updated, and also with all stackblitz workarounds from this issue.

I know that this is not an official solution, don't worry and thanks for your time. :+1:

Satish-Lakhani commented 6 years ago

How about this..? (click)="birth_date.open()" (focus)="birth_date.open()" as shown below.

<mat-form-field>
    <input matInput [matDatepicker]="birth_date" formControlName="birth_date" (click)="birth_date.open()" (focus)="birth_date.open()">
    <mat-datepicker-toggle matSuffix [for]="birth_date"></mat-datepicker-toggle>
    <mat-datepicker #birth_date></mat-datepicker>
</mat-form-field>
julianobrasil commented 6 years ago

@Satish-Lakhani, I used to do it. I took away this approach of some projects of mine because of the focus event though. Navigating using tab key is great, but if you close the calendar, you have to build a workaround to prevent it to reopen as the focus is restored to the original datepicker input. If you switch windows back and forth to the browser after choosing a date, there comes the focus again along with the calendar poping up... 🤣

DmitryEfimenko commented 4 years ago

I love how people show enthusiasm to come up with a workaround until an official solution is provided! Thank you everyone. Meanwhile, I've noticed some accessibility issues with the current workarounds:

DmitryEfimenko commented 4 years ago

Also, the calendar does not get updated while you type in the associated input. You have to close and re-open the calendar to see the change

julianobrasil commented 4 years ago

@DmitryEfimenko, usually workarounds aren't finished solutions. They are just start points, from where people can adapt to do whatever they needed when they opened the issue.

DmitryEfimenko commented 4 years ago

I certainly realize that. I'm just pointing out edge cases. By the way, this seems relevant: #13486

angular-robot[bot] commented 2 years ago

Just a heads up that we kicked off a community voting process for your feature request. There are 20 days until the voting process ends.

Find more details about Angular's feature request process in our documentation.

angular-robot[bot] commented 2 years ago

Thank you for submitting your feature request! Looks like during the polling process it didn't collect a sufficient number of votes to move to the next stage.

We want to keep Angular rich and ergonomic and at the same time be mindful about its scope and learning journey. If you think your request could live outside Angular's scope, we'd encourage you to collaborate with the community on publishing it as an open source package.

You can find more details about the feature request process in our documentation.