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>

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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
Also when I do that ([value]="selectedDate") it runs the getter multiple times !
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
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);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.