Purpose#
AddressForm is the reusable that drives the address selection + address form UX in Checkout.It renders saved addresses (when present), allows editing, supports creating a new address, optionally collects invoice data (billing), and synchronizes billing/shipping via a shared Alpine store.On progression it also updates the Checkout state by calling servicesreusabledefault.updateCheckout(...).Where it's rendered#
It’s not rendered directly from a component; it’s rendered by BillingRetail (Checkout billing section).From Reusables/BillingRetail/Default.liquid:{% render 'Reusables\\AddressForm\\Default',
addressType: 'billing',
addresses: addresses,
countriesList: countriesList,
headQuarterAddress: headQuarterAddress
%}
{% render 'Reusables\\AddressForm\\Default',
addressType: 'shipping',
addresses: addresses,
countriesList: countriesList,
headQuarterAddress: headQuarterAddress
%}
Source: Reusables/AddressForm/Default.liquid.The reusable is initialized via Alpine using:x-data='addressformreusabledefault.initComponent(
{{ addresses | serialize | escape }},
{{ countriesList }},
{{ deliveryZonesEnabled }},
"{{ addressType }}",
"{{ inValidAddressMessage }}",
{{ modalTranslationsInvoice }},
"{{ addressErrorMessage }}",
"{{ addressUpdateErrorMessage }}",
"{{ postalCodesErrorMessage }}",
{{ isB2bCustomer }}
)'
Model shape (storefront example)#
Below is a sanitized excerpt from a real Checkout model dump (the fields used by this reusable via addresses and countriesList).{
"countriesList": [
{
"code": "GR",
"name": "Greece"
},
...
],
"addresses": [
{
"name": "",
"isSelectable": true,
"address": {
"id": "Address Id",
"firstName": "Sample first name",
"lastName": "Sample last name",
"phoneNumber": "Sample phone",
"address1": "Sample address",
"city": "Sample city",
"state": "Sample state",
"country": "Sample country",
"countryCode": "GR",
"postalCode": "10552",
"email": "sample@example.com"
},
"requiresInvoice": false
}
]
}
Required fields#
addresses#
Expected input is an array; each entry is expected to look like:Used as a “branch label” in some display cases.
Used when deliveryZonesEnabled is true.
The actual address fields that the forms bind to (examples seen in Liquid/JS):label (optional; shown in some recaps)
additionalInfo (used for new addresses)
countriesList#
addressType#
Optional fields#
Used only in the B2B flow.
In Default.liquid it’s displayed when isB2bCustomer == true and addressType == 'billing'.
Derived from GlobalData.Settings.deliveryZonesEnabled.
When enabled, the country/postal code behavior changes and extra validity checks apply.
inValidAddressMessage, addressErrorMessage, addressUpdateErrorMessage, postalCodesErrorMessage.
Template behavior (Liquid + Alpine)#
Source: Reusables/AddressForm/Default.liquid.Retail: editable forms + saved address selection.
For billing, it shows headQuarterAddress recap (read-only).
For shipping, it still renders radios and uses the shared store for selection.
Edit form fields: ${addressType}-edit-${index}-<field>
New form fields: ${addressType}-new-${index}-<field>
Invoice fields: company-${addressType}-edit-${index}-<field>
Data contract (JS runtime)#
From Reusables/AddressForm/Default.js:initComponent(
addresses,
countriesList,
deliveryZonesEnabled,
addressType,
inValidAddressMessage,
modalTranslationsInvoice,
addressErrorMessage,
addressUpdateErrorMessage,
postalCodesErrorMessage,
isB2bCustomer
)
addresses: normalizedAddresses.map(a => a.address)
branchesNames: normalizedAddresses.map(a => a.name || '')
deliveryZonesEnabled ? normalizedAddresses.map(a => a.isSelectable) : normalizedAddresses.map(() => true)
JavaScript#
Source: Reusables/AddressForm/Default.js.init()#
Sets up the initial address arrays and the billing/shipping synchronization.Initializes shared address/invoice arrays and binds them to $store.sharedAddresses.
Handles retail vs B2B initialization paths.
Triggers checkout address preselection logic (billing first, then shipping).
init#
Alpine lifecycle method that prepares shared address state and wires billing/shipping synchronization.Creates blank “new address” rows via startNewAddress().
In billing mode, it also seeds $store.sharedAddresses.addresses and $store.sharedAddresses.invoices.
In shipping mode, it reads back from $store.sharedAddresses.
Watches $store.sharedAddresses.updateShippingAddresses to keep the two forms in sync.
Reads field values from the DOM and pushes them into $store.sharedAddresses.
Then keeps only the first address and calls saveTemporarilyAddress(0).
Uses $store.sharedAddresses.addresses.
displayAddresses() / displayInvoices()#
These methods decide what list items to show.They hide “blank” placeholder entries depending on:displayAddresses#
Returns the list of addresses that should be rendered as “saved addresses”, hiding temporary/blank rows depending on mode.
displayInvoices#
Returns the invoice rows that should be shown for billing (mirrors how address placeholders are hidden).
startEditing(index) / cancelEditing(index)#
Enables edit mode for a selected address.
Takes snapshots in originalData[index] and originalInvoiceData[index].
Clears errors and, when deliveryZonesEnabled, normalizes country values and triggers onCountryChange(...).
Resets touched/validity state.
startEditing#
Enters edit mode for the selected address, snapshots original values, and preloads delivery-zone selects when enabled.
cancelEditing#
Exits edit mode and restores the address/invoice from snapshots.
saveTemporarilyAddress(index, type = 'edit')#
Validates the address form fields and stores the normalized data.addresses[index]?.editFlag
addresses[index]?.tempType === 'new'
Updates $store.sharedAddresses.validAddresses[index].
Shows a blocking toast (inValidAddressMessage) and resets the current checkout step if the address is not selectable.
saveTemporarilyAddress#
Validates and normalizes the current address form fields.
Writes the result into $store.sharedAddresses (and updates validAddresses when delivery zones are enabled).
saveTemporarilyInvoice(index, type = 'edit')#
Validates invoice fields (billing only), updates $store.sharedAddresses.invoices, and may persist the address/invoice in the profile via updateAddressDataProfile(...).Also flips $store.sharedAddresses.updateShippingAddresses = true to sync shipping.saveTemporarilyInvoice#
Validates and stores invoice fields (billing only), and triggers shipping synchronization when needed.
nextCheckoutStep()#
nextCheckoutStep#
Builds an updated checkout payload from $store.checkout.data + $store.sharedAddresses and calls:Persists the selected billing/shipping address (and invoice mode when billing) into checkout.
Calls servicesreusabledefault.updateCheckout(updatedCheckoutData, stepName) for the active step.
getCheckoutSelectedId#
Reads the currently selected billing/shipping address id from $store.checkout.data.
preselectFromCheckout#
Uses the checkout-selected id to set selectedAddressIndex and mark the matching saved address radio as selected.
Scrolls the current checkout section to the top (used after toggling/editing flows).
startNewAddress#
Appends a blank “new address” row (and related bookkeeping) so the user can add another address.
Opens/closes the “new address” form UI and refreshes derived lists accordingly.
validateInvoice#
Handles invoice-option validation/confirmation for billing (may open a modal and controls invoice edit mode).
Other helper methods#
The reusable’s JS also includes a few helper methods that are used to coordinate UI state and shared checkout selection:Behavior differs per addressType:Sets documentType to Invoice vs Receipt based on invoiceOption.
If shipping is already valid, it updates the shipping step too.
Always updates checkout with billingShippingAddress as the step.
updateAddressDataProfile(addressId, type = 'edit')#
Persists an address to the user profile (create or update) using:updateAddressDataProfile#
Persists the address (and invoice when applicable) to the user profile via createUserAddress / updateUserAddress.
validateField(field)#
Client validation rules for required fields, email format, and select validation.For SELECT required fields, behavior depends on delivery zones:validateField#
Validates a single field and updates the errors map and CSS validity classes.
onCountryChange(index, showToast = false)#
Delivery-zone-specific behavior:onCountryChange#
Syncs country / countryCode, fetches postal codes via servicesreusabledefault.fetchPostalCodes, and validates the postal-code selection.
Invoice helpers#
Helpers that toggle invoice mode, track invoice edit state, and determine whether the invoice form is modified.
Global Alpine stores#
updateAddress(index, data, ...)
updateInvoice(index, data)
updateValidAddress(index, boolean)
validateStep(stepName, ...)
add(...), remove(...), removeAll()
Services / API calls#
From Reusables/AddressForm/Default.js:servicesreusabledefault.updateCheckout(updatedCheckoutData, stepName)
servicesreusabledefault.createUserAddress(dataToSend)
servicesreusabledefault.updateUserAddress(dataToSend, addressId)
servicesreusabledefault.fetchPostalCodes(countryCode)
Dependencies#
Notes#
Checkout update (updateCheckout) – used to progress checkout steps.
Profile persistence (createUserAddress / updateUserAddress) – used when storing/updating addresses.
Examples#
{% render 'Reusables\\AddressForm\\Default',
addressType: 'billing',
addresses: model.addresses,
countriesList: model.countriesList,
headQuarterAddress: GlobalData.Settings.IsB2bCustomer ? model.checkout.billingAddress : nil
%}
{% render 'Reusables\\AddressForm\\Default',
addressType: 'shipping',
addresses: model.addresses,
countriesList: model.countriesList,
headQuarterAddress: GlobalData.Settings.IsB2bCustomer ? model.checkout.billingAddress : nil
%}
Modified at 2026-05-12 08:43:59