ToDo & Changelog
ToDo List#
- Release
4.0.0: bumpversioninpackage.json(still3.0.0) before tagging, the publish workflow requires the tag to match it - Release
4.0.0: replaceREPLACED_AT_THE_4.0.0_RELEASEindocs/start/loading.mdwith the hash of the publisheddist/dynamic-forms.umd.js(command in that page); at every later release, update the version and hash of that snippet
Changelog#
Dynamic-forms follows the semver standard.
4.0.0 (unreleased)#
- Breaking:
form.notify()rejects when a select's remote call fails, instead of logging the error and resolving.form.ready()rejects too when an initialisation remote call fails (#27). Changes triggered by the user still log the error to the console form.ready()rejects when initialisation fails (e.g.beforeInitthrows), instead of never settling (#28)- Breaking: every Field Hook receives the
NodeListof the elements with the field name, even when there is only one. Hooks on single elements readhtmlElement[0].valueinstead ofhtmlElement.value. Before, checkbox and radioio.get,io.setandbehavior.clearand selectbehavior.updateStatus,postProcessDataandsaveDataalways received theNodeList, while the other hooks received the element itself when only one had the field name - Breaking (TypeScript): public types reworked so configurations need no casts. The package now exports
FormConfiguration,FormHooks,UpdateRule,InitialisationRule,FieldConfiguration,FieldIoHooks,FieldFetchConfiguration,FieldHooks,SelectConfiguration,CheckboxConfiguration,UpdateDataand theFormtypeFormConfigurationrequires onlyid, andbehavioraccepts any subset of the Form HooksFieldConfigurationhas theselectandcheckboxkeys; the formerSelectConfiguration/CheckboxConfigurationinterfaces extending it are gone, and the names now belong to the formerSelectSelectConfiguration/CheckboxCheckboxConfiguration- Renamed
FormBehaviortoFormHooks,FieldBehaviorConfigurationtoFieldHooks,FieldIoConfigurationtoFieldIoHooks - The
datareceived by Field Hooks,makeUrl,makeBodyandexternalDataisUpdateData(Record<string, any>) instead ofobject, sodata.countryneeds no annotation - Form Hooks and
externalDatareceive astringSubject name, nevernull - Field Hooks receive
NodeListOf<FieldElement>(FieldElementisHTMLInputElement | HTMLSelectElement | HTMLTextAreaElement) instead ofHTMLElement | NodeList, sohtmlElement[0].valueneeds no narrowing or cast.FieldConfiguration<E, V>states the element and value types of a field (FieldConfiguration<HTMLSelectElement>,FieldConfiguration<HTMLInputElement, string[]>); the value staysanyby default select.postProcessDatareceives the response asanyand returnsSelectOption[]({ value: string | number, text: string }), whichselect.saveDatareceivesInitialisationRule.valueisunknowninstead ofany- The
behavior.afterUpdateField Hook returnsvoidinstead ofboolean: its result was never used - Exports
FieldElementandSelectOptiontoo
- Breaking:
fetch.fullFetchConfigis a function of the Update Data,(data) => RequestInit, instead of a static object, as the documentation already described (#31). Wrap an existing object asfullFetchConfig: () => ({ ... }) makeForm()no longer modifies the given configuration: defaults and discovered fields go into an internal copy. Form Hooks set toundefinedfall back to the default instead of throwing- Breaking:
makeForm()throws when the Update Rules form a cycle, including a field listing itself in its ownupdate, which used to be ignored (#38). A cycle used to clear the field the user had just changed - Breaking: a custom
behavior.beforeUpdateon a select no longer switches offselect.clearOnParentVoid: with an empty Subject's value the select is cleared and its update stops before the hook is called (#40). SetclearOnParentVoid: falseto handle an empty Subject's value in the hook - Breaking:
makeForm()throws when the configuration mentions a name which is not a field of the form: in an Update Rule (itsname,updateoradditionalData) or in an Initialisation Rule. These used to fail at the first update withUnknown element, or be silently ignored (#45). The error lists every unknown name, and every missingname; a Field Configuration for a missing field joins the list instead of throwingElement ... not found - Breaking:
makeForm()throws when a Form for that id already exists, instead of leaving the previous one listening to the fields with its old configuration (#47). The newform.destroy()removes the Form's listeners, unregisters it fromgetForm()and aborts its remote calls; a pendingnotify(),manualUpdate()orready()rejects withForm "id" was destroyed, as do later calls. Fields added aftermakeForm()are picked up by destroying the Form and creating it again - The
behavior.updateStatusField Hook receives a 4th parameter, theAbortSignalaborted when the Form is destroyed. Asignalreturned byfetch.fullFetchConfigis combined with it - Breaking: a failed remote call rejects
notify()andready()with aRemoteCallError, anErrorexported by the package with the url and status in its message and theResponseinerror.response, instead of rejecting with theResponseitself (#49). Readerror.response.statusinstead oferror.status - New
select.loadOptionsField Hook: it replaces only the remote call of a select, receiving the parameters ofupdateStatus, and its data goes throughpostProcessDataandsaveDataas usual (#50) - Breaking: the package is
"type": "module"and its files are renamed:dist/dynamic-forms.js(ES module),dist/dynamic-forms.umd.cjs(UMD forrequire) anddist/dynamic-forms.umd.js(the same UMD for<script>tags and CDNs).exportsgives each ofimportandrequireits own types, so the types resolve with every TypeScriptmoduleResolution(before, only with the deprecatednode), and exports./package.jsonand./dist/*. Thebrowserfield is removed;unpkgandjsdelivrkeep the bare CDN URL serving the UMD build.publintand@arethetypeswrong/clicheck the package in CI, andnpm run test:packagetests the packed package withimportandrequirein Node, as a<script>tag in jsdom and bundled by Vite (#52) - The package is built with tsdown instead of Vite (#54):
- The sourcemaps map to the right lines of the TypeScript source (they were 6 lines off), and the new declaration maps make "Go to definition" open the TypeScript source in
src - Bundlers tree-shake the ES module build: importing only
RemoteCallErrorno longer bundles the whole library. The ES module build isn't minified any more (consumers' bundlers minify it); the UMD builds still are - Breaking: supported browsers are stated: Chrome and Edge 111+, Firefox 114+, Safari 16.4+, which is also the build target (the README said "works on all browsers"). A
signalreturned byfetch.fullFetchConfigworks where the browser lacksAbortSignal.any
- The sourcemaps map to the right lines of the TypeScript source (they were 6 lines off), and the new declaration maps make "Go to definition" open the TypeScript source in
- Third-party widgets (select2, Choices.js, Tom Select, bootstrap-select, flatpickr, form-associated custom elements) can be integrated through Field Hooks (#57), with a new Third-party widgets page and tested recipes:
- New
io.listen(htmlElement, onChange, standard)Field Hook: it subscribes to the field's changes and returns the function which unsubscribes, called byform.destroy(). It hears widgets that announce changes only through jQuery events or callbacks, whichio.eventnever could - Every Field Hook which replaces a Standard Behaviour receives it as its last parameter,
standard:io.listen,io.get,io.set,behavior.clear,behavior.updateStatus,select.loadOptions,select.postProcessDataandselect.saveData. Call it to add to the Standard Behaviour instead of rewriting it - Breaking: while the library writes to a field (
io.set,behavior.clear,select.saveData), it ignores the changes that field fires: a hook or widget firingchangewhen written no longer starts a second, duplicate update of the field's Observers FieldConfiguration<E>accepts anyHTMLElement, so a custom element's type can be stated
- New
- A newer Update of a field replaces a pending one (#60): a stale remote response no longer overwrites the options of a later change (e.g. Italian cities after choosing France), nor refills a select emptied meanwhile by
clearOnParentVoidor a Cascade Clear. The replaced Update's remote call is aborted and its data discarded (nopostProcessData,saveDataorafterUpdate); anotify(),manualUpdate()orready()waiting on it settles with the newest Update of that field. ThesignalofupdateStatusandloadOptionsis also aborted when a newer Update replaces theirs - A select's standard
saveDataempties the select with the library's own clear, no longer through thebehavior.clearField Hook (#67): a hook resetting only the value no longer leaves the old options behind (['', '1', '1']after two loads), and the Choices.js and bootstrap-select recipes refresh the widget once per load instead of twice. A Cascade Clear andclearOnParentVoidstill run the hook - A select with nowhere to load its options from (no
fetch.makeUrl, noselect.loadOptions) keeps the options of its html: listing such a static select in an Initialisation Rule no longer makesready()reject withFunction fetch.makeUrl not specified(#62) - Breaking:
makeForm()throws for an Observer select with neitherfetch.makeUrl,select.loadOptionsnorbehavior.updateStatus, which used to fail only at the first change of its Subject (#62) - Breaking: the Initialisation comes first (#64): until
ready()settles, the fields' changes are ignored andnotify()andmanualUpdate()reject withForm "id" is initialising: await ready() first, also for a form without Initialisation Rules. A change made during the Initialisation used to leave the form inconsistent (the user's value overwritten by the initial one, its Observers loaded for the user's). Callnotify()afterawait form.ready()(or in.then) - Breaking:
notify()andmanualUpdate()throw at once, before returning their promise, for a name which is not a field of the form:Form "id" has no field "name".notify()used to resolve and call the Form Hooks with that name,manualUpdate()to reject withUnknown element(#69) - Breaking:
makeForm()andgetForm()return aForm, which has only its documented methods:ready,notify,manualUpdate,destroy,setEnabled,isEnabledandgetId, described in the new Form page (#72). The form's internals (fieldsMap,config,htmlElement,behavior,debug,enabled,initPromise...) are no longer reachable, neither in the types nor at runtime: setdebugin the Form Configuration, and useisEnabled()instead ofenabledandready()instead ofinitPromise - Breaking: a disabled form rejects
notify()andmanualUpdate()withForm "id" is disabled: call setEnabled(true) first(#72).notify()used to resolve without updating anything, andmanualUpdate()to update the field, with its remote call and Field Hooks. The user's changes are still ignored without errors, the updates already running when the form is disabled still complete, and disabling the form doesn't stop its Initialisation - The loading docs cover every way to load the library: a bundler (
importorrequire), a<script>tag from a CDN (with theDynamicFormsglobal, and with Subresource Integrity), ES modules from esm.sh, an import map without a bundler, and self-hosting. CDN snippets use@4instead of@latest, and the snippets that didn't work are fixed - Initialisation no longer clears the initial value of a field reached by the Cascade Clear of another initialised field (#37)
- An update clears the Observers' Observers before updating the Observers, so the result no longer depends on the order of the names in
update form.notify()andform.ready()reject when clearing the Observers' Observers fails (e.g. abehavior.clearField Hook throws), instead of leaving an unhandled rejection- Clearing a checkbox unchecks it instead of checking it (#25)
- A select filled by a remote call gets an empty option when neither the html nor the data have one (#26). An option with value
0is not taken for the empty one
3.0.0#
- Webpack has been replaced with Vite
- Library import does not expose the
dynamicFormsmodule anymore butmakeformandgetFormfunctions directly
2.0.0#
- Library has been rewritten in typescript
FieldConfiguration.dropdownrenamed toFieldConfiguration.select- Better documentation
1.3.0#
- Added automatic field discovery: you don't need to specify fields with standard behavior anymore
1.2.0#
- Supported fields initialisation
- Added
form.ready()method to synchronise post-initialisation operations
1.0.1#
1.0.0#
- First release