Bind an input with type datetime-local to a Date property in Angular 2

Related searches

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.

Demo Plnkr

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" />

Demo Plnkr with Directive

Bind an input with type datetime-local to a Date property in Angular 2, 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� Angular 2 custom form control with multiple inputs in template-driven way 0 Angular 2 dynamic forms example with ngmodel results in “expression has changed after it was checked”

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" />

Bind input type datetime-local in Angular 2.0.0-alpha.45 � GitHub, Bind input type datetime-local in Angular 2.0.0-alpha.45 - app.ts. The date accessor for writing a value and listening to changes that can be used by the. Two-way binding allows us to use the property to set the initial value of an <input>, and then have the user's changes flow back to the property. We can also change the property value in the code and have these changes reflected in the <input> .

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

input[datetime-local], v1.6.3, v1.6.2, v1.6.1, v1.6.0, v1.6.0-rc.2, v1.6.0-rc.1, v1.6.0-rc.0, v1.5.11, v1. 5.10 In browsers that do not yet support the HTML5 date input, a text <input type="datetime-local" ng-model="string" [name="string"] Use ngRequired instead of required when you want to data-bind to the required attribute. // this selector changes the previous behavior silently and might break existing code selector: 'input[type=date][formControlName],input[type=date][formControl],input[type=date][ngModel]' But please be aware, that this might break existing implementations that rely of the old behaviour.

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

input[date], Since many modern browsers do not yet support this input type, it is important to instead of required when you want to data-bind to the required attribute. In Angular 2, one-way data binding directive is replaced with [property]. The ng-bind directive is used for one-way binding in Angular 1.x. Angular 2.0 uses HTML DOM element property for one-way binding. The square brackets are used with property name for one-way data binding in Angular 2. For example, if we want one-way binding between Model

DatePipe, Property binding � Attribute, class, and Input valuelink. value, any. The date expression: a Date object, a number (milliseconds since UTC epoch), or an ISO string (https://www.w3.org/TR/NOTE-datetime). When not supplied, uses the end-user's local system timezone. Field type, Format, Description, Example Value� Angular date pipe used to format dates in angular according to the given date formats,timezone and country locale information. Using date pipe, we can convert a date object, a number (milliseconds from UTC) or an ISO date strings according to given predefined angular date formats or custom angular date formats.

Property binding flows a value in one direction, from a component's property into a target element property. You can't use property binding to read or pull values out of target elements. Similarly, you cannot use property binding to call a method on the target element. If the element raises events, you can listen to them with an event binding.

Angular doesn’t come with built-in two-way data binding anymore, but with APIs that allow to implement this type of binding using property and event bindings. ngModel comes as a built-in directive as part of the FormsModule to implement two-way data binding and should be preferred when building components that serve as custom form controls.

Comments
  • Have you tried to remove the [] around ng-model? The common bind is ng-model="nameOfTheVarFromscopeOrPropertie"
  • it works, but after binding it (using a directive), clicking to up & down arrows on control doesn't work correctly - when I have selected a hour sub-field, clicking to up-arrow increases hour +2 instead of +1 and when I have selected a minute subfield, clicking to up-arrow increases minute +1 and also hour +1
  • on which device / browser?
  • Google Chrome 47.0 on Windows (desktop)
  • Fixed parsing logic - see updated plnkr. toDateString() and parseDateString() should be complementary: the first formats a date to yyyy-MM-ddTHH:mm format, the second parses it back to a Date object.
  • Been a while your component seems nice, but how would you go updating the value when the value is lazy loaded? When set through ngModel at a later stage the value shows as empty. You can try this by wrapping the date = in a setTimeout
  • I like this approach, but this has a drawback for me: filterDateFrom is type Date, but (ngModelChange)="filterDateFrom = $event" assigns a string. (and thus also the timezone is off)
  • @ne1410s i tried your sample but i need to show LongTime format for 24 hours
  • @Dan Simon should your bound property not be named '_dateTimeLocal ' with the underscore?
  • @Pascal - I think I am missing some context to your question. I am going to guess you mean the underscore shouldn't be there due to a coding style convention? Maybe, I am not up to speed with the best JS coding practices, I tend to make all my private vars start with underscore. If you're asking from a functional perspective, the code functions as expected, which can be viewed and run from the plunker link in my answer.
  • Its not about style. Your binding expression {{dateTimeLocal}} is bound to this._dateTimeLocal = new Date(); which has an underscore. I do not understand why this works at all!?
  • @Pascal: Ah ok, I think I understand where the confusion is coming from. As you know C#, the simplest translation is 'dateTimeLocal' with a type of string is a property and '_dateTimeLocal' with a type of DateTime is a field. Typescript supports Accessors and that is where 'dateTimeLocal' comes from, note the 'get' and 'set' keywords on what appear to be functions in the bottom of the code pasted above, this is the TypeScript syntax. I did this because the HTML control isn't a DateTime but a String, so the Accessors deals with that.
  • Ah I did not know that dateTimeLocal is a getter. Haven`t fully scrolled down ;-) Now I understand it. Grabs value from private class field.