Purpose#
The ForgotPassword component renders the “Forgot password” form and initiates the password reset flow by submitting the user’s email to the services layer.An instructional block (3-step explanation)
A single email input with client-side validation
A submit button that becomes enabled only when the form is valid
Toast feedback for success/failure
Model shape (storefront example)#
{
"name": "ForgotPassword",
"view": "Default",
"section": "SectionA",
"settings": {
"id": "Component Id",
"section": "SectionA",
"type": "NoirForgotPassword",
"name": "ForgotPassword",
"configuredInContentApi": true,
"view": "Default",
"displayName": "",
"cssClass": ""
},
"translations": {
"forgotPassword": "Sample text",
"fillEmailAddress": "Sample text",
"linkToChangePassword": "Sample text",
"...": "..."
}
}
Required fields#
settings.id
Used for wrapper id: comp-{{ id }}.
Optional fields#
settings.cssClass
Applied only when non-empty and not (UNDEFINED).
translations.*
Used for titles, instructions, input labels/placeholders, validation messages, and toast messages:
JavaScript#
Global object#
The component exposes an Alpine-ready object:It’s mounted via x-data="forgotpassworddefault" on the form.
State fields#
errorType: ""
Current validation error code:"required" / "invalidEmail" / ""
isFormValid: false
Controls submit button enablement.
isSending: false
Prevents double submits and disables the submit button during the request.
email: null
Cached reference to the email <input> element.
errorType is intentionally a single value (not a map), because the form has only one field.
init()#
Finds the email input within the form scope:this.email = this.$el.querySelector("input")
Adds a blur listener so validation runs when the user leaves the field:this.email.addEventListener("blur", () => this.validateField(this.email))
This assumes the form contains exactly one input. If you add more inputs later, change the selector to target #reset-email specifically.
validateField(field)#
Validates email using a simple regex:empty required → errorType = "required"
invalid format → errorType = "invalidEmail"
valid → isFormValid = true
Updates input CSS classes:toggles .invalid / .valid
Updates the .icon-state element (warning vs check icon classes).
The regex is intentionally basic. If you tighten it too much, you’ll reject valid emails.
The icon selector depends on the template structure (.icon-state inside the label). If you change markup, keep the selector in sync.
clearForm()#
Clears the email input value.
Removes .valid / .invalid classes.
Resets the .icon-state element by removing:ic-check-circle-fill, ic-warning, hidden
This is called only on successful submission. If you want a “Reset form” UX later, reuse this method.
checkForm(successMessage, errorMessage) (async)#
1.
Clears all existing toasts:Alpine.store("toast").removeAll()
2.
Validates the email field:this.validateField(this.email)
3.
{ Email: this.email.value }
calls the services layer:servicesreusabledefault.accountForgotPassword(info)
The code contains a backend workaround (FIXME comment):It treats "User Not Found" as a non-fatal outcome (no error toast).
For other failures, it shows an error toast:toast.add(errorMessage, "ic-warning", "error", true)
the last true typically indicates a sticky/persistent toast.
toast.add(successMessage, "ic-check", "success")
The "User Not Found" exception is a temporary compatibility hack. When the backend is fixed, remove this special-case so error handling is consistent.
Wrap the service call with try/finally if you want to guarantee isSending returns to false even on unexpected exceptions.
Consider normalizing the response contract so all failures return success === false with a 200 status; the code already hints that this is the desired backend behavior.
Global Alpine stores (used by ForgotPassword)#
ForgotPassword is a small form component. It doesn’t share state with other components, but it uses a global store for consistent user feedback.$store.toast (user feedback)#
This store is used to show success/error messages as toasts, consistent with the rest of the theme.How ForgotPassword uses itClears previous messages before validating/submitting:Alpine.store("toast").removeAll()
On API error (except the known “User Not Found” workaround):Alpine.store("toast").add(errorMessage, "ic-warning", "error", true)
Alpine.store("toast").add(successMessage, "ic-check", "success")
What it means in practiceThe component does not render success/error banners inside the UI; it relies on toasts.
It keeps the page layout stable (no shifting content) while still informing the user.
The last argument true in toast.add(..., true) typically means a “sticky” toast (depends on store implementation).
If the toast store isn’t available, the user will get no feedback even if the API fails or succeeds, so $store.toast is a hard dependency for good UX.
Services / API calls#
servicesreusabledefault.accountForgotPassword({ Email })
Initiates the password reset process (sends email instructions / temporary link).
Dependencies#
Services layer: servicesreusabledefault.accountForgotPassword(...)
Notes#
The form is intentionally minimal (email only). All other content is instructional.
The success flow does not redirect; it relies on the toast message to inform the user.
There is a known backend inconsistency (“User Not Found” error handling) reflected directly in the JS via a FIXME comment.
Template behavior (Liquid + Alpine)#
Layout & instructions#
Renders a title from translations.forgotPassword.
Renders instructions (translations.changePasswordInstructions) plus a 3-step ordered list: x-data="forgotpassworddefault"
@submit.prevent="checkForm(successMessage, errorMessage)"
Validation messaging#
Validation messages are driven by a single state field (errorType) and shown with:x-show="errorType === 'required'"
x-show="errorType === 'invalidEmail'"
The submit button is disabled when:!isFormValid OR isSending
:disabled="!isFormValid || isSending"
Modified at 2026-04-14 13:18:56