Components: datepicker: (change) event does not work in mdInput

Created on 17 May 2017  路  32Comments  路  Source: angular/components

Bug

change event does not work

<md-input-container>
    <input mdInput [mdDatepicker]="picker" (change)="doSomething($event)">
     <button mdSuffix [mdDatepickerToggle]="picker"></button>
</md-input-container>

<md-datepicker #picker></md-datepicker>    

What is the expected behavior?

change event must work

What is the current behavior?

when choose a date from datepicker no (change) event is triggered

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

Angular 4.1.2 & material 2.0.0-beta.5

P3 has pr

Most helpful comment

@da45 @mohi86 For now, you can split out the [(ngModel)] like this

<input [ngModel]="myDate" (ngModelChange)="updateMyDate($event)">
updateMyDate(newDate) {
  this.myDate = newDate;
  this.runOtherCalculations(newDate);
}

https://plnkr.co/edit/aZKLaA5qlZj71AqD5ZFz?p=preview

All 32 comments

what about this:

this is not working either. Here's the code from @angular/material

MdCalendar.propDecorators = {
'startAt': [{ type: Input },],
'startView': [{ type: Input },],
'selected': [{ type: Input },],
'minDate': [{ type: Input },],
'maxDate': [{ type: Input },],
'dateFilter': [{ type: Input },],
'selectedChange': [{ type: Output },],
};

What event can I hook into when a date is selected?

@mohi86 mohi86
If your method is just to update the date, try the following binding:
[(ngModel)]="myDate" ,
For my case, I really have to call a function to run some complex tasks,

@da45
Thanks, but I have the same issue, I need to run some calculations when the date is selected.
I used angular2-material-datepicker but it's not working well with the angular material form elements.

the binding: [(ngModel)]="myDate" , works for me if I pick the date in the UI, but date modification made directly in the input field is not taken into account.

After further test, the binding problem described in my previous post was related to the localization (French) of the date format (dd/mm/yyyy) instead of (mm/dd/yyyy) wich works fine.

@da45 @mohi86 For now, you can split out the [(ngModel)] like this

<input [ngModel]="myDate" (ngModelChange)="updateMyDate($event)">
updateMyDate(newDate) {
  this.myDate = newDate;
  this.runOtherCalculations(newDate);
}

https://plnkr.co/edit/aZKLaA5qlZj71AqD5ZFz?p=preview

while waiting for the final fix, idea of @willshowell is helpful,

