1

Description


I'm trying to wrap the MatDatePicker in a custom component called MyDatePicker so that I can use it in the html as the following.

Issue


The dateVariable is always undefined and seems the two-way binding I implemented below is not working. Once the user selects a new date from the picker, the setter gets called. However, the new value is not binded to the dateVariable.

Questions


  • How can I implement a two-way binding between my custom date picker and a variable?
  • How is it possible to implement a one-way binding (from date picker -> variable)?

Implementation


HTML file:

<my-datepicker [(selectedDate)]="dateVariable" placeholder="some text"></my-datepicker>

<button (click)="btnClick()">Show Selected Date</button>

The TS file:

//...

    dateVariable: string;       

    btnClick() {

      console.log('selected date:', this.dateVariable);
   }
//...

MyDatepicker.component.ts

export class MyDatepickerComponent {

    dateValue: string;
    @Input() placeholder: string;

    @Output() dateChange: EventEmitter<string> = new EventEmitter<string>();

    @Input()
    get selectedDate() {
        console.log('getter');
        return this.dateValue;
    }
    set selectedDate(val) {
        console.log('setter');
        this.dateValue = val;
        this.dateChange.emit(this.dateValue);
    }

    pickerEvent(event: MatDatepickerInputEvent<Date>) {
        this.selectedDate = event.value.toISOString();
    }
}

MyDatepicker.component.html:

<mat-form-field>
        <input matInput [matDatepicker]='pickerDate' placeholder='{{placeholder}}' (dateInput)="pickerEvent($event)">
        <mat-datepicker-toggle matSuffix [for]='pickerDate'></mat-datepicker-toggle>
        <mat-datepicker #pickerDate></mat-datepicker>
</mat-form-field>
A-Sharabiani
  • 17,750
  • 17
  • 113
  • 128

2 Answers2

1

For two-way-binding to work, the Output() has to be named the same as the Input() with the suffix Change. So change your code as such:

@Output() selectedDateChange: EventEmitter<string> = new EventEmitter<string>();

and the same in the setter:

set selectedDate(val) {
    console.log('setter');
    this.dateValue = val;
    this.selectedDateChange.emit(this.dateValue);
}
AT82
  • 71,416
  • 24
  • 140
  • 167
1

As already stated: For the two-way binding [()] (banana-in-a-box syntax) we have to write an @Input with the corresponding @Output like:

@Input() selectedDate: Date;

@Output() selectedDateChange: EventEmitter<Date> = new EventEmitter<Date>();

This can be used with an Input-Binding and a separate Output-Binding:

<my-datepicker [selectedDate]="dateVariable" (selectedDateChange)="dateVariable = $event" placeholder="some text"></my-datepicker>

Or with a little syntactic sugar provided by Angular with the banana-in-the-box syntax:

<my-datepicker [(selectedDate)]="dateVariable" placeholder="some text"></my-datepicker>

Aside Notes:

1) I think you need to bind the matInput with the actual Date value as well

<mat-form-field>
        <input matInput [matDatepicker]='pickerDate' [value]="selectedDate" placeholder='{{placeholder}}' (dateInput)="pickerEvent($event)">
        <mat-datepicker-toggle matSuffix [for]='pickerDate'></mat-datepicker-toggle>
        <mat-datepicker #pickerDate></mat-datepicker>
</mat-form-field>

2) If you want to use my-datepicker inside of an Angular form you need to implement the ControlValueAccessor interface

3) If the only reason you are writing this custom component is that you want to use an ISO date string as input I would suggest handling this transformation on an API / Service level instead of in a component.

ChrisY
  • 1,681
  • 10
  • 12
  • Why do you think I need to "bind the matInput with the actual Date value as well"? I changed the name as you said and it works. Without binding the matInpur with the Date – A-Sharabiani Jul 31 '19 at 19:28
  • Also when I do that ([value]="selectedDate") it runs the getter multiple times ! – A-Sharabiani Jul 31 '19 at 19:32
  • I was not sure if you need it. If it works for you that way everything is fine :) The multiple setter calls are most likely due to the change-detection in Angular, which is triggered behind the scenes on several events that occur in the browser by default. You can change that if you change the `ChangeDetectionStrategy` of your datepicker component to `OnPush` but then you will have to work with immutable datastructures – ChrisY Aug 01 '19 at 17:47