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

Month and/or year selecion in DatePicker #4853

Open
julianobrasil opened this issue May 28, 2017 · 47 comments
Open

Month and/or year selecion in DatePicker #4853

julianobrasil opened this issue May 28, 2017 · 47 comments
Labels
area: material/datepicker feature This issue represents a new feature or feature request rather than a bug or bug fix P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Comments

@julianobrasil
Copy link
Contributor

julianobrasil commented May 28, 2017

Bug, feature request, or proposal:

Feature

What is the expected behavior?

User being able to select a month in a year, or just a full year number. The component already have a startView attribute. It could have also a view attribute so the component could be configured to show just months of the year, or years.

What is the current behavior?

The user can select a full date only (day, month and year).

What is the use-case or motivation for changing an existing behavior?

Although the design docs does mention that will be a date & time option in the future, it does not mention this feature (at least, not explicitly). It's sometimes useful to set the view of a datepicker to allow the selection of just a month or a year. Maybe the problem is related to the date object, that demands a complete date. But it can be left to the developer the control of what is being grabbed from the datepicker, for example:

  • if in year view mode and the user selects july/2017, the component parse it to the first day of july. Something like new Date(2017,6,1);
  • if in "decades" view mode and the user selects the year of 2017, the component parse it to the first day of 2017. Something like new Date(2017,0,1).

The developer would have to know how he configured the component and what kind of information it'll give back to him.

The current state of the component can be used for the user to select a month and a year, but it's too confusing to the user if I put a full calendar to choosing just a month (along with some information like "choose any day in the desired month").

A work around to the lack of this feature is to use a regular mdInput with type="number" (that's the way I use for now, because, after all, representing months as numbers are common knowledge). But it's not a nice way because the user must select (in case of months) the month and the year in separate controls.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

all

Is there anything else we should know?

no

@fxck
Copy link
Contributor

fxck commented May 29, 2017

it will be added eventually #675 (comment) (and following discussion)

@mmalerba mmalerba added the feature This issue represents a new feature or feature request rather than a bug or bug fix label May 30, 2017
@mmalerba mmalerba self-assigned this May 30, 2017
@mmalerba
Copy link
Contributor

dup of #5845

@mmalerba
Copy link
Contributor

oh whoops, misread this, not a dup

@mmalerba mmalerba reopened this Nov 21, 2017
@mmalerba mmalerba added the P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent label Nov 21, 2017
@julianobrasil
Copy link
Contributor Author

julianobrasil commented Nov 22, 2017

@mmalerba, they are very similar, in fact 😄

This component let me choose the year (it returns an integer instead of a Date or Moment object):

image

Returning an integer is an alternative when you use a datepicker configured to work in Month or Year mode. It's better than just use a regular input with type=number

@julianobrasil
Copy link
Contributor Author

julianobrasil commented Feb 14, 2018

This is a first approach of a wrapper to the datepicker in order to achieve some kind of pickers I need in a project of mine. Maybe it can help if someone needs something like a month picker. It's not fully tested: https://stackblitz.com/edit/angular-mulitdate-picker-demo

@mmalerba, the yearSelected and monthSelected outputs really make the picker more flexible. But you know what? In this particular project, I also need a week picker and a semester picker. I worked it around using filters. Is this kind of feature too far from the final goal planned for this component? Month and year pickers are pretty common, but week picker is kind of rare and semester picker... I think I'm the only one in the world who is using it. hahaha

@mmalerba
Copy link
Contributor

@julianobrasil I want to try to abstract out the concept of a calendar view, have it be some interface that people can provide their component as. Then allow people to put custom views in the calendar and have the calendar interact with them via that interface. Similar idea to how the form field works with custom form field controls, haven't gotten the time to work on it yet though.

@Iznogood1
Copy link

@julino,

thanks a lot fot your month picker
Excatly what I need !

@krishragu12395
Copy link

@julianobrasil is that feature is added? . If yes means please elaborate that. Thanks in advance.

@julianobrasil
Copy link
Contributor Author

julianobrasil commented Mar 9, 2018

@krishragu12395, if you are referring to the year picker and month picker, strictly speaking, no, they are not available yet. If you are asking whether it's possible to emulate these features, fortunately, the answer is yes.

However, this demands the 6-beta versions of material to be installed because you'll use the new @Outputs: yearSelected and monthSelected (see the stackblitz demo in my comment above). For now, you can read about them over here as the docs site is likely to be updated just when the team releases the final version.

@krishragu12395
Copy link

Thank you @julianobrasil ...

@dojchek
Copy link

dojchek commented Mar 9, 2018

Month, week, quarter, semester, etc.. Would be awesome if we would have an adapter as mentioned above as it would cover any kind of custom input format. Quarters/Week/Month formatters are all supported by moment and date-fns libs.
Is there any chance that something like this will get implemented?

@julianobrasil
Copy link
Contributor Author

@dojchek, at some point, there will be a way to have all these custom pickers as @mmalerba mentioned in #4853 (comment)

@jdewell
Copy link

jdewell commented Mar 15, 2018

@julianobrasil, in your demo you have the line:
import { default as _rollupMoment, Moment } from 'moment';

I get an error saying:
.../node_modules/moment/moment has no exported member 'default'. Ideas?

@julianobrasil
Copy link
Contributor Author

Yeah... in my projects I use, instead:

import * as moment from 'moment';
type Moment = moment.Moment;

@jdewell
Copy link

jdewell commented Mar 15, 2018

Ahh, ok. Thanks

@Iznogood1
Copy link

Onother way to avoid the "moment has no exported member 'default'" error is to set
"allowSyntheticDefaultImports": true
in the "compilerOptions" section of the jsconfig.json file.

Here is an example

{
  "compileOnSave": false,
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2017",
      "dom"
    ]
  }
}