Unfortunately,
material 2.0.0-beta.6 was released but without fixing this issue :( ,

The following works for me. Note the spelling of the event!

<md-datepicker #picker (selectedChanged)="updateDate($event)">

Hello guys,

@mohi86 mentioned that he used (selectedChange) event but it doesn't work, me too. In the latest material 2 release, you can use (selectedChanged) instead of (selectedChange).

Close this issue, now it works.

Hi,

@gtzinos @da45 @mmalerba , I can confirm this is working as of version 2.0.0-beta.6. Thanks

Hi @mohi86, i found a new issue, which inside the event (if you use ngModel in your datepicker input) your model will not be up to date. So one workaround is to get your data from event and to save them back to model or just to use the event data.

4858

I would say this issue is solved using the selectedChanged event. However, would be nice to (also?) have the change event. All other inputs use change, why suddenly the selectedChanged event?

I am getting value of datepicker by $event mentioned as argument in selectedChanged event, it's fine. But in case of reactive forms, we get all values of inputs by the reactive form controls, but I am not able to get value of datepicker by reactive form control.

So, value of datepicker should also be get by reactive form control.

@sahil-developer-88 I'm not sure I follow.. I don't think getting the value with reactive forms is related, and seems to work just fine:

http://plnkr.co/edit/5Eqk4nDWrYUvnwYwzeK7?p=preview

@willshowell, I didn't mean that you mentioned above in your code. I will share one example with you soon...Thanks

selectedChanged event is working fine for me when I select a date with a mouse.

However, if I manually change the date I need to be able to fire change or blur event which is not working currently.

https://plnkr.co/edit/rphB4cgzUmFG7Insfpqx?p=preview

Is there a workaround for this and will this be officially implemented?

EDIT:

<md-input-container>
  <input mdInput [mdDatepicker]="picker" placeholder="Choose a date" (change)="called(2)">
  <button mdSuffix [mdDatepickerToggle]="picker"></button>
</md-input-container>
<md-datepicker #picker (selectedChanged)="called(1)" ></md-datepicker>

Change/blur event works on the input tag.

I am seeing the same behavior as @ashaykolhe . selectedChanged works only if date is selected using mouse.

@willshowell, the issue related to reactive form control is resolved after updating angular material, can you please tell me how can i update already selected datepicker value by clicking of another button click event?

+1 i encounter the same issue

In reactive form, we can change or update already selected datepicker value by setValue() by click event.
For example, this.myForm.controls['selectedDate'].setValue("07/08/2017");

Using (blur) instead of (change) will result in the function only being called if the input is changed using 2.0.0-beta.8 and seems like a simple workaround.

Hi all,
What if I need to use rxjs/Observable? Is there any workwround? I was hoping to subscribe the changes, since it can be changed by typing or the button. Any suggestion is appreciated.

    <md-input-container>
      <input mdInput #businessDatePickerInput [mdDatepicker]="businessDatePicker" placeholder="business date">
      <button mdSuffix [mdDatepickerToggle]="businessDatePicker"></button>
    </md-input-container>
    <md-datepicker #businessDatePicker></md-datepicker>
    Observable.fromEvent(this.businessDatePickerInput.nativeElement, 'change')
      .subscribe(() => {
        console.log('on change');
      });

@kylelix7 did you found any solution?
I also need to use an observable and 'selectedCanged' does not get triggered. and 'change' only works if the input is changed after typing

@kylelix7 @alexandrupaul7

With the master build, you can do something like this. It will respond to input changes and datepicker selections.

Observable
  .merge(this.datepickerInput.dateChange, this.datepickerInput.dateInput)
  .map(e => e.value)
  .distinctUntilChanged((a, b) => a.toString() === b.toString())
  .subscribe(date => console.log(date));

http://plnkr.co/edit/YFwWl6sK7VxdUgsEiDM5?p=preview

Actually, there's a workaround to get thru this, and I faced a similar situation while I was working on start/end dates using reactive forms - where I had to validate them both at the same time using the input from picker button and text box.

Below is the code, both the html and ts source of SomeComponent

some.component.html

<form [formGroup]="someFormGroup">
  <md-input-container class="input-container">
    <input #startAtUtc mdInput type="date" formControlName="startAtUtc" placeholder="Start date" [mdDatepicker]="startAtUtcPicker" [mdDatepickerFilter]="filterStartAtUtc">
    <button mdSuffix [mdDatepickerToggle]="startAtUtcPicker"></button>
  </md-input-container>
  <md-datepicker #startAtUtcPicker (selectedChanged)="onStartAtUtcChange($event)"></md-datepicker>
  <md-input-container class="input-container">
    <input #endAtUtc mdInput type="date" formControlName="endAtUtc" placeholder="End date" [mdDatepicker]="endAtUtcPicker" [mdDatepickerFilter]="filterEndAtUtc">
    <button mdSuffix [mdDatepickerToggle]="endAtUtcPicker"></button>
  </md-input-container>
  <md-datepicker #endAtUtcPicker (selectedChanged)="onEndAtUtcChange($event)"></md-datepicker>
</form>

some.component.ts

...
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/observable/fromEvent';
...
export class SomeComponent implements OnDestroy, OnInit {
  @ViewChild('startAtUtc') startAtUtcRef: ElementRef;
  @ViewChild('endAtUtc') endAtUtcRef: ElementRef;

  someFormGroup: FormGroup;

  private startAtUtc$: Subscription;
  private endAtUtc$: Subscription;

  filterStartAtUtc = (startAtUtc: Date, endAtUtc?: Date) => {
    // NOTE: implement logic
    return true;
  };

  filterEndAtUtc = (endAtUtc: Date, startAtUtc?: Date) => {
    // NOTE: implement logic
    return true;
  }

startDateValidator = (): AsyncValidatorFn => {
    return (control: AbstractControl): Promise<{ errors: ValidationErrors }> => {
      return new Promise(resolve => {
        // NOTE: implement validation rule(s)
        resolve(undefined);
      });
    };
  };

  endDateValidator = (): AsyncValidatorFn => {
    return (control: AbstractControl): Promise<{ errors: ValidationErrors }> => {
      return new Promise(resolve => {
        // NOTE: implement validation rule(s)
        resolve(undefined);
      });
    };
  };

  constructor(private readonly formBuilder: FormBuilder) {
  }

  ngOnInit(): void {
    this.someFormGroup = this.formBuilder.group({
      startAtUtc: ['', undefined), Validators.required, [this.startDateValidator()]],
      endAtUtc: ['', Validators.required, [this.endDateValidator()]]
    });

    this.startAtUtc$ = Observable.fromEvent(this.startAtUtcRef.nativeElement, 'change')
      .subscribe(() => this.someFromGroup.get('endAtUtc').updateValueAndValidity());

    this.endAtUtc$ = Observable.fromEvent(this.endAtUtcRef.nativeElement, 'change')
      .subscribe(() => this.someFromGroup.get('startAtUtc').updateValueAndValidity());
  }

  ngOnDestroy(): void {
    this.startAtUtc$.unsubscribe();
    this.endAtUtc$.unsubscribe();
  }

  onStartAtUtcChange($event: any): void {
    if ($event instanceof Date && !isNaN($event.valueOf())) {
      this.someFromGroup.get('startAtUtc').setValue($event);
      this.someFromGroup.get('startAtUtc').markAsTouched();

      this.someFromGroup.get('endAtUtc').updateValueAndValidity();
    }
  }

  onEndAtUtcChange($event: Date): void {
    if ($event instanceof Date && !isNaN($event.valueOf())) {
      this.someFromGroup.get('endAtUtc').setValue($event);
      this.someFromGroup.get('endAtUtc').markAsTouched();

      this.someFromGroup.get('startAtUtc').updateValueAndValidity();
    }
  }
}

