web-dev-qa-db-fra.com

Angular2: Comment utiliser un objet Date en JavaScript avec la liaison bidirectionnelle NgModel

Je travaille avec Angular 2 et j'ai ce code:

JS, ce code lance la variable employee du modèle:

handleEmployee(employee : Employee){
        this.employee = employee;
        this.employee.startDate = new Date('2005/01/01');
        console.log(this.employee);
    }

Modèle:

...
<div>
    <label>Start date: </label>
    <input [(ngModel)]="employee.startDate" type="date" name="startDate"/>
  </div>
  <div>
...

D'autres données telles que le prénom sont affichées correctement. Mais pour la date, je viens d'avoir:

mm/dd/yyyy

Dans l'élément d'entrée, ce qui devrait être une date.

Comment puis-je faire ceci?

48
stijn.aerts

MISE À JOUR:

quand j'ai écrit cette réponse DatePipe n'existait pas, maintenant vous pouvez simplement le faire

<input [ngModel]="startDate | date:'yyyy-MM-dd'" (ngModelChange)="startDate = $event" type="date" name="startDate"/>

`


Ancienne réponse:

PLUNKER

Vous devez convertir date object au format input type="date" qui est yyyy-mm-dd, voici comment cela fonctionnera

Modèle:

<input [(ngModel)]="humanDate" type="date" name="startDate"/>

Composant (TS):

export class App {
  startDate: any;

  constructor() {
    this.startDate = new Date(2005, 1, 4);
  }

  set humanDate(e){
    e = e.split('-');
    let d = new Date(Date.UTC(e[0], e[1]-1, e[2]));
    this.startDate.setFullYear(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
  }

  get humanDate(){
    return this.startDate.toISOString().substring(0, 10);
  }
}
89
Ankit Singh

Lire pipes et ngModel et ma décision:

<input type="date" class="form-control" id="myDate" [ngModel]="myDate | date:'y-MM-dd'" (ngModelChange)="myDate = $event" name="birthday">
18
mbraint

FormControls (à la fois basé sur un modèle et réactif) souscrivent des valeurs et écrivent des valeurs via des directives implémentant ControlValueAccessor. Examinez la méthode appropriée selectValueAccessor , utilisée dans toutes les directives nécessaires. Les contrôles de saisie normaux (par exemple, _<input type="text">_) ou les zones de texte sont gérés par le DefaultValueAccessor . Un autre exemple est CheckboxValueAccessor qui est appliqué aux contrôles d'entrée de case à cocher.

Le travail n'est pas compliqué du tout. Nous avons juste besoin d'implémenter un nouvel accesseur de valeur pour les contrôles de saisie de date.
DateValueAccessor est un joli nom:

_// date-value-accessor.ts

import { Directive, ElementRef, HostListener, Renderer, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

export const DATE_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => DateValueAccessor),
  multi: true
};

/**
 * The accessor for writing a value and listening to changes on a date input element
 *
 *  ### Example
 *  `<input type="date" name="myBirthday" ngModel useValueAsDate>`
 */
@Directive({
  selector: '[useValueAsDate]',
  providers: [DATE_VALUE_ACCESSOR]
})
export class DateValueAccessor implements ControlValueAccessor {

  @HostListener('input', ['$event.target.valueAsDate']) onChange = (_: any) => { };
  @HostListener('blur', []) onTouched = () => { };

  constructor(private _renderer: Renderer, private _elementRef: ElementRef) { }

  writeValue(value: Date): void {
    this._renderer.setElementProperty(this._elementRef.nativeElement, 'valueAsDate', value);
  }

  registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
  registerOnTouched(fn: () => void): void { this.onTouched = fn; }

  setDisabledState(isDisabled: boolean): void {
    this._renderer.setElementProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
  }
}
_

Nous attachons le DateValueAccessor au multi-fournisseur _DATE_VALUE_ACCESSOR_, de sorte que selectValueAccessor puisse le trouver.

La seule question est de savoir quel sélecteur devrait être utilisé. J'ai opté pour une solution opt-in.
Ici, DateValueAccessor sélectionne l’attribut "useValueAsDate".

_<input type="date" name="myBirthday" ngModel useValueAsDate>

OR

<input type="date" name="myBirthday" [(ngModel)]="myBirthday" useValueAsDate>

OR

<input type="date" formControlName="myBirthday" useValueAsDate>
_

Il est également possible de corriger l'implémentation par défaut.
Le sélecteur suivant activerait la fonction comme par magie.

_// 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]'
_

Mais sachez que cela pourrait casser les implémentations existantes qui reposent sur l'ancien comportement. Je choisirais donc la version opt-in!

Tout est sur NPM et Github

Pour votre commodité, j'ai créé le projet angular-data-value-accessor sur Github.
Un package NPM est également disponible:

_npm install --save angular-date-value-accessor
_

Ensuite, importez simplement le module via NgModule:

_// app.module.ts

import { DateValueAccessorModule } from 'angular-date-value-accessor';

@NgModule({
  imports: [
    DateValueAccessorModule
  ]
})
export class AppModule { }
_

Vous pouvez maintenant appliquer le "useValueAsDate" à vos contrôles de saisie de date.

Démo

Bien sûr, il y a une démonstration à: http://johanneshoppe.github.io/angular-date-value-accessor/

8
Johannes Hoppe

J'ai commencé à essayer d'implémenter la solution d'Ankit Singh et j'ai rencontré quelques problèmes de validation et de fuseau horaire. (Même après avoir essayé les suggestions dans la section commentaire de cette réponse)

Au lieu de cela, j'ai choisi d'utiliser moment.js pour gérer la transformation entre chaîne et date à l'aide de chaînes de date au format ISO8601. J'ai eu d'excellents résultats dans le passé en utilisant moment.js, donc ce n'était pas une décision difficile. Cela semble bien fonctionner pour moi, j'espère que quelqu'un d'autre trouvera cela utile.

Pour mon application Angular 2, j'ai lancé npm install --save moment, puis j'ai transformé la solution d'Ankit en wrapper autour d'un objet js Date:

import * as moment from 'moment';

export class NgDate {

    date: any;

    constructor() {
        this.date = new Date();
    }

    set dateInput(e) {
        if (e != "") {
            var momentDate = moment(e, moment.ISO_8601).toDate();
            this.date = momentDate;
        }
        else {
            this.date = null;
        }
    }

    get dateInput() {
        if(this.date == null)
        {
            return "";
        }

        var stringToReturn = moment(this.date).format().substring(0, 10);
        return stringToReturn;
    }
}

Puis pour le HTML:

<input type="date" name="someDate" [(ngModel)]="ngDateModel.dateInput"/>
3
boylec1986

Fixé avec ce code:

handleEmployee(employee : Employee){
        this.employee = employee;

        let dateString : string = employee.startDate.toString();
        let days : number = parseInt(dateString.substring(8, 10));
        let months : number = parseInt(dateString.substring(5, 7));
        let years : number = parseInt(dateString.substring(0, 5));
        let goodDate : Date = new Date(years + "/" + months + "/" + days);
        goodDate.setDate(goodDate.getDate() + 2);
        this.date = goodDate.toISOString().substring(0, 10);
    }

Html:

<div>
    <label>Start date: </label>
    <input [(ngModel)]="date" type="date" name="startDate"/>
  </div>
2
stijn.aerts