2

Suppose a component (inside the angular app) having several reactive forms containing datepickers (I'm using mat-datepicker from angular material lib) and other inputs. After a user hit "Submit" button I send the value of forms to backed (with HttpClient.post). The problem is that datepicker fields are serialized as "2020-11-18T22:00:00.000Z" (obviously Date.toJSON() method is called) while backend expects other format.

Note that I'm using formly lib to build my forms, cause set of components on each form may vary. You might not be familiar with formly but anyway set of datepickers may vary as well, so I can't convert datepicker fields directly cause I don't know the exact list of date fields in a place where I send the value of forms.

Is there an elegant solution to my problem? Can't think up something better than monkey-patching Date.prototype.toJSON() or looping through objects sent on the server, check the type of fields and changing field if it's a Date? I can't find a way to set the format of value output by datepicker either in material or in formly.

Eugeny89
  • 3,797
  • 7
  • 51
  • 98
  • This might help: https://stackoverflow.com/questions/46215105/angular-httpclient-custom-json-parser-jsog – str Nov 30 '20 at 10:12
  • 1
    What you want is to customise how date are serialised, which is offered by `mat-datepicker` https://material.angular.io/components/datepicker/overview#customizing-the-parse-and-display-formats – maxime1992 Nov 30 '20 at 10:42
  • 1
    @maxime1992 please correct me if I'm wrong, but as far as I can see there's a way to customize parse and input formats, no way to explain `MatDatepicker` to put e.g. "YYYY-mm-DD" in form value – Eugeny89 Nov 30 '20 at 13:27
  • 1
    Did you think about creating a ControlValueAccessor implementation to wrap the datepicker component while changing the model in the `writeValue` and before `onChange` ? – ibenjelloun Dec 03 '20 at 10:05

3 Answers3

3

Implementing ControlValueAccessor would be an elegant solution. The idea is to create a date picker component that takes your date format as input and send back your format as output.

For that you just have to create a new component that I would call MatDatepickerWrapperComponent for this example. The template of this component would be nothing more than the material date picker :

<mat-form-field appearance="fill">
  <mat-label>Choose a date</mat-label>
  <input matInput [matDatepicker]="picker" [(ngModel)]="model" (ngModelChange)="modelChange($event)">
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <mat-datepicker #picker></mat-datepicker>
</mat-form-field>

From the component side, you will have to implement ControlValueAccessor, and do the transformations you need :

writeValue(value: string): void {
    this.model = transformDateFromMyFormatToIso(value);
}
modelChange(value: string) {
    const transformedValue = transformIsoDateToMyFormat(value);
    this.onChange(transformedValue);
  }

You can now add the new component to a form the way you would have added the original one.

Here is a running stackblitz example.

ibenjelloun
  • 7,425
  • 2
  • 29
  • 53
  • Did this answer work in the context of a Formly datepicker field? The provided Stackblitz does not include Formly and I'm unable to duplicate the solution using Formly. [Stackblitz example](https://stackblitz.com/edit/angular-formly-material-datepicker-p2ygu8?file=src/app/components/formly-datepicker-type/formly-datepicker-type.component.ts). On Simple example, DOB is using the Formly datepicker and Anniversary is using custom datepicker. Desired date output is "yyyy-M-d" after submitting. – ctaleck Feb 05 '21 at 20:15
  • @ChristopherTaleck Yes it should work, here is documentation of how to add custom template to formly : https://formly.dev/guide/custom-formly-field – ibenjelloun Feb 09 '21 at 08:11
1

I would try to use an HTTP interceptor. I think you might be able to use your interceptor to change all the dates in the body to something else.

enter link description here is an example of how the interceptor works. In the example, a header is added. Here and here are an example of modifying the body.

Robin Dijkhof
  • 18,665
  • 11
  • 65
  • 116
1

I had the same problem. I could solve this by overriding JSON date converter in the app component.:

overrideToJSONDate() {
    Date.prototype.toJSON = function () {
      return new Date(this).toLocaleString();
    }
  }

I call this function once in app component's constructor. You can apply and logic inside the function but be sure, to return with current string.

Becike
  • 71
  • 3
  • And where do you think it's a best place to call this overrideToJSONDate() in angular app? – Eugeny89 Dec 06 '20 at 09:55
  • I called the function in the app.component's cunstructor once, and thats all. From there, all of your Date will be stringified by your funciton. – Becike Dec 06 '20 at 20:27