Привязать вход с типом datetime-local к свойству Date в Angular 2

Можно связать свойство компонента типа Date с входом HTML5 с типом, установленным на datetime-local?

В моем компоненте у меня есть poperty:

public filterDateFrom: Date;

и в моем шаблоне у меня есть вход, определенный как:

<input type="datetime-local" [(ngModel)]="filterDateFrom" />

но привязка не работает.

Ответы

Ответ 1

Демо Plnkr

Вы можете привязать дату, используя следующий формат: yyyy-MM-ddTHH:mm, который вы также можете получить из date.toISOString().slice(0,16) (срез удаляет временную часть после минут).

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

Имейте в виду, что date.toISOString() вернет смещение даты из локального времени. Вы также можете сами построить строку даты:

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

Если вы хотите привязать выбор к модели Date, вы можете использовать это для создания настраиваемого компонента даты:

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

Пользователи вашего компонента привязываются к модели Date с привязкой к двусторонней модели:

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

Или, если вы хотите избежать пользовательских тегов, перепишите компонент как директиву:

<input type="datetime-local" [(date)]="date" />

Демо Plnkr с директивой

Ответ 2

Теперь, когда его Spring 2017, DatePipe отправлен OOTB. Вы можете добиться односторонней привязки, указав параметры формата в трубу. Например:

<input type="datetime-local" [ngModel]="filterDateFrom | date:'yyyy-MM-ddTHH:mm'" />

Незначительное предостережение заключается в том, что вам необходимо управлять модельной частью DOM → для обработки клиентских изменений в элементе управления (если только я не пропущу что-то!), но это кажется намного более чистым таким образом.


Update

Похоже, что я действительно что-то пропустил!

Добавление ngModelChange к приведенному выше должно содержать DOM → модельную сторону процесса двусторонней привязки:

<input type="datetime-local" 
       [ngModel]="filterDateFrom | date:'yyyy-MM-ddTHH:mm'
       (ngModelChange)="filterDateFrom = $event" />

Ответ 3

Я тоже изучал эту проблему и начал спускаться по этой примерной дороге. Однако вы можете использовать [(ngModel)] на входе типа [date, datetime, datetime-local]. Ключ должен соответствовать ожидаемому формату, который ожидает контроллер. В этом случае он ожидает этого формата. Это также означает, что тип, который вы привязываете к элементу управления, должен быть строкой. Я привел пример plunker, который демонстрирует, как использовать [(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);
  }
}

Ответ 4

Вдохновленный ответом @ne1410s, я закончил делать что-то очень похожее, но без потери типа даты.

Я использовал канал, чтобы объявить ngModel и вызвать метод dateChanged просто для того, чтобы вернуть преобразование новой даты в ts.

HTML-код:

<input type="datetime-local" [ngModel]="filterDateFrom | date:'yyyy-MM-ddTHH:mm'" (ngModelChange)="filterDateFrom = dateChanged($event)"/>

код ts:

 dateChanged(eventDate: string): Date | null {
   return !!eventDate ? new Date(eventDate) : null;
 }