Hateoas form helpers
Installation
yarn add @alza/hateoas --exactField map utility (auto-mapping)
@alza/hateoas works with a fieldMap object. It is used to map server field names to your local (client/UI) field names.
In many cases you want a 1:1 mapping where local field names are the same as server field names. For that, use:
createFieldMapFromForm(form, options)(named export)Hateoas.createFieldMapFromForm(form, options)(same function on theHateoashelper)
It builds a HateoasFieldMap from HateoasFormV2.value[] and infers "string"/"integer" field types.
Unsupported itemTypes are skipped by default.
Terminology (current names):
- Raw API form type:
HateoasFormV2 - Field map type:
HateoasFieldMap
Example: identity mapping
import { Hateoas } from "@alza/hateoas";
const fieldMap = Hateoas.createFieldMapFromForm(form);
const fields = Hateoas.getUiFields(form, fieldMap);
// fields.password.validators?.(t) ...Example: rename fields (server -> local)
import { createFieldMapFromForm } from "@alza/hateoas";
const fieldMap = createFieldMapFromForm(form, {
rename: {
password: "auth.password",
},
});
// Use the local name "auth.password" in your UI / form library.Example: skip or throw on unsupported itemTypes
import { createFieldMapFromForm } from "@alza/hateoas";
// Default: unsupported fields are skipped
const fieldMap1 = createFieldMapFromForm(form);
// Throw when encountering unsupported fields
const fieldMap2 = createFieldMapFromForm(form, { unsupported: "throw" });Generic parameters
- required
- by
field.isRequired(V3 has to be true, V2 cannot be false - implicit true) - also marks labelAsRequired (*)
- by
- disabled
- by
field.isEnabled(V3 has to be true, V2 cannot be false - implicit true)
- by
- label
- by
field.label
- by
- hidden
- by
field.isHidden
- by
- placeholder
- by
field.placeholder
- by
- description
- by
field.description
- by
- validationError
- by
field.validationError - error returned from API
- by
String
Standard text input (field.itemType === 'string' or field.type === 'string')
- pattern
- by
field.pattern - validates string by provided RegExp pattern
- by
- email
- by
field.semanticItemType === 'email' - uses
field.patternif present, otherwise falls back to local email regex
- by
- phone
- by
field.semanticItemType === 'phone' - uses
field.patternif present, otherwise falls back to local phone regex
- by
- IBAN
- by
field.semanticItemType === 'iban' - uses
field.patternif present, otherwise falls back to local IBAN validation
- by
- minLength
- by
field.minLengthorfield.min - validates string minimal length (characters)
- by
- maxLength
- by
field.maxLengthorfield.max - validates string maximal length (characters)
- by
Behaviour can also be modified by field.semanticItemType with values of password, textArea.
Integer
Text input limited to numbers (field.itemType === 'string' or field.itemType ==='decimal')
- max
- by
field.max - validates maximum value
- by
- min
- by
field.min - validates minimum value
- by
Boolean
Checkbox input (field.itemType === 'boolean')
Set
Select input (field.itemType === 'set' or field.type === 'set'). Can be multichoice.
- minSize
- by
field.minSize - validates minimum selected size (e.g. you have to pick atleast 3 options)
- by
- maxSize
- by
field.maxSize - validates maximum selected size (e.g. you can pick up to 3 options)
- by
Example: render set as <select>
SetField is the UI model produced by Hateoas.getUiFields(form, fieldMap).
field.optionscontains available options (withdisabledalready mapped fromisEnabled)field.multipletells you whether to render multi-selectfield.valueis UI-friendly:- single-select:
string | "" - multi-select:
string[]
- single-select:
import { Hateoas } from "@alza/hateoas";
import type { HateoasFieldMap, HateoasFormV2, SetField } from "@alza/hateoas";
const fieldMap = {
day: ["day", "set"],
} as const satisfies HateoasFieldMap;
export function Example({ form }: { form: HateoasFormV2 }) {
const fields = Hateoas.getUiFields(form, fieldMap);
const day = fields.day as SetField;
// Render:
// <select multiple={day.multiple} defaultValue={day.value}>
// {day.options.map(o => (
// <option key={o.value} value={o.value} disabled={o.disabled}>
// {o.label}
// </option>
// ))}
// </select>
}Example: submit set values
Server expects set values as an array.
When mapping local UI values back to server values you can pass:
- single-select value as
string→ it is coerced to[string] - multi-select value as
string[]→ stays as-is
import { AlzaHateoasForm, Hateoas } from "@alza/hateoas";
import type { HateoasFieldMap, HateoasFormV2 } from "@alza/hateoas";
const fieldMap = {
day: ["day", "set"],
} as const satisfies HateoasFieldMap;
const hateoasFields = Hateoas.getUiFields(form, fieldMap);
// UI values (single-select)
const localValues = { day: "nextday" };
const serverValues = Hateoas.mapLocalValuesToHateoas(
localValues,
fieldMap,
hateoasFields,
);
const hf = new AlzaHateoasForm(form as HateoasFormV2);
hf.changeFormValues(serverValues);
const submit = hf.getFormSubmitData();
// submit.data.day === ["nextday"]Range
Slider number input (field.itemType === 'range or field.type === 'range' or field.itemType === 'integer). Pick number between min-max values. // opravdu integer? legacy bug?
DateTime
Date or DateTime input (field.itemType === 'dateTime' or field.semanticItemType === 'date' or field.semanticItemType === 'dateTime'). field.semanticItemType specifies if its date or dateTime.
- maxDate
- by
field.max
- by
- minDate
- by
field.min
- by
BlobAttachment
Attachment upload input (field.itemType === 'blobAttachment' or field.type === 'blobAttachment)
- uploadUrl
- by
field.uploadUrl - url where to upload the files (returns attachment ids that are then used as actual values)
- by
- minSize
- by
field.min - minimum attachment count
- by
- maxSize
- by
field.maxCount - maximum attachment count
- by
- allowedContentTypes
- by
field.allowedContentTypes - validates attachment content types (e.g. jpg, png, pdf…)
- by
- maxAttachmentSize
- by
field.maxAttachmentSize - validates each attachment (file) size in Bytes
- by
SubmitButton
Submit button defined from API (field.itemType === 'submitButton or field.type === 'submitButton)
ObjectArray
Custom object array field (field.itemType === 'objectArray) - usually requires custom implementation. Used to just pass predefined complex data structures generated by API.