The ngrx-forms library is a valuable tool for bringing Redux-style state management to your application’s forms. However, its concept of Value Boxing hides a few pitfalls that can be tricky to troubleshoot.
Learn what Value Boxing is and find out how to parse common error messages and add Value Boxing to your ngrx-forms.
What is Value Boxing?
Value Boxing is a technique used by ngrx-forms to ensure non-primitive form values are correctly inferred as form controls. The ngrx-forms v4 release requires all non-primitive FormControlState values to be Boxed.
Consider the UserForm
interface below: it has a name
property with a primitive type of string
, and a tags
property with a non-primitive type of string[];
export interface UserForm {
name: string;
tags: string[];
}
In ngrx-forms v4, the non-primitive tags
value must now be wrapped in the Boxed interface.
export interface UserForm {
name: string;
tags: Boxed<string[]>;
}
This will wrap the tags
form value in the Boxed interface. If you console log the user form, the tags control’s value will look something like this:
controls: [],
errors: {},
...
value: {
tags: {
value: ['foo', 'bar'],
_boxed: ""
}
}
The library will automatically “unbox” this value in your templates via its default Value Converter. It also ships with box
and unbox
functions, allowing you to programmatically set the value of a boxed control or retrieve its unboxed value.
What Values Can You Box?
In order for a FormControlState’s value to be Boxed, it must be serializable. Attempting to assign a non-serializable value will throw the following error:
Error: A form control value must be serializable (i.e. value === JSON.parse(JSON.stringify(value)), got {“_boxed”, “value”:”someValue”}
Serialization is the process of converting data into bytes, often for the purpose of transferring it between environments. JSON stringifying is an example of client-side serialization, converting a complex type (Object) into a more universal type (String).
In practice, this means all values must be identical before serialization and after serialization. This requirement rules out anything which must be invoked to generate data, like functions and classes. In fact, the list of types that can be serialized is pretty short.
Here are all the serializable types in JavaScript:
-
Booleans
-
Integers
-
Strings
-
Arrays
-
Plain Objects (no functions or other non-serializable key values allowed)
If your FormControlState’s value does not meet these criteria, you’ll need to refactor before adding Value Boxing!
Working with Dates
Another common Boxing pitfall is the need to capture JavaScript Dates in a form. You might be tempted to Box a Date and added it directly to the form, like this:
export interface UserForm {
name: string;
tags: Boxed<string[]>;
dateOfBirth: Boxed<Date>;
}
However, Date is technically a class and classes are not serializable! Boxing a Date value will throw the same runtime error as any other non-serializable value.
Error: A form control value must be serializable (i.e. value === JSON.parse(JSON.stringify(value)), got {“_boxed”, “value”:”someDate”}
To solve this, the author of ngrx-forms suggests ISO strings as a serializable alternative to JavaScript Dates.
export interface UserForm {
name: string;
tags: Boxed<string[]>;
dateOfBirth: string; // ISO format date string
}
The library even includes a value converter that will automatically convert Dates to ISO Strings in your templates :
<input
type="date"
[ngrxFormControlState]="demoForm.controls.birthDate"
[ngrxValueConverter]="NgrxValueConverters.dateToISOString"
>
Wrap-up
The ngrx-forms library is a great tool for managing complex forms. Value Boxing can appear tricky, but it is easy to work with once you understand the rules! Ensure your non-primitive values are serializable, and adding Value Boxing will be an achievable refactor.
Still have questions about ngrx-forms? We’d love to help!
Previous Post