This is a client-side form library for Svelte. It's super early in developement so expect bugs, changes, and dragons.
Demo app: https://pluma-svelte-forms.netlify.app/
To install:
npm i pluma-svelte-forms
<script>
import {controller} from 'pluma-svelte-forms';
import {writable} from 'svelte/store';
const settings = {
async onSubmit (values) {
console.log(values)
}
}
</script>
<form use:controller={settings}>
<input type="email" name="email" required>
<input type="password" name="password" required>
<button type="submit">Submit</button>
</form>
Validation happens on every event (input, blur, etc). The controllerState is always updated with the current state of the complete form. Check the demo app to get an idea of what this means.
Once a field is considered to be valid or invalid, its input will be marked with a CSS class. By default these are valid and invalid but you can configure custom CSS class names (eg: is-valid and is-invalid if you're using Bootstrap).
You can completely deactivate this behavior by using null for the validClass and/or invalidClass settings.
The valid CSS class will not be applied to checkboxes, select, and radio inputs. You can change this behavior with the addValidClassToAllInputs setting.
Other than the CSS classes mentioned before, this library will not display any errors for you.
You can pass a Svelte writable on the initial configuration which will be updated with the currently displayed errors.
Although the library knows the state of the complete form at all times, errors are not always displayed. There's nothing more annoying than a form nagging you about an incorrect email address when you haven't finished writing it.
This is the default behavior:
blur and focus events will not hide an error.See the settings below on customizing the default behavior.
When using the external validation on a field, the library will ignore all the error displaying logic and you will have total control on when to display an error.
When configuring a field you can add any number of sync validators:
function isAllCaps (value) {
const regexLowercase = /[a-z]/;
const testLowercase = regexLowercase.test(value);
if (testLowercase) return 'Only use uppercase letters';
return true;
}
const formControllerSettings = {
async onSubmit (values) {},
fields: {
titleInCaps: {
validators: [isAllCaps]
}
}
}
In your validator, return true if the value is valid. Anything other than true will mark the field as invalid, and whatever you return will be available in the controllerState and displayedErrors stores.
You can add a per-field external validator which will have full control over the validation of a field:
function checkDomainIsAvailable (fieldState, eventType, update) {
// do your thing
}
const formControllerSettings = {
async onSubmit (values) {},
fields: {
domain: {
externalValidator: checkDomainIsAvailable
}
}
}
fieldState is the current state of the field (value, etc)eventType is whatever event that triggered the external validator ('submit', 'input', etc).update is a function you can use to update the state of the field such as marking it valid, invalid, etc.See the async validation form for a full example on how to implement async validation with promise cancelation.
Because the DOM is the source of truth, you can alter it in any way you need. You can add and remove fields dynamically and the state of the library will be updated magically.
See the dynamic form demo for an example of a dynamic form.
Field settings are connected to a particular input using its name attribute. This works for the vast majority of use cases, but there are more complex scenarios where this breaks.
For example, imagine you had a field called productName with a custom validator to ensure the name is properly formatted:
const formControllerSettings = {
async onSubmit (values) {},
fields: {
productName: {
validators: [checkNameFormat]
}
}
}
By default, the productName settings would only be applied to this input:
<input type="text" name="productName"/>
But what if you wanted to edit multiple products in the same form? What if the number of products was dynamic and you didn't know how many you'd want to edit?
The solution is using name aliases so that you can apply a particular setting to any input:
<input type="text" name="productName|3b761efe-f253-484c-9b56-febf9dcb7268"/>
<input type="text" name="productName|f52ddac8-d46a-42cd-9de3-f84275e786b2"/>
<input type="text" name="productName|9329ff26-90e7-4b01-a1c8-f0ef0d0b0d3a"/>
Now all those inputs will use the productName settings, but will be treated as different inputs with their own name. In this example, the product id.
See the dynamic form with custom validation demo for a full example on how to use aliases.
controller action settingsRequired:
onSubmit a callback function that will be triggered when all validation has passed and the form is submitted.Optional:
fields an object with field configurationsvalidClass custom CSS class that will be applied to valid input elements. The default is valid. If set to null no class will be applied.invalidClass custom CSS class that will be applied to invalid input elements. The default is invalid. If set to null no class will be applied.useNativeErrorTooltips use the browser's error tooltips. Defaults to false.displayedErrors a Svelte writable that will be updated with displayed errorscontrollerState a Svelte writable that will be updated with the controller statedisplayErrorsOnBlur display field errors on blur events. The default is false.displayErrorsOnChange display field errors on change and input events. The default is false.hideErrorsOnChange hide field errors on change and input events. The default is false.addValidClassToAllInputs add the validClass too all types of inputs. The default is false.All optional:
validators an array with sync functions that will be used to validate the value of the field after the native validators have passed.externalValidator a sync function that will be used for all validation. When using this option, no validation will be performed by the library itself.displayErrorsOnChange show field errors on input and change events. The default is false.