@labyed
Copy link

labyed commented Mar 16, 2018

@julianobrasil Thank you so much !!!

@jdewell
Copy link

jdewell commented Mar 16, 2018

@julianobrasil Do you know why in your demo the chosenMonthDate in the _monthSelectedHandler is one month less than what was chosen? I put in this code:
console.log("chose "+chosenMonthDate.month());

I chose July and above printed:
chose 6

However, the formcontrol displays the correct month/year: 07/2017. I got into this as I was trying to figure out how to display Jul 2017

@mmalerba
Copy link
Contributor

@jdewell JavaScript uses 0-indexed months, January = 0, February = 1, etc.

@julianobrasil
Copy link
Contributor Author

julianobrasil commented Nov 15, 2018

Hey, @pavan6cs, what exactly are you trying to do? I'm afraid you've tried to do something like <jp-multidatepicker (dateChange)="..."> in my stackblitz sample...

Well, I'm not exposing the dateChange output in that example. You'd have to take a look inside it and do it by yourself, but it's not hard to do it. Just create some @Outputs (name them after dateChange if you want), one per inner picker and a wrapper @Output for the wrapper component in order do forward the emmited date changes by the inner inputs.

@pavan6cs
Copy link

@julianobrasil ,what im trying to do is to emitt an event when date is changed by input or by picking from calandrer menu.

<mat-form-field class="datepicker"> <input matInput [matDatepicker]="dp" (dateChange)="addEvent('change', $event)" placeholder="Month and Year" [formControl]="date"> <mat-datepicker-toggle matSuffix [for]="dp"></mat-datepicker-toggle> <mat-datepicker #dp touchUi="true" startView="year" (yearSelected)="chosenYearHandler($event)" (monthSelected)="chosenMonthHandler($event, dp)" panelClass="example-month-picker"> </mat-datepicker> </mat-form-field>

in the above code (dateChange) will not trigger when the monthSelected handler is added.

Can you please suggest how can i emitt the event when date is changed through input and also through calender pick?

@julianobrasil
Copy link
Contributor Author

Oh, I got it. It won't fire a date change in this case (it's in the docs), because the date is really changed only when the user goes through all the process and selects a day. The day selection is the only event that triggers a date change (maybe you can update the control when the year is selected to force a date change...)

@pavan6cs
Copy link

@julianobrasil
how can i force the date change from controler ? do u have any sample . it will be lot help if u have one

thank you..

@aayushwadhwa
Copy link

I have tried something and might fulfil the purpose.
https://stackblitz.com/edit/angular-stimns-emrki2

@julianobrasil
Copy link
Contributor Author

@pavan6cs, I'm sorry, but I missed your #4853 (comment). Well, I've built up something for momentjs and you can take a look on: https://github.com/julianobrasil/multipicker

But @aayushwadhwa's comment answered exactly what you asked.

@nekomamushi01
Copy link

just found a solution about this problem

//for month//
//in your template
<input [matDatepicker]="datepicker">
<mat-datepicker #picker (monthSelected)="close($event)" startView="year">

//in your controller
import { MatDatepicker } from '@angular/material';

@ViewChild('picker') datePicker: MatDatepicker; //

close($event) {
this.datePicker.close();
}

//for year//
//in your template
<input [matDatepicker]="datepicker">
<mat-datepicker #picker (yearSeleceted)="close($event)" startView="multi-year">

//in your controller
import { MatDatepicker } from '@angular/material';

@ViewChild('picker') datePicker: MatDatepicker; //

close($event) {
this.datePicker.close();
}

@nekomamushi01
Copy link

