Advanced form validation in Magewire
Hyvä includes a JavaScript form validation library that can be used with Magewire form components, so that validation failure messages are automatically displayed next to the input elements in an accessible manner.
A magewire
validation rule is automatically added in the Hyvä Checkout.
Preparing the form element
The form validation needs to be initialized on the form element as described in the form validation documentation.
Additionally, a novalidate
attribute has to be added.
Under the hood: why is novalidate
needed?
The form validation library adds the novalidate
attribute to the form automatically when the form Alpine component is initialized.
However, when Magewire renders the form element again during subsequent requests, it will remove the novalidate
attribute if it isn't also rendered in the template.
The effect of this is that the form validation works correctly during the first form submission, but during form submissions after subsequent requests the browser will apply native HTML5 form validation itself again without delegating to the JavaScript validation library.
Preparing the input elements
To add the validation rule to an input, add "magewire": true
to the list of validations in the data-validate
attribute JSON
The current state of the form component attribute is specified with a data-magewire-is-valid
data attribute.
valid == 1, invalid == 0
The attribute state has to be specified as an integer value of 1
if it is valid, or 0
if the component attribute is invalid.
Using a boolean true
or false
or other truthy/falsy values will not work.
Finally, if the form component attribute is invalid, the validation message has to be specified using the data-msg-magewire
attribute.
<?php if ($magewire->hasError('myInput')): ?>
data-msg-magewire="<?= $escaper->escapeHtmlAttr($magewire->getError('myInput')) ?>"
<?php endif; ?>
Putting it all together:
<input id="my-input"
type="text"
name="my-input"
wire:model.defer="myInput"
data-validate='{"magewire": true}'
data-magewire-is-valid="<?= (int) !$magewire->hasError('myInput') ?>"
<?php if ($magewire->hasError('myInput')): ?>
data-msg-magewire="<?= $escaper->escapeHtmlAttr($magewire->getError('myInput')) ?>"
<?php endif; ?>
>
Triggering form validation
Form validation is automatically triggered after Magewire updates an element during subsequent requests.
To trigger the validation on the frontend without a Magewire roundtrip, call the Alpine method validate()
to validate all fields, or use @change="onChange"
on a form element.
Submitting a form
If the form is bound to a Magewire method with wire:submit.prevent="submitForm"
this will conflict with the JavaScript form validation.
To work around the issue, trigger the validate()
method and call the Magewire form component target method if there are no errors.
<button type="button" class="btn btn-primary"
@click="validate().then(() => $wire.submitForm()).catch(() => {})">
<?= $escaper->escapeHtml(__('Save')) ?>
</button>
Under the hood: why does it conflict?
The Magewire wire:submit.prevent=""
action adds a readonly
attribute to all form elements before the subsequent request is made.
This happens before the Alpine form validation is processed.
The problem is that the readonly
attribute prevents form elements from being validated, so the form will be submitted without any frontend validation.