selectedChanged will work for a mouse click in the datepicker. to get the change from the text-field you can do as @willshowell showed in this comment above:
https://github.com/angular/material2/issues/4615#issuecomment-303460818

The docs as of beta-10 say:
https://material.angular.io/components/datepicker/overview#input-and-change-events

image

To get both the mouse click and the text field changes via the docs suggested implementation, see @willshowell's plunkr above:
https://github.com/angular/material2/issues/4615#issuecomment-319952531

At first I tried this example based on the docs, but it didn't work: https://github.com/angular/material2/blob/master/src/demo-app/datepicker/datepicker-demo.ts

@willshowell thanks!

This worked for me. Although not super pretty.

.html
`


                    <input mdInput [mdDatepicker]="minDatePicker" (change)="onManualMinDateChange($event)"
                        placeholder="Min date">

                    <button mdSuffix [mdDatepickerToggle]="minDatePicker"></button>

                </md-input-container>
                <md-datepicker #minDatePicker (selectedChanged)="onMinDateChange($event)"></md-datepicker>

                <md-input-container>

                    <input mdInput [mdDatepicker]="maxDatePicker" (change)="onManualMaxDateChange($event)"
                        placeholder="Max date">

                    <button mdSuffix [mdDatepickerToggle]="maxDatePicker"></button>

                </md-input-container>

                <md-datepicker #maxDatePicker (selectedChanged)="onMaxDateChange($event)"></md-datepicker>

            </p>`

.ts

`
private minDate: Date;
private maxDate: Date;
(...)
onMinDateChange(event) {
console.log('min Date changed');
console.dir(event);
this.minDate = event;
console.log(this.minDate);
}

onMaxDateChange(event) {
console.log('max Date changed');
console.dir(event);
this.maxDate = event;
console.log(this.maxDate);
}

onManualMinDateChange(event) {
console.log('manual min Date changed');
const eve = event.target.value;
this.minDate = new Date(eve);
console.log(this.minDate);
}

onManualMaxDateChange(event) {
console.log('manual max Date changed');
const eve = event.target.value;
this.maxDate = new Date(eve);
console.log(this.maxDate);
}`

<mat-form-field>
   <input matInput [matDatepicker]="pickerStart" placeholder="From" (dateInput)="yourFunction($event)" (dateChange)="yourFunction($event)" disabled>
   <mat-datepicker-toggle matSuffix [for]="pickerStart"></mat-datepicker-toggle>
   <mat-datepicker disabled="false" #pickerStart></mat-datepicker>
</mat-form-field>

(dateInput) and (dateChange) works just fine

Is there an event to identify when the user clicks either next/previous month? I can't find it :/

@bvasqueztbc I've looked for the same event too, but I didn't find it :-(

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings