@morish000/alpinejs-form-validate-plugin v0.1.5
This document was originally written in Japanese, and the English version is machine translated. For the original, please refer to README_ja.md.
Form Validate Plugin for Alpine.js
This is a plugin for performing form validation with
Alpine.js.\
I wanted to use hx-post and
Validation Integration in
HTMX, but I couldn't find convenient utilities using HTML5
form validation, so I created one myself.
Key Features
- Change default validation error messages
- Add validation logic
- Specify validation execution timing (
onChange,onBlur,onInput) - Dispatch success/failure events after validation
- Set
debounce/throttleforinputevents - Define validation logic externally for reuse
- Define messages externally for reuse
Messages are set using setCustomValidity to conform as much as possible to HTML5 standard validation.
Regarding Radio Buttons and Checkboxes:
Radio buttons and checkboxes are tricky elements within FORM, but this plugin
treats those with the same name attribute collectively as follows:
- Radio Buttons: Treated as SELECT tags with
multiple=false - Checkboxes: Treated as SELECT tags with
multiple=true
Thus, the value of radio buttons is handled as a string, and the value of
checkboxes as a string array.\
Therefore, it is expected that the validate directive is written only to one
element among radio buttons or checkboxes with the same name attribute.\
The behavior when written to multiple elements is not considered.
Sample on CodePen
- Display Errors with Browser Balloon Message (Default)
- Display Errors Using Bootstrap
- Example of Internationalizing Messages with i18next
Simple Usage
Only supports ESM.
Import Map Sample
You can obtain it from esm.sh as follows:
<script type="importmap">
{
"imports": {
"@morish000/alpinejs_form_validate_plugin": "https://esm.sh/@morish000/alpinejs-form-validate-plugin",
"@morish000/alpinejs_form_validate_plugin/plugin": "https://esm.sh/@morish000/alpinejs-form-validate-plugin/plugin",
"@morish000/alpinejs_form_validate_plugin/functions": "https://esm.sh/@morish000/alpinejs-form-validate-plugin/functions",
"@morish000/alpinejs_form_validate_plugin/i18next/alpinejs_i18next_plugin": "https://esm.sh/@morish000/alpinejs-form-validate-plugin/i18next/alpinejs_i18next_plugin",
"@morish000/alpinejs_form_validate_plugin/i18next/i18next_message_resolver": "https://esm.sh/@morish000/alpinejs-form-validate-plugin/i18next/i18next_message_resolver"
}
}
</script>You can obtain it from jsDelivr as follows:
<script type="importmap">
{
"imports": {
"@morish000/alpinejs_form_validate_plugin": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/mod.mjs",
"@morish000/alpinejs_form_validate_plugin/plugin": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/alpinejs_form_validate_plugin.mjs",
"@morish000/alpinejs_form_validate_plugin/functions": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/functions/index.mjs",
"@morish000/alpinejs_form_validate_plugin/i18next/alpinejs_i18next_plugin": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/i18next/alpinejs_i18next_plugin.mjs",
"@morish000/alpinejs_form_validate_plugin/i18next/i18next_message_resolver": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/i18next/i18next_message_resolver.mjs"
}
}
</script>Setup
x-init triggers the initialization of Alpine.js, so it's unnecessary if
x-init or x-data is already present on necessary tags.
<html>
<head>
<script type="importmap">
{
"imports": {
"alpinejs": "https://esm.sh/alpinejs@3.14.x",
"@morish000/alpinejs_form_validate_plugin": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/alpinejs_form_validate_plugin.mjs"
}
}
</script>
<script type="module">
import Alpine from "alpinejs";
import { createValidatePluginDefault } from "@morish000/alpinejs_form_validate_plugin";
document.addEventListener("DOMContentLoaded", () => {
Alpine.plugin(createValidatePluginDefault);
Alpine.start();
});
</script>
</head>
<body id="body-1" x-init></body>
</html>Changing Messages
Validation becomes active after pressing the submit button once.\
Refer to
ValidityState
for keys such as valueMissing, tooLong, tooShort.
elis the element object where the directive is set, and is set byAlpine.js.idandnameof theinputtag are mandatory. They don't need to be the same.- By default, validation is executed with the
onChangeevent, but for text boxes, it can be executed immediately on input by settingonInputtotrue.
In the sample below, template literals are used for messages, but they are evaluated only once during directive initialization. This means they are not reactive.
Text Length Check:
<form x-validate-form autocomplete="off" novalidate>
<input
id="text-1"
name="text-1"
type="text"
required
minlength="2"
x-validate="{
onInput: true,
m: {
valueMissing: `${$el.name} is required`,
tooShort: `Please enter at least ${$el.minLength} characters`
}
}"
/>
<input type="submit" value="Submit" />
</form>Required Check for Radio Buttons:
<form x-validate-form autocomplete="off" novalidate>
<input
id="radio-1"
name="radio-1"
type="radio"
value="radio-1"
required
x-validate="{
m: {
valueMissing: `${$el.name} is required`,
}
}"
/>
<input id="radio-2" name="radio-1" type="radio" value="radio-2" />
<input type="submit" value="Submit" />
</form>Adding Validation
- For elements without the
requiredattribute, if not entered or selected, other validations do not run, and it ends successfully. This follows the behavior of HTML5 standard validation. - When multiple validation functions are set, validation completes if one of them results in an error. Only one error message is displayed.
The first argument of the validation function (el) is the field element
object.\
The second argument (value) follows the definitions below.
| Element | Contents of value |
|---|---|
| Radio Buttons | Value of the selected element with the same name attribute (string). |
| Checkboxes | Array of values of selected elements with the same name attribute (string array; if none selected, array with length=0). |
| File | Array of File regardless of multiple (if none selected, length=0). |
| SELECT (multiple=false) | Value if selected (string). If not selected, the value of the first option tag following HTML behavior (string). |
| SELECT (multiple=true) | Array of values of selected option tags (string array; if none selected, array with length=0). |
| Other | The value of el.value (string). If not entered, an empty string (""). |
When a function is specified directly, the key (written below as validate_1)
can be any name.\
The 0th element of the array is the validation function, and the 1st is the
message definition.
File Size Check (multiple=false):
<form x-validate-form autocomplete="off" novalidate>
<input
id="file-1"
name="file-1"
type="file"
x-validate="{
v: {
validate_1: [(el, value) => value.length == 1 && value[0].size < 256, 'Please select a file smaller than 256 bytes'],
},
}"
/>
<input type="submit" value="Submit" />
</form>SELECT (multiple=true) - Require more than two selections:
<form x-validate-form autocomplete="off" novalidate>
<select
id="select-1"
name="select-1"
multiple
required
x-validate="{
v: {
validate: [(el, value) => value.length > 1, 'Please select at least two options'],
},
m: {
valueMissing: 'Please select at least two options'
}
}"
>
<option value="check-1">Option 1</option>
<option value="check-2">Option 2</option>
<option value="check-3">Option 3</option>
</select>
<input type="submit" value="Submit" />
</form>Checkbox Example - Require more than two selections:
<form x-validate-form autocomplete="off" novalidate>
<input
id="check-1"
name="check-1"
value="check-1"
type="checkbox"
required
x-validate="{
v: {
validate: [(el, value) => value.length > 1, 'Please select at least two options'],
},
m: {
valueMissing: 'Please select at least two options'
}
}"
/>
<input id="check-2" name="check-1" value="check-2" type="checkbox" />
<input type="submit" value="Submit" />
</form>Configuration Values
validate-form: Directive
Default Values:
{
report: true,
trigger: {
target: el,
event: "submit",
preventDefault: true,
before: null,
after: null,
},
}| Name | Value |
|---|---|
| report | Specifies whether to display the browser's default message. Set to false to create a custom error display. |
| trigger | Settings for events that trigger validation execution. |
| trigger.target | The object from which the event is issued. By default, it is the form tag where the directive is set. |
| trigger.event | Event name. By default, it operates with the submit event of the form object. |
| trigger.preventDefault | Specifies whether to stop the default action when there is a validation error. |
| trigger.before | Function to execute before validation. Type is (e?: Event) => void. |
| trigger.after | Function to execute after validation. Type is (e?: Event) => void. |
You can set the x-validate-form directive of the form tag in the same format
and overwrite the default values.
validate: Directive
Default Values:
{
v: {},
m: {},
report: false,
onChange: true,
onBlur: false,
onInput: false,
inputLimit: "none",
inputLimitOpts: {
debounce: { wait: 250, immediate: false },
throttle: {
wait: 500,
options: { leading: false, trailing: true },
},
},
}| Name | Value |
|---|---|
| v | Custom validation definitions. |
| m | Specify messages for HTML5 standard validation features. |
| report | Specify whether to show the browser's default message. The default value is false, but the form setting takes precedence. |
| onChange | Set for execution with the change event. |
| onBlur | Set for execution with the blur event. |
| onInput | Set for execution with the input event. |
| inputLimit | Restrict event processing when the input event is enabled. |
| inputLimitOpts | Detailed options for inputLimit. Used only if inputLimit is set. |
| inputLimitOpts.debounce.wait | Do not fire the next event if this time has not elapsed between the previous and the next event. Milliseconds. |
| inputLimitOpts.debounce.immediate | Whether to fire the first event. |
| inputLimitOpts.throttle.wait | Do not fire the next event until this time has elapsed after the event. Milliseconds. |
| inputLimitOpts.throttle.options.leading | Whether to fire the first event. |
| inputLimitOpts.throttle.options.trailing | Whether to fire an event after the waiting time if an event occurred during the waiting time. Fires only once even if multiple occur. |
You can set the x-validate directive of the form field tag in the same
format and overwrite the default values.
v
Add custom validation.\
Only one validation function is executed at a time.\
HTML5 standard validation operates first, so it works after the standard
validation succeeds.\
Also, if required is not specified and not entered (not selected), custom
validation does not operate, and validation succeeds.
You can abbreviate the following format:
{
key1: [(el, value) => boolean, arg],
key2: [(el, value) => boolean, [...args]],
key3: {
v: (el, value) => boolean,
m: arg
},
key4: arg,
key5: [...args],
key6: {
v: arg,
m: arg
},
}These convert to the following formal format:
{
key1: {
v: (el, value) => boolean,
m: [arg]
},
key2: {
v: (el, value) => boolean,
m: [...args]
},
key3: {
v: (el, value) => boolean,
m: [arg]
},
key4: {
v: [],
m: [arg],
},
key5: {
v: [],
m: [...args],
},
key6: {
v: [arg],
m: [arg]
},
}If v is a function, the key is meaningless. The function is executed, and if
there is an error, a message is set according to the settings of m.\
If v is an array, the function registered with the key is executed. The array
becomes a parameter to the function.\
By default, no functions are registered, so setting is required to enable this
feature. An example using Validator.js is shown later.\
The first argument to the registered function is value, so the second and
subsequent arguments to the v array are set.\
m is the message set if the execution result of v is false.\
By default, the 0th element of the array becomes the message as it is.\
If defining message resources externally, the m array becomes an argument to
the message retrieval function.\
Examples of how to use i18next to define messages externally are shown later.
m
Set messages for HTML5 standard validation.\ You can abbreviate the following format:
{
valueMissing: arg,
typeMismatch: [...args]
}These convert to the following formal format:
{
valueMissing: [arg],
typeMismatch: [...args],
}You can use the following keys: Refer to ValidityState.
- valueMissing
- typeMismatch
- patternMismatch
- tooLong
- tooShort
- rangeUnderflow
- rangeOverflow
- stepMismatch
- badInput
The array specified here has the same meaning as the v.m array.\
That is, by default, the 0th element of the array becomes the message as it is.\
If defining message resources externally, the m array becomes an argument to
the message retrieval function.\
Examples of how to use i18next to define messages externally are shown later.
onChange, onBlur, onInput
These values are usually boolean values, but like form, you can set
functions before and after validation execution.\
An object set is regarded as true.
Example:
onChange = {
before: (e?: Event) => {
// Processing before validation
},
after: (e?: Event) => {
// Processing after validation
}
}inputLimit
Controls the execution of input events.\
You can specify debounce, throttle, and their behavior is general.\
The type definition of inputLimit is as follows.
type InputLimit =
| "none"
| "debounce"
| "throttle"
| `debounce:${number}`
| `throttle:${number}`;${number} is the wait value, and those specified here will be used with the
highest priority.
validate-message-for: Directive
Directive for outputting validation messages to the textContent of an element.
Example:
<p x-validate-message-for="#sample"></p>The attribute value is the search parameter for the target form field
element.\
If the found form field has a validation error, the message is set to the
element specified with the directive (in the example above, the p tag)
textContent.\
Set parameters to
document.querySelector
for the attribute value.\
If multiple matches occur, the error message of the first element is displayed.\
If not found, nothing is done. Typically, it should be an id search.
This directive does not have a feature to control the display and hide of tags,
so it may be necessary to combine with other methods.\
An example using Bootstrap is shown later.
Changing Default Values
You can overwrite the default values during plugin initialization.
Using Default Values:
import { createValidatePluginDefault } from '...';
...
Alpine.plugin(createValidatePluginDefault);Overwriting Default Values:
import { createValidatePlugin } from '...';
...
Alpine.plugin(createValidatePlugin({
defaultFormOptions: {
report: ...,
trigger: {
target: ...,
}
},
defaultFieldOptions: {
v: ...,
inputLimitOpts: {
throttle: {
wait: ...,
}
}
}
}));Specify only the items you want to overwrite, but you must follow the tree structure.\ The usual priority is as follows:
Values specified in the directive of the tag>Values specified at initialization>Default values
However, only the report value of the field element has the priority below.
Values specified in the directive of the field tag>Determined value of the form tag>Values overwritten at initialization>Default values
Therefore, the report default value or overwritten value at initialization
of the field tag is usually not used.
Events
In the form tag and the form field tag, the execution result of validation
is dispatched as a CustomEvent.
On Success:\
Event Name: x-validate:success
On Error:\
Event Name: x-validate:failed
If the prefix of Alpine.js is changed, the x- in the event name will also
change.
i18next (Optional)
There is an option to use i18next as a message
resource.\
Here, setup and examples for using i18next are shown.\
How to actually validate is shown later.
Setup
Refer to the i18next documentation for details on the i18next setup.\
Here, we will use i18nextHttpBackend to get message resources from the
server.\
Assuming message resource files are placed on the server at the following paths:
/locales/validate/en.json:{ "greeting": "Hello, {{name}}!" }/locales/validate/ja.json:{ "greeting": "こんにちは, {{name}}!" }
<html>
<head>
<script type="importmap">
{
"imports": {
"alpinejs": "https://esm.sh/alpinejs@3.14.x",
"i18next": "https://esm.sh/i18next@24.2.x",
"i18next-http-backend": "https://esm.sh/i18next-http-backend@3.0.x",
"@morish000/alpinejs_form_validate_plugin/i18next/alpinejs_i18next_plugin": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/i18next/alpinejs_i18next_plugin.mjs"
}
}
</script>
<script type="module">
import Alpine from "alpinejs";
import i18next from "i18next";
import i18nextHttpBackend from "i18next-http-backend";
import { createI18NextPlugin } from "@morish000/alpinejs_form_validate_plugin/i18next/alpinejs_i18next_plugin";
document.addEventListener("DOMContentLoaded", () => {
i18next.on("initialized", () => {
const i18nStore = Alpine.reactive(
{
timestamp: Date.now(),
i18next: () => i18next,
},
);
Alpine.plugin(createI18NextPlugin(i18nStore));
Alpine.start();
});
i18next.on("failedLoading", (lng, ns, msg) => {
console.error(`failedLoading ${lng}, ${ns}, ${msg}`);
});
const i18nextOptions = {
fallbackLng: "en",
ns: ["validate"],
defaultNS: "validate",
preload: ["en", "ja"],
backend: {
loadPath: "/locales/{{ns}}/{{lng}}.json",
},
};
i18next
.use(i18nextHttpBackend)
.init(i18nextOptions);
});
</script>
</head>
<body id="body-1" x-init>
<p x-text="$t('greeting', { name: 'morish000'})"></p>
<p x-i18next-text="['greeting', { name: 'morish000'}]"></p>
<button @click="$i18next().changeLanguage('en')">English</button>
<button @click="$i18next().changeLanguage('ja')">Japanese</button>
<button @click="$i18next().reloadResources()">Reload</button>
</body>
</html>In this example, the DOMContentLoaded event handler is used to initialize in
the script tag.\
When bundling, it's better to load with
script defer.
In that case, it's no longer necessary to set to the DOMContentLoaded event
handler.
To make it reactive, Alpine.reactive is used, but Alpine.store can also be
used.
Alpine.store("i18next", {
timestamp: Date.now(),
i18next: () => i18next,
});
const i18nStore = Alpine.store("i18next");t: Magic Function
Returns the value obtained with the t
function of i18next as it is.\
This utility does not perform sanitization processing.\
Sanitization should be performed as needed when outputting.
Example:
<p x-text="$t('greeting', { name: 'morish000'})"></p>i18next-text: Directive
Sets the string obtained with the t function of i18next to textContent.
Example:
<p x-i18next-text="['greeting', { name: 'morish000'}]"></p>i18next: Magic Function
Gets the i18next object. Used when you want to change locales or reload.
Example:
<button @click="$i18next().changeLanguage('en')">English</button>
<button @click="$i18next().changeLanguage('ja')">Japanese</button>
<button @click="$i18next().reloadResources()">Reload</button>The t magic function and the i18next-text directive operate reactively with
changeLanguage and reloadResources.
Use Bootstrap to Display Error Messages
An example of using Bootstrap to output validation messages.
Example:
<html>
<head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
/>
<style>
[x-cloak] {
display: none !important;
}
</style>
<script type="importmap">
{
"imports": {
"alpinejs": "https://esm.sh/alpinejs@3.14.x",
"@morish000/alpinejs_form_validate_plugin": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/alpinejs_form_validate_plugin.mjs"
}
}
</script>
<script type="module">
import Alpine from "alpinejs";
import { createValidatePluginDefault } from "@morish000/alpinejs_form_validate_plugin";
document.addEventListener("DOMContentLoaded", () => {
Alpine.plugin(createValidatePluginDefault);
Alpine.start();
});
</script>
</head>
<body id="body-1" x-init>
<div class="container">
<form
x-validate-form="{ report: false }"
@x-validate:success="$el.classList.remove('was-validated');"
@x-validate:failed="$el.classList.add('was-validated');"
autocomplete="off"
novalidate
>
<div class="form-group">
<input
id="text-1"
name="text-1"
type="text"
required
minlength="2"
x-validate="{
onInput: true,
m: {
valueMissing: `${$el.name} is required`,
tooShort: `Please enter at least ${$el.minLength} characters`
}
}"
/>
<p x-validate-message-for="#text-1" class="invalid-feedback" x-cloak>
</p>
</div>
<div class="form-group">
<input
id="check-1"
name="check-1"
value="check-1"
type="checkbox"
required
x-validate="{
v: {
validate: [(el, value) => value.length > 1, 'Please select at least two options'],
},
m: {
valueMissing: 'Please select at least two options'
}
}"
/>
<input id="check-2" name="check-1" value="check-2" type="checkbox" />
<p x-validate-message-for="#check-1" class="invalid-feedback" x-cloak>
</p>
</div>
<div class="form-group">
<input
id="radio-1"
name="radio-1"
value="radio-1"
type="radio"
required
x-validate="{
m: {
valueMissing: 'This value is required'
}
}"
/>
<input id="radio-2" name="radio-1" value="radio-2" type="radio" />
<p x-validate-message-for="#radio-1" class="invalid-feedback" x-cloak>
</p>
</div>
<input type="submit" value="Submit" />
</form>
</div>
</body>
</html>- The
linktag downloadsBootstrap. - Set
report=falseinx-validate-formto prevent the default balloon message. - Use
@x-validate:successand@x-validate:failedto add or remove thewas-validatedclass. - Surround each field with a
divtag of theform-groupclass and output messages withx-validate-message-for. Set theinvalid-feedbackclass on theptag for message output. - For
x-cloak, refer to Alpine.js Documentation. - For the
was-validatedandinvalid-feedbackclasses, refer to the Bootstrap Manual.
Use Validator.js and Custom Function for Validation Functions
An example of using Validator.js
for custom validation functions.\
Keys execute with the function name, so you can use custom implementations if
the first argument is a check value and the return value is boolean.
In the example below, the contains and isUppercase functions of
Validator.js are executed.\
The v is set with the second and third arguments to the contains function.\
The isUppercase function has no parameters set other than the check value, so
v is not set.
Additionally, a function for file size checks is added as a custom implementation by including it as a validation function.
Specify it in the parameter of the createValidatePlugin function during
initialization.
Example:
<html>
<head>
<script type="importmap">
{
"imports": {
"alpinejs": "https://esm.sh/alpinejs@3.14.x",
"validator": "https://esm.sh/validator@13.12.x",
"@morish000/alpinejs_form_validate_plugin": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/alpinejs_form_validate_plugin.mjs",
"@morish000/alpinejs_form_validate_plugin/functions": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/functions/index.mjs"
}
}
</script>
<script type="module">
import Alpine from "alpinejs";
import validator from "validator";
import { createValidatePlugin } from "@morish000/alpinejs_form_validate_plugin";
import { createCustomFieldValidator } from "@morish000/alpinejs_form_validate_plugin/functions";
document.addEventListener("DOMContentLoaded", () => {
Alpine.plugin(createValidatePlugin({
defaultFunctionsOptions: {
customFieldValidators: [
createCustomFieldValidator(validator),
createCustomFieldValidator({
maxFileSize: (value, size) =>
value.length == 1 && value[0].size < size,
}),
],
},
}));
Alpine.start();
});
</script>
</head>
<body id="body-1" x-init>
<form x-validate-form autocomplete="off" novalidate>
<input
id="text-1"
name="text-1"
type="text"
required
x-validate="{
onInput: true,
v: {
contains: {
v: ['abc', { ignoreCase: true}],
m: 'Please include \'abc\'.'
}
},
m: {
valueMissing: `${$el.name} is required`,
}
}"
/>
<input
id="text-2"
name="text-2"
type="text"
required
x-validate="{
onInput: true,
v: {
isUppercase: 'Please enter in uppercase.'
},
m: {
valueMissing: `${$el.name} is required`,
}
}"
/>
<input
id="file-1"
name="file-1"
type="file"
x-validate="{
v: {
maxFileSize: {
v : 256,
m : 'Please select a file smaller than 256 bytes'
}
}
}"
/>
<input type="submit" value="Submit" />
</form>
</body>
</html>Use i18next for Message Resources
An example using i18next for message resources.
/locales/validate/en.json:{ "moreThanChars": "Please enter at least {{ count }} characters.", "required": "This value is required. {{ name }}" }/locales/validate/ja.json:{ "moreThanChars": "{{ count }} 文字以上で入力してください。", "required": "この値は必須です。{{ name }}" }
Example:
<html>
<head>
<script type="importmap">
{
"imports": {
"alpinejs": "https://esm.sh/alpinejs@3.14.x",
"i18next": "https://esm.sh/i18next@24.2.x",
"i18next-http-backend": "https://esm.sh/i18next-http-backend@3.0.x",
"@morish000/alpinejs_form_validate_plugin": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/alpinejs_form_validate_plugin.mjs",
"@morish000/alpinejs_form_validate_plugin/i18next/alpinejs_i18next_plugin": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/i18next/alpinejs_i18next_plugin.mjs",
"@morish000/alpinejs_form_validate_plugin/i18next/i18next_message_resolver": "https://cdn.jsdelivr.net/gh/morish000/alpinejs-form-validate-plugin/dist/i18next/i18next_message_resolver.mjs"
}
}
</script>
<script type="module">
import Alpine from "alpinejs";
import i18next from "i18next";
import i18nextHttpBackend from "i18next-http-backend";
import { createI18NextPlugin } from "@morish000/alpinejs_form_validate_plugin/i18next/alpinejs_i18next_plugin";
import { createI18NextMessageResolver } from "@morish000/alpinejs_form_validate_plugin/i18next/i18next_message_resolver";
import { createValidatePlugin } from "@morish000/alpinejs_form_validate_plugin";
document.addEventListener("DOMContentLoaded", () => {
i18next.on("initialized", () => {
const i18nStore = Alpine.reactive(
{
timestamp: Date.now(),
i18next: () => i18next,
},
);
Alpine.plugin(createI18NextPlugin(i18nStore));
Alpine.plugin(createValidatePlugin({
defaultFunctionsOptions: {
messageResolver: createI18NextMessageResolver(i18nStore)(
Alpine,
),
},
}));
Alpine.start();
});
i18next.on("failedLoading", (lng, ns, msg) => {
console.error(`failedLoading ${lng}, ${ns}, ${msg}`);
});
const i18nextOptions = {
fallbackLng: "en",
ns: ["validate"],
defaultNS: "validate",
preload: ["en", "ja"],
backend: {
loadPath: "/locales/{{ns}}/{{lng}}.json",
},
};
i18next
.use(i18nextHttpBackend)
.init(i18nextOptions);
});
</script>
</head>
<body id="body-1" x-init>
<form x-validate-form autocomplete="off" novalidate>
<input
id="text-1"
name="text-1"
type="text"
required
x-validate="{
onInput: true,
v: {
validate: {
v: (el, value) => value.length > 1,
m: ['moreThanChars', { count: 2 }]
}
},
m: {
valueMissing: ['required', { name: $el.name }],
}
}"
/>
<input type="submit" value="Submit" />
</form>
<button @click="$i18next().changeLanguage('en')">English</button>
<button @click="$i18next().changeLanguage('ja')">Japanese</button>
<button @click="$i18next().reloadResources()">Reload</button>
</body>
</html>The i18next setup is the same as described above.\
To use it in validation, set messageResolver as a parameter for
createValidatePlugin.\
The array set in m in the directive is an argument to the
i18next.t function. The 0th element is
the key corresponding to it, and the 2nd element is the options.
License
This project is licensed under the MIT License - see the LICENSE file for details.
12 months ago