GolemUI v2.1.1: validation and repeater fixes, and a deprecation schedule
GolemUI v2.1.1 is released. It is a patch release after v2.1.0, with no new widgets. Most of it is bug fixes, but a few behaviours change, so read Upgrading before you update.
Validation matches what the user sees
Section titled “Validation matches what the user sees”A hidden input no longer makes the form invalid. In 2.1.0 its error still counted, so a submit button disabled with $formIsInvalid stayed disabled because of a field the user could not see. Now the error leaves $errors, and $formIsInvalid and isFormValid update at once. Check the box below, click in the VAT number, then click outside it. The error shows and Pay is disabled. Now uncheck the box:
The other validation fixes:
- A cleared optional string is valid. With
validator: { minLength: 3 }, a user who types a letter and deletes it gets no error. A string validator withoutrequiredtreats''likeundefined. uniqueItemsis checked. The array validator compares the items by value, with a default message and amessages.uniqueItemsoverride.- Injected issues update
isFormValidat once, so a server error on an input marks the form invalid with no other validation. - A language change translates the errors on screen. The form validates again each input that has an error, so its message and
$errorsuse the new language. - A submit validates once. One click gives one
VALIDATE_ALLaction, so each validator runs once.
See Validators.
Limits are validator rules
Section titled “Limits are validator rules”A length or count limit now goes in the validator. The field lets the user type, paste or add past the limit, shows that the value is over it, and marks the form invalid. No widget blocks input.
- The
maxLengthprop of the textarea and the markdown editor has no effect, and it is deprecated. Usevalidator.maxLength, which controls the counter and the error. - The
limitprop of the repeater disables the Add button with no message, and it is deprecated. Usevalidator.maxItems. The Add button stays enabled, so the user can add a row and then remove another one.
This repeater takes at most two contacts. Add a third one, and the error shows at once. Remove one, and the error goes away:
The JSON Schema converter follows the same rule: maxItems goes in the validator, and the repeater gets no limit.
Tags skip repeats by default
Section titled “Tags skip repeats by default”gui-tags and the Tags widget now skip a tag that the value already holds. They show no error, because the value does not change. Type the same tag twice:
To let the user add the same tag more than once, set allowDuplicates: true in a form, or the allow-duplicates attribute on the element.
The input checks only the tags that the user adds. A value from a server can still hold ['vip', 'vip']. To reject it, use validator: { uniqueItems: true }.
Pasting also works better. A paste goes in at the caret and replaces the selected text, and a paste that adds no tag keeps the text in the input. Escape in an empty input can now close a dialog that holds the field.
Repeater rows keep their state
Section titled “Repeater rows keep their state”A row has state besides its data: its touched state, its errors, the issues your app injects and the prop overrides your app sets by path. In 2.1.0 this state stayed at its position, so after a removal the next row received the state of the removed one. Now the Remove button reports the row it removed, and the state moves with the rows, also in nested repeaters.
Take the rows [Alice, Bob], and an app that makes the name of Bob read-only:
event.callback({ type: 'OVERRIDE_WIDGET_PROP', payload: { path: 'people.1.name', prop: 'readonly', value: true },});The user removes Alice. Bob is now row 0, and his name is still read-only. A row that the user adds later is not read-only.
Two cases stay at their positions: setData with a new array, because the form can’t know which new row was which old row, and an error that a widget reports itself, such as a rejected date. See State of a row.
A custom repeater reports the removal with onRowRemoved(rows, index) in React and Vue, or rowRemoved(rows, index) on the adapter in Angular and Lit. See Input widgets.
Broken forms report through formHealth
Section titled “Broken forms report through formHealth”An invalid form definition now gives a health error instead of an exception, so your app can show its own message.
- Code 22 now also covers no definition at all (
null, for example when a backend returns no form), a repeater without atemplate, and a widget function that throws. The form calls a row function once without a row when it builds the form, so read$itemwith?.. - Code 60 is new. A validator failed to run: a JSON form names a custom rule that is not registered, or a custom validator throws or runs an async check. The message has the path of the input, and the form does not submit. The error stays until you pass a new config.
See the list of codes and When a validator fails to run.
Form data keeps its values
Section titled “Form data keeps its values”- Values stay as they are. A
Date, aMapor a class instance stays the same object informEventandformSubmit. - A
nullparent acts like a missing one. Withdata: { order: null }, a repeater atorder.linesrenders no rows, and the form health isok. - Data paths read own keys only. An input at the path
toStringworks. A__proto__key in the data is plain data, and it never changes a prototype. - Hidden data keeps what a visible input uses. A dropdown at
countryfor some customers and a text input atcountryfor the others can share one value. - An i18n param keeps its value.
params: { count: 3 }hands the translator a number, so plural rules work. An expression param gives its result, alsonullorundefined, and never the expression text.
GolemUI Components
Section titled “GolemUI Components”gui-currencyuses the decimals of the currency: 2 forUSD, 0 forJPY, 3 forKWD. In 2.1.0 it showed 2 unless you set otherwise.gui-number,gui-currencyandgui-sliderread a numeric string on thevalueproperty as its number, andgui-otptakes a number.gui-select,gui-radiogroupandgui-list: the attributevalue="2"selects the number option2.gui-selectandgui-radiogroup:label-fieldandvalue-fieldwin over the objects’ ownlabelandvaluekeys.gui-select: a value that matches no option is not submitted, and it blocks the submit.gui-range-slider: a value with its ends reversed reads low end first, and a value with only one end is no value.gui-radiogroupfiresgui-bluronly when the focus leaves the group.gui-numberfires no events for a step stopped at a bound.gui-file-uploadwithrequiredgives its upload button “Required” as its description, for screen readers.gui-markdown: the toolbar no longer treats an_inside a word, as insnake_case, as italic.
Other changes
Section titled “Other changes”- An unknown item renderer uses the default renderer. Several apps can share one JSON form, and one app may not register every renderer the form names. In dev mode the console warns once.
setDataandsetMetabefore the form starts are kept in Lit and Angular. The last call replaces thedataormetaof the config.- React cancels a pending debounced call on unmount, so a dropdown sends no filter event after its widget is removed. Its dropdown also shows a value that no item matches, as the other frameworks do.
- Vue keeps the exact values of the other rows when a repeater row is removed.
Date,undefinedandNaNvalues used to change. - Angular lets Enter in a dropdown field submit the form, and binds on-demand widgets before their elements first render.
- Lit does not throw when a dropdown filter clears before its items load.
- A removed prop override also removes the prop, when only the override had set it.
Deprecations
Section titled “Deprecations”DEPRECATIONS.md lists every deprecated API, what to use instead and the release that removes it. All of these still work, and v3.0.0 removes them:
| API | Use instead |
|---|---|
maxLength prop of the textarea and the markdown editor | validator.maxLength |
limit prop of the repeater | validator.maxItems |
locale-id attribute of gui-number | Nothing. It has no effect |
The flex layout: type: 'flex', gui.layouts.flex, horizontalFlex and verticalFlex | grid, horizontalGrid, verticalGrid |
autoFit, columnGap, rowGap and align props of the grid layout | columns, gap, gap, justify |
Upgrading
Section titled “Upgrading”A few changes need your attention when you upgrade:
- Upgrade every
@golemui/*package to 2.1.1 at once. - Tags: if your users must be able to add the same tag twice, set
allowDuplicates: true. - Currency:
JPYnow shows no decimals andKWDshows 3. To keep a fixed number of decimals, setminimumFractionDigitsandmaximumFractionDigits. - Optional strings: a cleared optional field no longer fails
minLength,patternorformat. If the field must not be empty, setrequired: true. uniqueItems: a validator that sets it now rejects repeated items.- JSON Schema: a form you generate again from a schema with
maxItemshas an enabled Add button past the cap, and an error. A form you generated before keeps itslimit, which works until v3.0.0. - Select: a value that matches no option now blocks a native form submit.
- Translators: a param can now be a number,
nullorundefined. A translator that expects only strings must handle them. - Error handling: if you catch exceptions from a broken form definition, listen to
formHealthinstead. - Custom validators: register every custom rule that a JSON form names. A rule that is missing, that throws or that runs an async check now gives code 60.
- Custom submit widgets:
onClickno longer validates. A widget with no form element must callonSubmitafteronClick. - Custom repeaters: report a removed row with
onRowRemovedorrowRemoved. Without it, the state of the rows stays at its position.
The full list of changes is in the changelog.