Runs when Alpine mounts.
Ensures a draft address exists and syncs state to shared stores.
setting an address as primary
Enters edit mode for an existing address, or prepares the draft last-row for creating a new one.
optional delivery-zones validation flow (postal-code + countryCode-driven)
Exits edit mode and restores the address/invoice from snapshots.
It’s rendered from Components/Profile/Default.liquid on the addresses tab:
Validates the form and creates or updates the user address via the SDK.
addresses: addresses,
Opens a confirmation modal via Alpine.store('modal') and delegates deletion to removeAddress(index).
Deletes an address via servicesreusabledefault.deleteUserAddress(id) and removes it from all parallel arrays.
Sets an address as primary in the backend and then reorders all arrays so the selected item becomes index 0.
isValid: boolean (only used when delivery zones are enabled)
Implements field validation and writes error state to errors[field.name].
if addresses is a single object → wrapped in a single-item array
Marks the row as touched, validates the field, and recomputes addressValidity[index] or invoiceValidity[index].
Computes whether the Save button should be enabled.
Delivery-zones mode helper: syncs countryCode <-> country and fetches postal codes.
Delivery-zones mode: shows/removes per-address invalid toasts, and wires toast “scroll to card” button.
Edit/create form is shown when selectedAddressIndex != null.
Ensures the completely empty address object is always the last item.
an address grid iterating addresses.slice(0, -1) (the last item is expected to be an empty draft)
The reusable reads GlobalData.Settings.deliveryZonesEnabled.
When enabled:
<select> bound to addresses[selectedAddressIndex].countryCode<select> with options loaded for the selected countryisValid/profile/addresses route)When disabled:
The root element binds:
x-data='addressesreusabledefault.initComponent(addresses, countriesList, deliveryZonesEnabled, ...translations)'>
Factory signature (see `Reusables/Addresses/Default.js`):
- `addresses`
- `deliveryZonesEnabled`
- `inValidAddressMessageButton`
- `removeAddressErrorMessage`
- `postalCodesErrorMessage`
### Normalization
- `startAddresses`: original input array
- `addresses`: `normalizedAddresses.map((a) => a.address)`
- `invoices`: `normalizedAddresses.map((a) => a.invoice || {})`
- if delivery zones enabled → `normalizedAddresses.map((a) => a.isValid)`
It also ensures there is an **empty address draft at the end** (used as the “new address” form row).
## JavaScript
### Global object
const addressesreusabledefault = {
}
It’s instantiated from Liquid via:
Factory that normalizes the input addresses and returns the Alpine component state + methods.
[addresses][]this.addresses = normalizedAddresses.map(a => a.address)invoiceOption[i] = strings/non-empty check on invoice.companyNameInitializes validity:
storedAddressesValidity = normalizedAddresses.map(a => a.isValid)trueRuns when Alpine mounts.
Ensures a draft address exists and syncs state to shared stores.
Key behaviors
Publishes to Alpine.store('sharedAddresses'):
sharedAddresses.addresses
sharedAddresses.validAddresses
sharedAddresses.invoices
Calls resetToasts()
If true, calls controlToastVisibility()
Watches Alpine.store('profile').tabChanged:
cancelEditing(selectedAddressIndex, true) and exits edit mode.Key behaviors
index == null, targets the last draft row; if it’s no longer empty, it calls startNewAddress() then uses the new last index.originalData[index] = { address: {...}, invoice: {...} }originalInvoiceOption[index] = invoiceOption[index]touched, openPopup, validity booleans).onCountryChange(index) (fetches postal codes + validates selects)Key behaviors
addresses[index], invoices[index], and invoiceOption[index] from originalData/originalInvoiceOption.skipToastReset is false, sets Alpine.store('sharedAddresses').runToast = true (so delivery-zones warnings can be recalculated).scrollToTop().Key behaviors
[id^="profile-address-edit-"][id^="company-profile-address-edit-"]validateField(...) and updates errors.address: this.addressDatainvoice: invoiceOption[index] ? this.invoiceData : nullisValid: trueaddresses[index].id -> createUserAddress(...) (and removes address.id from payload)updateUserAddress(payload, id)success === false) shows a toast and triggers sharedAddresses.runToast = true.addresses[index] and invoices[index]truemoveEmptyAddressToEnd() to keep the blank draft lastsharedAddresses.runToast = trueAlpine.store('modal') and delegates deletion to removeAddress(index).servicesreusabledefault.deleteUserAddress(id) and removes it from all parallel arrays.Key behaviors
isRemoving[index], isAddressStateChanging) and disables interactions.The JS includes small utilities to keep the “draft” address last and to reorder parallel arrays consistently:
moveItem(array, fromIndex, toIndex) moves an entry within an array.moveToFront(array, index) moves an entry to the start of an array.Other helper/method names:
action(...) is a generic handler used by the Alpine state.removeAddressErrorMessage toast and restores loading flags.removeAddressData(index).Key behaviors
servicesreusabledefault.setUserAddressAsPrimary(id).addresses, invoices, storedAddressesValidity, invoiceOption, etc.errors[field.name].Key behaviors
invalidEmailinvalid / valid on inputs and .select-wrap.icon-state classes (ic-warning / ic-check-circle-fill)addressValidity[index] or invoiceValidity[index].Key behaviors
addressData, invoiceData) with persisted arrays (addresses[index], invoices[index]).invoiceOption[index] vs originalInvoiceOption[index]).countryCode <-> country and fetches postal codes.Key behaviors
errors[postalCode] = 'wait' while fetching.servicesreusabledefault.fetchPostalCodes(countryCode).errorFetchingPostalCodes (unexpected response shape)notFound (failed request / no data)this.postalCodesoriginalData[index] if still availableKey behaviors
invalidToastId[index] to avoid duplicates..address-wrap-box at the same index.Key behaviors
"" and moves it to the end across all parallel arrays.The reusable syncs shared state to Alpine.store('sharedAddresses'):
.addresses.validAddresses.invoicesIt also uses:
.runToast flag to (re)trigger delivery-zones invalid-address toast logic.It uses Alpine.store('toast') for errors and delivery-zone warnings.
This reusable calls SDK wrappers from servicesreusabledefault:
createUserAddress(data) (when address.id is missing/empty)updateUserAddress(data, id)On non-2xx or success === false it shows an error toast.
Alpine.store('toast')Alpine.store('sharedAddresses')servicesreusabledefault.createUserAddress(...)servicesreusabledefault.updateUserAddress(...)disabled attribute in Liquid.addresses.slice(0, -1); the last address is expected to be the “draft” row.countryCode + dynamically loaded postal codes; if input data has countryCode empty, the selects may be disabled until the user chooses a country.This reusable doesn’t receive the full Profile model — only addresses and countriesList.
Sanitized example based on your Profile model:
{
"countriesList": [
{ "code": "COUNTRY_CODE", "name": "Country name" },
{ "code": "...", "name": "..." }
],
"addresses": [
{
"address": {
"id": "ADDRESS_ID",
"firstName": "FIRST_NAME",
"lastName": "LAST_NAME",
"phoneNumber": "PHONE",
"email": "EMAIL",
"address1": "ADDRESS_LINE_1",
"additionalInfo": "",
"label": "",
"city": "CITY",
"postalCode": "POSTAL_CODE",
"state": "STATE",
"country": "COUNTRY_NAME",
"countryCode": "COUNTRY_CODE"
},
"invoice": {
"companyName": "COMPANY_NAME",
"profession": "PROFESSION",
"companyPhoneNumber": "COMPANY_PHONE",
"taxOffice": "TAX_OFFICE",
"tin": "TIN"
},
"isValid": true
},
{
"address": {
"id": "...",
"firstName": "...",
"lastName": "..."
},
"invoice": null,
"isValid": true
}
]
}