Angular 2: How to use JavaScript Date Object with NgModel two way binding

后端 未结 7 1695
醉酒成梦
醉酒成梦 2020-12-02 10:01

I\'m working with Angular 2 and I have this code:

JS, this code initiates the employee-variable for the template:

handleEmployee(employee : Employee)         


        
相关标签:
7条回答
  • 2020-12-02 10:10

    I think the accepted answer lacks a function call to transform input date string to Date object. So this:

    (ngModelChange)="startDate = $event"
    

    Should be something like:

    (ngModelChange)="startDate = toDate($event)"
    

    I'm using Moment.js, which makes things MUCH easier:

    my.component.ts

    ...
    import * as moment from 'moment';
    ...
    @Component ({
      ...
    })
    export class MyComponent implements OnInit {
    
      public fromDate: moment.Moment;
      public toDate: moment.Moment;
      
      ngOnInit() {
        this.toDate = moment();
        this.fromDate = moment().subtract(1, 'week');
      }
    
      dateStringToMoment(dateString: string): moment.Moment {
        return moment(dateString);
      }

    my-component.html

    ...
    <input type="date" min="{{ fromDate | date:'yyyy-MM-dd' }}" name="toDate" [ngModel]="toDate | date:'yyyy-MM-dd'" (ngModelChange)="toDate = dateStringToMoment($event)">
    <input type="date" max="{{ toDate | date:'yyyy-MM-dd' }}" name="fromDate" [ngModel]="fromDate | date:'yyyy-MM-dd'" (ngModelChange)="fromDate = dateStringToMoment($event)">
    ...

    0 讨论(0)
  • 2020-12-02 10:14

    I began trying to implement Ankit Singh's solution and ran in to a few problems with validation and timezone stuff. (Even after trying the suggestions in the comment section of that answer)

    Instead I chose to utilize moment.js to handle the transforming between string and date using ISO8601 format date strings. I've had great results in the past using moment.js so this was not a difficult decision. Seems to be working well for me, hopefully someone else finds this useful.

    For my Angular 2 app I ran npm install --save moment and then turned Ankit's solution into a wrapper around a js Date object:

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

    Then for the HTML:

    <input type="date" name="someDate" [(ngModel)]="ngDateModel.dateInput"/>
    
    0 讨论(0)
  • 2020-12-02 10:14

    Fixed it with this 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>
    
    0 讨论(0)
  • 2020-12-02 10:24

    UPDATE:

    StackBlitz

    when I wrote this answer DatePipe did not exist, now you can just do this

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

    `


    Old Answer:

    PLUNKER

    You need to convert date object in the input type="date" format which is yyyy-mm-dd, this is how it will work

    Template:

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

    Component (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);
      }
    }
    
    0 讨论(0)
  • 2020-12-02 10:26

    FormControls (both template-driven and reactive) subscribe for values and write values via Directives that implement ControlValueAccessor. Take a look at the relevant method selectValueAccessor, which is used in all necessary directives. Normal input controls (e.g. <input type="text">) or textareas are handled by the DefaultValueAccessor. Another example is the CheckboxValueAccessor which is applied to checkbox input controls.

    The job isn't complicated at all. We just need to implement a new value accessor for date input controls.
    DateValueAccessor is a nice name:

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

    We attach the DateValueAccessor to the multi-provider DATE_VALUE_ACCESSOR, so that selectValueAccessor can find it.

    The only question is, which selector should be used. I decided for an opt-in solution.
    Here the DateValueAccessor selects on the attribute "useValueAsDate".

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

    It is also possible to fix the default implementation.
    The following selector would activate the feature magically.

    // 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. So I would go for the opt-in version!

    It's all on NPM and Github

    For your convenience, I created the project angular-data-value-accessor on Github.
    There is also a NPM package available:

    npm install --save angular-date-value-accessor
    

    Then just import the module via NgModule:

    // app.module.ts
    
    import { DateValueAccessorModule } from 'angular-date-value-accessor';
    
    @NgModule({
      imports: [
        DateValueAccessorModule
      ]
    })
    export class AppModule { }
    

    Now you can apply the "useValueAsDate" to your date input controls.

    Demo

    Of course, there is a demo at: http://johanneshoppe.github.io/angular-date-value-accessor/

    0 讨论(0)
  • 2020-12-02 10:27

    Created a local string variable

    dateField: string;
    

    And I'm binding that to my form

    Date input

    <input type="text" class="form-control" required [(ngModel)]="dateField" />
    

    Later just assign that back to the Date property just before making any API call

    insert() {
        this.myObjet.date = new Date(this.dateField);
        ... call api
    
    update() {
        this.myObjet.date = new Date(this.dateField);
        ... call api
    
    0 讨论(0)
提交回复
热议问题