A-Sharabiani
A-Sharabiani

Reputation: 19329

Implement variable binding in mat-datepicker

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


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>

Upvotes: 1

Views: 3149

Answers (2)

ChrisY
ChrisY

Reputation: 1783

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.

Upvotes: 1

AVJT82
AVJT82

Reputation: 73357

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);
}

Upvotes: 1

Related Questions