ohh my bad @aayushwadhwa already answered it.
but additional, if you want month picker only, change startView to year :)

@igornowosad
Copy link

igornowosad commented Dec 31, 2019

you can do this also without using @ViewChild
I had two date pickers and I only passed ref to them as a argument of close function

  <div>
    <mat-form-field>
      <input matInput [matDatepicker]="fromDatePicker" placeholder="From">
      <mat-datepicker-toggle matSuffix [for]="fromDatePicker"></mat-datepicker-toggle>
      <mat-datepicker startView="multi-year" (yearSelected)="closeDatePicker(fromDatePicker)" #fromDatePicker></mat-datepicker>
    </mat-form-field>
    <mat-form-field>
      <input matInput [matDatepicker]="toDatePicker" placeholder="To">
      <mat-datepicker-toggle matSuffix [for]="toDatePicker"></mat-datepicker-toggle>
      <mat-datepicker startView="multi-year" (yearSelected)="closeDatePicker(toDatePicker)" #toDatePicker></mat-datepicker>
    </mat-form-field>
  </div>
  closeDatePicker(elem: MatDatepicker<any>) {
    elem.close();
  }

@stephendavid
Copy link

Yes We can using moment,

add below lines above @component

import { default as _rollupMoment, Moment } from 'moment';
import { MomentDateAdapter, MAT_MOMENT_DATE_ADAPTER_OPTIONS } from '@angular/material-moment-adapter';

const moment = _rollupMoment || _moment;

// See the Moment.js docs for the meaning of these formats:
// https://momentjs.com/docs/#/displaying/format/
export const MY_FORMATS = {
parse: {
dateInput: 'YYYY',
},
display: {
dateInput: 'YYYY',
monthYearLabel: 'YYYY',
dateA11yLabel: 'LL',
monthYearA11yLabel: 'YYYY',
},
};

Then Add below code in component
@component({
selector: 'xxx',
templateUrl: './xxx.tml',
styleUrls: ['./xxx.scss'],
providers: [
// MomentDateAdapter can be automatically provided by importing MomentDateModule in your
// application's root module. We provide it at the component level here, due to limitations of
// our example generation script.
{
provide: DateAdapter,
useClass: MomentDateAdapter,
deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS]
},

{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS },

],
})
export class BrandDialogComponent implements OnInit {
date = new FormControl(moment());

constructor() {}

ngOnInit() {}

chosenYearHandler(normalizedYear: Moment, datepicker: MatDatepicker) {
const ctrlValue = this.date.value;
ctrlValue.year(normalizedYear.year());
this.date.setValue(ctrlValue);
datepicker.close();
}

}

in html add below code =>

<input matInput [matDatepicker]="dp" placeholder="Choose Year" [formControl]="date">
<mat-datepicker-toggle matSuffix [for]="dp">
<mat-datepicker #dp startView="multi-year" (yearSelected)="chosenYearHandler($event, dp)"
panelClass="example-month-picker">

Which is works perfectly, and don't forgot to add =>
"allowSyntheticDefaultImports": true - in tsconfig.json

@castor-dev3
Copy link

castor-dev3 commented Jan 13, 2020

you can do this also without using @ViewChild
I had two date pickers and I only passed ref to them as a argument of close function

  <div>
    <mat-form-field>
      <input matInput [matDatepicker]="fromDatePicker" placeholder="From">
      <mat-datepicker-toggle matSuffix [for]="fromDatePicker"></mat-datepicker-toggle>
      <mat-datepicker startView="multi-year" (yearSelected)="closeDatePicker(fromDatePicker)" #fromDatePicker></mat-datepicker>
    </mat-form-field>
    <mat-form-field>
      <input matInput [matDatepicker]="toDatePicker" placeholder="To">
      <mat-datepicker-toggle matSuffix [for]="toDatePicker"></mat-datepicker-toggle>
      <mat-datepicker startView="multi-year" (yearSelected)="closeDatePicker(toDatePicker)" #toDatePicker></mat-datepicker>
    </mat-form-field>
  </div>
  closeDatePicker(elem: MatDatepicker<any>) {
    elem.close();
  }

The only trouble with this was the form control isn't updating his value, so for me the input keeps empty, besides you can get the value of the year adding the envent as input in closeDatapicker

closeDatePicker(elem: MatDatepicker<any>, event: MatDatepickerInputEvent<Date>) {
        elem.close();
        // This setting value also can't refresh the value in the html input
        this.formGroup.get('year').setValue(String(event).split(' ')[3]);
    }

@tobiasschweizer
Copy link
Contributor

Is there a way for the user to dynamically decide wether she wants to select a day, month, or year?

If I understand correctly, the date picker is closed when a year has been selected, so there is no way to pick a month or day with that config.

