All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Documentation now located at https://tanepiper.github.io/svelte-formula/
- Update example to use
isFormValid
correctly (thanks @macmillen) - Improved types of form states (thanks @macmillen)
- Default value added to Beaker default values (thanks @fabien)
- Types for Formula now provide stronger typing
- Formula now won't apply
aria-role
on items that already have a role set - Better handling of value and validation updates, store update is called less times
-
New
data-formula-bind
attribute for elements - allows the event bindings to be customised. If used the default bindings are not made (so they must be also passed) - more than one event type can be bound to by using the pipe (|
) separator.<!-- Use other browser event types to bind to --> <input type='text' name='myValue' data-formula-bind='mouseover' /> <!-- Bind to more than one event type, such as including the original event --> <input type='text' name='myValue' data-formula-bind='mouseover|keyup' /> <!-- You can also emit your own custom events via elements inside custom components --> <input type='number' name='myValue' data-formula-bind='customEvent' bind:this='{el}' /> <button on:click|preventDefault='{()' => el.dispatchEvent(new Event('customEvent'))}>Click Me</button>
-
options.preChanges
method that is called when a change has been detected, but before any values are read or stores updated -
options.postChanges
method that is called after all changes have been made to the stores, with the most recent form values (this is functionally the same asformValues.subscribe(values => {}))
)
options.formValidators
now correctly updateisFormValid
after other validity checks have been carried out instead of before<input type="hidden">
fields now supported, both single and multi-value. Formula can only set the value of this field via default values and cannot set the value ( but for example it can be changed in something likeoptions.preChanges
) - hidden fields have no events, so these are a special case where the values are read with each update.- Input types
number
,date
,time
andweek
now bind to bothkeyup
andchange
handlers to handle both valid input cases on these elements
-
Existing attribute
data-beaker-key
has been renamed todata-formula-name
- this allows a custom name to be used instead of the<input name="...">
value -
Existing attributes
data-beaker-form
anddata-beaker-row
now correctlydata-formula-form
anddata-formula-row
-
All event handlers wrapped in a
setTimeout(()=> {}, 0)
to defer an update until after DOM tick, this allows elements to ensure value changes have completed before reading
- Form validity check is not fired when
novalidate
it set on form and there is a submission
- Removed Beaker changes for
MutationObserver
- reverted code back to using a check for all rows and re-create forms each time as tracking changes becomes too complex. New code in place that handles subscription correctly to child stores for each form instance within the group and better extraction of values. This also fixes infinite loop bug caused by new code.
- Beaker stores now correctly showing store values in a group without requiring
bind:value
- Fix issue where updated mutation code expected default values
- Group
reset
method - instead to reset a group of form instances callgroup.init
with the initial data that was used to create the group.
- Beaker now accepts
defaultValues
as an array (group.init
will always override this) - Internal refactoring to improve group handling
- Removed global state stores for initial values, now only generated internally for reset methods
- Touched and Dirty and Invalid fields now have attributes set
formValidity
in Beaker stores is now an array- Group stores no longer emit twice on changes
- Groups correctly now use
MutationRecord
added and removed nodes to be the reference to form instances when adding and removing data
- Group elements now have
data-in-group
attribute set which allows for better filtering of groups from form data
- When a single value field is emptied after having values, correctly update the form state instead of leaving previous value
- Removed a stay console introduced in
0.8.5
, and fixed typo in0.8.4
- Example in readme fixed with link to demo on Svelte Repl
-
update
method on groups, takes aFormulaOptions
object and applies it to all forms in the group -
Formula and Beaker now set ARIA properties for some form and field states
- Form, Group and Row are applied to containers
- Sets radio elements and attempts to discover the
radiogroup
root element - Set properties like required and checked for value types and attributes
- Performance improvements and some internal refactoring
- Documentation improvements
- Added
clear
method tobeaker
- this will empty the store of any group data - Group forms now available on
forms
property
- Fixed
reset
method of group, now correctly calls reset on the forms - Fixed lifecycle issues with group
-
New
beaker
API that provides functionality for working with groups of data as multi-line forms. See documentation for more details on useimport { beaker } from 'svelte-formula' const contacts = beaker(); export let loadedContacts = []; const contactValues = contacts.formValues; contacts.init(loadedContacts) // Set initial values // Easily add items to the group function addRow(item) { contacts.add({...item}); } //... or remove them function removeRow(index) { contacts.delete(index); } .... <div use:contacts.group> {#each $contactValues as contact, i} <!-- Template for each row --> {/each} </div>
- Some internal API fixes and refactoring
- Fixed single checkbox not setting correctly
- Correctly rebind touch and dirty handlers after resetting
- Remove stray
console.log
-
defaultValues
option that allows default values to be set on fields - supports single and multi-value properties - these values are only applied if there is no value already bound to the field.<script> import {formula} from 'svelte-formula'; const { form } = formula({ defaultValues: { textField: 'Initial Value', numberField: 42, checkBox: true, multiValue: ['option1', 'option3'] } }) </script>
-
Added
isFormReady
store - Formula stores are created immediately when using theformula
method, previously they were always empty objects filled with keys from parsing the form. This meant early binding would cause an error and forced the use of?.
operators in the templates. This store can now be used as{#if $isFormReady}
, reducing the need for the number of conditionals in templates -
initialValues
store that contains the values at form initialisation, this is generated from merging any initial element values merged with any potential default values -
formReset
function that when called will reset the form to the pristine state at setup time
-
Formula now returns an
updateForm
method that takes an updatedFormulaOptions
object - this function destroys all existing handlers on the forms and rebinds them with the new options - this allows for dynamically adding and removing of validations and messages<script> import { formula } from 'svelte-formula'; const formValidators = { passwordsMatch: (values) => values.password === values.passwordMatch ? null : 'Your passwords do not match', }; const { form, updateForm } = formula({ formValidators, }); function addDomainValidation() { const options = { formValidators, validators: { username: { inDomain: (value) => value.includes('@svete.codes') ? null : 'You in the svelte codes?', }, }, }; updateForm(options); } function removeDomainValidation() { updateForm({ formValidators }); } </script>
-
Formula now returns a
destoryForm
method that allows the form to be destroyed early - when using theuse
directive this is called automatically on component destroy, but this allows for early form unbinding. -
New
enrich
option added toFormulaOptions
and a newenrichment
store. Enrichment allow methods to be added to fields to return computed values (e.g. password score) that can be used to drive other parts of the UI<script> import { formula } from 'svelte-formula'; import {passwordScore} from '../libs/password' const { form, enrichment } = formula({ enrich: { password: { passwordStrength: (value) => passwordScore(value) } } }) </script> <label for='password'>Password</label> <input type='password' id='password' name='password' /> <meter value={$enrichment?.password?.passwordStrength || 0} min='0' max='100' low='33' high='66' optimum='80' />
-
New
globalStore
Map object - if Formula is used with an element with anid
property, the instances stores will be added to the global store and can be accessed viaglobalStore.get(name)
- allowing sharing of data across multiple forms.
- Correct URL for docs site: https://tanepiper.github.io/svelte-formula
-
New Documentation Site (still WIP)
-
Added
messages
option to FormulaOptions, this is a key/valueObject
for setting custom validation messages per error:<script> import { formula } from 'svelte-formula' const { form } = formula({ messages: { valueMissing: 'Debes ingresar un nombre de usuario' } }) </script>
-
Add support for validation messages via HTML
data-*
attributes - these will always take presidency over items in themessages
object if both exist. The format of the name should be a hyphen before any characters that are uppercase (e.gvalueMissing
becomesdata-value-missing
)<input type="text" name="postcode" required data-value-missing="Bitte geben Sie Ihre Postleitzahl ein" />
- More internal performance improvements
- Large Internal refactoring of the code
- Support for
range
,color
,date
,time
,week
inputs - Support for
file
input
number
input type returns a number value, orundefined
if not set
formValid
nowisFormValid
-
Support for custom field-level validators via the
validators
property of theformula
options. Validators are provided as an object - the key is thename
of the fields, and the value is another object containing the validators. Each validator is has a key that is the name of the validation and a function that returns a string if the validation fails ornull
if it passes. The string message is used to display the message, the key is added to theerrors
object invalidity
<script> import { formula } from 'svelte-formula' import { calculateStrong } from '../libs/password' const { form, formValues, submitValues, validity, touched, dirty, formValid } = formula({ validators: { password: { isStrong: (value: string) => calculateStrong(value) ? null : 'Your password is too weak' }, 'invoice-ids': { invoicePrefix: (values: string[]) => values.every(value => value.startsWith('INV-')) ? null : 'Your invoice IDs must all begin with INV-' } } }); </script> ... <input type='password' name='password' required minlength='8' class:error={$touched?.password && $validity?.username?.invalid}/> <div hidden={$validity?.password?.valid}>{$validity?.password?.message}</div> <div hidden={$validity?.password?.errors?.isStrong}>You have a strong password!</div> ... <input type='text' id='invoice-1' name='invoice-ids' /> <input type='text' id='invoice-2' name='invoice-ids' /> <input type='text' id='invoice-3' name='invoice-ids' /> <div hidden={$validity?.['invoice-ids']?.valid}>{$validity?.['invoice-ids']?.message}</div>
-
Support for custom form-level validators via the
formValidators
property of theformula
options. Validators are provided as an object - Each validator has a key that is the name of the validation, and a function that returns a string if the validation fails ornull
if it passes. The error message are available via theformValidity
store.<script> import { formula } from 'svelte-formula' const { form, submitValues, submitValidity, formValid } = formula({ formValidators: { passwordsMatch: (values) => values.password === values.passwordMatch ? null : 'Your passwords must match' } }) </script> <input type='password' name='password' required minlength='8'> <input type='password' name='passwordMatch' required minlength='8'> <div hidden='{!$submitValidity?.passwordsMatch}'>{$submitValidity?.passwordsMatch}</div>
- Correctly pass options to form creation
-
First config option for
formula
- allowing the locale for sorting to be overridden if you don't want to use the users own locale -
Support for multiple input types that are not checkboxes (e.g
text
,number
, etc) - when using this each input with the same name REQUIRES andid
property. Returns an array sorted on theid
property alphabetically in the users' locale if detected (and always falls back toen
if not), if this needs to be overridden it can be passed to the formula constructor (e.gformula({ locale: 'de' });
)
- Add support for
<input type="radio">
- allows multiple radios of the same name and returns a string of the currently selected value - Add support for
<select>
fields for both single and multi-value select fields - if a single value returns a string, if set tomultiple
returns an array of strings - Added support for
<input type="checkbox">
- if a single value returns a boolean value for the checked state, if there are multiple checkboxes of the same name it returns an array of the selected values - Added
dirty
store that fires when a value is changed, and the selected field is blurred away from
- Internal refactor of code to clean up and make it easier to add features
- Added support for
touched
elements
values
becomesformValues
submit
becomessubmitValues
- Initial release