Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Format error when using a custom ControlValueAccessor for an input type=date in Angular2 rc4

Before I begin I am using Angular2 rc4 with typescript. In case you are wondering, I cannot yet upgrade, so I have to make this work within the constraints of rc4. In addition I'm an ng2 noob, so still getting my head around concepts and all the changes between the "release candidates".

Having done quite a bit of research about value accessors, I decided to create one specifically for <input type=date /> to avoid requiring a separate mule property in the component just to turn the date into a string for the input.

I found this gist, which seemed to do pretty much what I wanted to do.

Using that as a starting, and almost ending, point, I came up with this:

import { Directive, ElementRef, Renderer, forwardRef } from "@angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
import { isBlank, isDate } from "@angular/forms/src/facade/lang";
import moment from "moment";

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

@Directive({
    selector:
    "input[type=date][formControlName],input[type=date][formControl],input[type=date][ngModel]",
    host: {
        "(change)": "onChange($event.target.value)",
        "(input)": "onChange($event.target.value)",
        "(blur)": "onTouched()"
    },
    providers: [DATE_VALUE_ACCESSOR]
})
export class DateValueAccessor implements ControlValueAccessor {
    onChange = (_: any) => {
        // we don't need to do anything here
    };

    onTouched = () => {
        // we don't need to do anything here
    };

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

    writeValue(value: any): void {
        var normalizedValue: string = isBlank(value) ? "" : (isDate(value) ? moment(value).format("YYYY-MM-DD") : "");
        this._renderer.setElementProperty(this._elementRef.nativeElement, "value", normalizedValue);
    }

    registerOnChange(fn: (_: any) => void): void {
        this.onChange = (value: string) => {
            var date: moment.Moment = moment(value, "YYYY-MM-DD");
            var newValue: Date = date.isValid() ? date.toDate() : undefined;
            fn(newValue);
        };
    }

    registerOnTouched(fn: () => void): void {
        this.onTouched = fn;
    }
}

I feel I'm almost there, but not quite!

update When the form first loads, everything is hunky dory, but when I try to pick a new date, the browser complains about the format. /update

The error I can see in my devtools is:

The specified value "Fri Sep 09 2016 00:00:00 GMT+0100 (GMT Summer Time)" does not conform to the required format, "yyyy-MM-dd".

I have burned a bit of time tweaking the code and moving things around. I've also tried to make a call to writeValue from inside onChange (as can be seen in this plnkr), but I still saw the error...

Has anyone done this? Is there something obvious in my code that I've missed?

like image 252
The Bearded Llama Avatar asked Sep 08 '16 16:09

The Bearded Llama


1 Answers

Admittedly using a more up to date version of Angular (2.1.0) but the changes should apply and you can see the model binds correctly to your formatted string, here's a Plunker: https://plnkr.co/edit/8U0o0m49646tMH0kr1Mx?p=preview

The only change to your code is:

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

This does not have the display text vary as that is, to the best of my knowledge, bound to the input type="date" as an element and cannot be overridden. A quick search takes me to Is there any way to change input type="date" format? which seems to back this claim up.

like image 63
silentsod Avatar answered Nov 11 '22 19:11

silentsod