@igornowosad
Copy link

@tobiasschweizer you can try to make a function for both monthSelected and yearSelected output, something like

monthSelected() {
  if (user checked to select only month) {
    return close calendar
  }
  return
}
yearSelected() {
  if (user checked to select only year) {
    return close calendar
  }
  return
}

@tobiasschweizer
Copy link
Contributor

Ideally, the DatePicker would allow for only selecting a month if this is what the user wants and then close itself.

Would you suggest to set up different configuration options elsewhere in the template (to find out if "user checked to select only month" etc.) or something similar and then react to to this?

There could be a selection for precision the user could choose from: "year" | "month" | "day".

@tobiasschweizer
Copy link
Contributor

I had a look at the date-range branch. As @mmalerba has pointed out to me, there is a new component that lets the user select a date range with day precision. Internally, this is represented as a DateRange with a start and an end date:

/** A class representing a range of dates. */
export class DateRange<D> {
/**
* Ensures that objects with a `start` and `end` property can't be assigned to a variable that
* expects a `DateRange`
*/
// tslint:disable-next-line:no-unused-variable
private _disableStructuralEquivalency: never;
constructor(
/** The start date of the range. */
readonly start: D | null,
/** The end date of the range. */
readonly end: D | null) {}
}

In the template, the start and end dates can be bound to an input via the form control name. So it's like in the case of picking a single date, only that for a range you get two dates.

I think from the logical point of view this is all that's needed to represent dates with month and year precision. Internally, these can be treated as ranges from the first to the last day of a month or a year. In the GUI, however, these need to be displayed differently. Clicking on a month or year should be enough to create a date range for a date with month or year precision, the same applies when initializing an existing date range.

I think DateRange needs an additional member precision which could be day, month, or year. Given a range like start: 04/01/2020, end: 04/30/2020, this information would be displayed differently depending on precision: either as an actual selection from the first to the last day of the month (day precision) or just as an active month in month view (month precision). The same applies when the user just wants to define a year like 2020 (internally represented as a date range from the first day to last day of the year).

I am also thinking of periods, but they can be represented by two date ranges. With imprecise start and end dates of a period you then have four dates, but logically you just have to worry about the start date of the period's beginning end the end date of the period's end.

@mmalerba I am not sure if this belongs here. Shall I try to describe this in a doc? Where would that go?

@mmalerba
Copy link
Contributor

@tobiasschweizer we have a design doc template, though I'm not sure all of the sections really apply in this case, since we're talking about adding a feature to an existing component, not making a new component. I think just to give us a starting point to discuss you can copy the above into the "Proposed implementation" section.

Though it might be possible to represent the precision as a DateRange, I'm not sure if that's the right thing to do here. There are a number of places where we use the DateRange as a cue to show a little bit different UI. The logic in these areas could get event more confusing if a DateRange is sometimes not really a range and just a different precision date. I think if we wanted to represent precision like that we would probably create a separate class called Period or something.

I think the question of how we represent precision is important, but probably more important is how we work it into the existing pieces that are in play in the datepicker. Some of the pieces are definitely hardcoded to a specific precision (e.g. MonthView), and we might need to come up with a more generic framework that allows for more flexibility in terms of precision

@tobiasschweizer
Copy link
Contributor

@mmalerba Ok, thanks for the input. I made a copy of the design doc template and will proceed as you proposed. I will then share the doc with you.

@tobiasschweizer
Copy link
Contributor

tobiasschweizer commented May 5, 2020

@mmalerba I shared the design doc with you that I created. Are you able to access it?

@mmalerba
Copy link
Contributor

mmalerba commented May 5, 2020

Yep, its on my list of things to take a look at

@andreElrico
Copy link

Here is how I solved it:

https://stackblitz.com/edit/angular-pyeuls?file=app%2Fdatepicker-views-selection-example.html

@infacto
Copy link

infacto commented Apr 12, 2021

Please remove moment js. This should not be a dependency to achieve things like that. I see that Angular Material officially supports moment js. There is an adapter for the mat-datepicker etc. This is really annoying. Instead of that, allow date processor, a middleware or something like this. ... This framework is too complicated. You should get inspired by other Frameworks like Vuetify etc. And note that there are better alternatives to momentjs. Like dayjs or date-fns, etc. ...

I just wonder: Why does the mat-datepicker input displays the correct local date, but fails on user input with exactly the same date format? And why does this work in other Frameworks? Sorry, I'm pissed a bit because this is very time wasting stuff. ... Argh...

@axell9641
Copy link

https://material.angular.io/components/datepicker/examples#datepicker-views-selection

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: material/datepicker feature This issue represents a new feature or feature request rather than a bug or bug fix P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

No branches or pull requests