javascriptangularangular-materialangular-formly

Default Date to JSON conversion format in Angular app


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.


Solution

  • 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.