Bind an input with type datetime-local to a Date property in Angular 2
Solution 1
You can bind to a date using the following format: yyyy-MM-ddTHH:mm
, which you can also get from date.toISOString().slice(0,16)
(the slice removes the time portion after the minutes).
@Component({
selector: 'app',
template: `<input type="datetime-local" [value]="date"
(change)="date=$event.target.value" /> {{date}}`
})
export class AppComponent {
date: string;
constructor() {
this.date = new Date().toISOString().slice(0, 16);
}
}
Keep in mind that date.toISOString()
will return a date offset from local time. You can also construct the date string yourself:
private toDateString(date: Date): string {
return (date.getFullYear().toString() + '-'
+ ("0" + (date.getMonth() + 1)).slice(-2) + '-'
+ ("0" + (date.getDate())).slice(-2))
+ 'T' + date.toTimeString().slice(0,5);
}
If you want to be able to bind the select to a Date
model, you can use this to build a custom date component:
@Component({
selector: 'my-date',
events: ['dateChange'],
template: `<input type="datetime-local" [value] = "_date"
(change) = "onDateChange($event.target.value)" />`
})
export class MyDate{
private _date: string;
@Input() set date(d: Date) {
this._date = this.toDateString(d);
}
@Output() dateChange: EventEmitter<Date>;
constructor() {
this.date = new Date();
this.dateChange = new EventEmitter();
}
private toDateString(date: Date): string {
return (date.getFullYear().toString() + '-'
+ ("0" + (date.getMonth() + 1)).slice(-2) + '-'
+ ("0" + (date.getDate())).slice(-2))
+ 'T' + date.toTimeString().slice(0,5);
}
private parseDateString(date:string): Date {
date = date.replace('T','-');
var parts = date.split('-');
var timeParts = parts[3].split(':');
// new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
return new Date(parts[0], parts[1]-1, parts[2], timeParts[0], timeParts[1]); // Note: months are 0-based
}
private onDateChange(value: string): void {
if (value != this._date) {
var parsedDate = this.parseDateString(value);
// check if date is valid first
if (parsedDate.getTime() != NaN) {
this._date = value;
this.dateChange.emit(parsedDate);
}
}
}
}
Users of your component would bind to a Date
model with two-way model binding:
@Component({
selector: 'my-app',
directives: [MyDate],
template: '<my-date [(date)]="date"></my-date> {{date}}'
})
export class AppComponent {
@Input() date: Date;
constructor() {
this.date = new Date();
}
}
Or if you want to avoid custom tags, rewrite the component as a directive:
<input type="datetime-local" [(date)]="date" />
Solution 2
Now that its Spring 2017, DatePipe
is shipped OOTB. You can achieve (one-way) binding by specifying format parameters to the pipe. For example:
<input type="datetime-local" [ngModel]="filterDateFrom | date:'yyyy-MM-ddTHH:mm'" />
Slight caveat is that you can not use two-way binding with this technique, you have to use one way binding with the data pipe, then manage the DOM to model change events to handle client changes to the control (unless I'm missing something!), but it seems a lot cleaner this way.
Update
Looks like I was indeed missing something!
Adding ngModelChange
to the above should provide the DOM --> model side of the two-way binding process:
<input type="datetime-local"
[ngModel]="filterDateFrom | date:'yyyy-MM-ddTHH:mm'"
(ngModelChange)="filterDateFrom = $event" />
Solution 3
Inspired by @ne1410s answer I ended doing something very similar but without losing the date type.
I used a pipe to declare the ngModel
and call a method dateChanged
just to return the conversion of the new Date in the ts
.
html code:
<input type="datetime-local" [ngModel]="filterDateFrom | date:'yyyy-MM-ddTHH:mm'" (ngModelChange)="filterDateFrom = dateChanged($event)"/>
ts code:
dateChanged(eventDate: string): Date | null {
return !!eventDate ? new Date(eventDate) : null;
}
Solution 4
I was looking into this problem as well and started to go down this examples road. However, you can use [(ngModel)] on an input of the type [date,datetime,datetime-local]. The key is to match the expected format the control is expecting. In this case it expects this format. Which also means the type that you bind to the control needs to be a string. I have provided an example plunker, that demonstrates how to use [(ngModel)].
import { Component } from 'angular2/core';
@Component({
selector: 'my-app',
template: `
<form>
<input type="datetime-local" [(ngModel)]="dateTimeLocal"><br />
{{dateTimeLocal}}
</form>
`
})
export class AppComponent {
private _dateTimeLocal: Date;
constructor() {
this._dateTimeLocal = new Date();
}
private parseDateToStringWithFormat(date: Date): string {
let result: string;
let dd = date.getDate().toString();
let mm = (date.getMonth() + 1).toString();
let hh = date.getHours().toString();
let min = date.getMinutes().toString();
dd = dd.length === 2 ? dd : "0" + dd;
mm = mm.length === 2 ? mm : "0" + mm;
hh = hh.length === 2 ? hh : "0" + hh;
min = min.length === 2 ? min : "0" + min;
result = [date.getFullYear(), '-', mm, '-', dd, 'T', hh, ':', min].join('');
return result;
}
public set dateTimeLocal(v: string) {
let actualParsedDate = v ? new Date(v) : new Date();
let normalizedParsedDate = new Date(actualParsedDate.getTime() + (actualParsedDate.getTimezoneOffset() * 60000));
this._dateTimeLocal = normalizedParsedDate;
}
public get dateTimeLocal(): string {
return this.parseDateToStringWithFormat(this._dateTimeLocal);
}
}
Dominik Palo
My interests: Programming (especially mobile apps, Xamarin, .NET C#, WPF and Node.js), 3D printing (RepRap) and open-hardware
Updated on March 28, 2020Comments
-
Dominik Palo about 4 years
It is possible to bind a component property of Date type to a HTML5 input with type set to
datetime-local
?In my component I have a poperty:
public filterDateFrom: Date;
and in my template I have an input defined as:
<input type="datetime-local" [(ngModel)]="filterDateFrom" />
but binding doesn't work.