web-dev-qa-db-fra.com

Liez une entrée de type datetime-local à une propriété Date dans Angular 2

Il est possible de lier une propriété de composant de type Date à une entrée HTML5 avec un type défini sur datetime-local?

Dans mon composant j'ai une poperty:

public filterDateFrom: Date;

et dans mon modèle, j'ai une entrée définie comme:

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

mais la liaison ne fonctionne pas.

17
Dominik Palo

Démo Plnkr

Vous pouvez vous lier à une date en utilisant le format suivant: yyyy-MM-ddTHH:mm, Que vous pouvez également obtenir à partir de date.toISOString().slice(0,16) (la tranche supprime la portion de temps après les 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);
    }
}

Gardez à l'esprit que date.toISOString() renverra un décalage de date par rapport à l'heure locale. Vous pouvez également construire vous-même la chaîne de date:

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

Si vous souhaitez pouvoir lier la sélection à un modèle Date, vous pouvez l'utiliser pour créer un composant de date personnalisé:

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

Les utilisateurs de votre composant se lieraient à un modèle Date avec une liaison de modèle bidirectionnelle:

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

Ou si vous voulez éviter les balises personnalisées, réécrivez le composant en tant que directive:

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

Plnkr de démonstration avec directive

20
pixelbits

Maintenant que son printemps 2017, DatePipe est expédié OOTB. Vous pouvez obtenir une liaison (unidirectionnelle) en spécifiant des paramètres de format pour le tuyau. Par exemple:

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

Une légère mise en garde est que vous devez ensuite gérer le côté DOM -> modèle des choses pour gérer les modifications clientes du contrôle (sauf si je manque quelque chose!), Mais cela semble beaucoup plus propre de cette façon.


Mise à jour

On dirait qu'il me manquait bien quelque chose!

L'ajout de ngModelChange à ce qui précède devrait fournir le côté modèle DOM -> du processus de liaison bidirectionnelle:

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

J'examinais également ce problème et j'ai commencé à suivre cette voie d'exemples. Cependant, vous pouvez utiliser [(ngModel)] sur une entrée du type [date, datetime, datetime-local]. La clé est de faire correspondre le format attendu que le contrôle attend. Dans ce cas, il attend ce format . Ce qui signifie également que le type que vous liez au contrôle doit être une chaîne. J'ai fourni un exemple plunker , qui montre comment utiliser [(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
Dan Simon

Inspiré par la réponse de @ ne1410s, j'ai fini par faire quelque chose de très similaire mais sans perdre le type de date.

J'ai utilisé un canal pour déclarer le ngModel et appeler une méthode dateChanged juste pour renvoyer la conversion de la nouvelle date dans le ts.

code HTML:

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

code ts:

 dateChanged(eventDate: string): Date | null {
   return !!eventDate ? new Date(eventDate) : null;
 }
0
Daniel Piñeiro