Home
Wiki
Home
Wiki
  1. 3. Reusables
  • Back to home
  • 1. Themes
  • Vs Code
    • Getting Started
  • Kitchenware
    • Layout
      • New Layout
      • Legacy Layout
    • Components
      • Announcement
      • Banner Carousel
      • Banner With Products Carousel
      • Blog Category List
      • Blog List
      • Brand List
      • Brands Carousel
      • Breadcrumb
      • Call To Action
      • Cart
      • Categories List
      • Change Password
      • Checkout
      • Cookie Manager
      • Filter list
      • Footer
      • Forgot Password
      • Form
      • Hero Carousel
      • Icon Block
      • Invitation
      • Last Visited Products
      • Layout
      • Login
      • Map
      • Nav Bar
      • Offer
      • Product Attachments
      • Product Attributes
      • Product Documentation
      • Product Expected
      • Product Modal
      • Products Block
      • Products Carousel
      • Product Single
      • Profile
      • Quote
      • Register
      • Related Products
      • Search
      • Stores
      • Subscribe Newsletter
      • Text with Image
      • Top Bar
      • Video
    • Reusables
      • Getting Started
    • Assets
      • Getting Started
    • SDK
      • Products
        • _findProductsByCategory
        • _findProductsByIds
        • _findProductsByTitle
        • _findProductsByFilter
        • _findProductsByCriteria
        • _findProductsAndCalculate
        • _findProductsThenCalculate
        • _getProductAttributeSet
        • _setLastVisited
      • Categories
        • _findCategoryTreeById
        • _findCategoriesByIds
        • _findCategoryByAlias
        • _findCategoryTreeByAlias
        • _getCategoryContent
      • Collections
        • _getCollectionContent
        • _findCollectionsByIds
        • _findCollectionsByIdsThenCalculate
      • Brands
        • _getBrandContent
        • _findBrandsByIds
      • Cart
        • _addToCartMulti
        • _addToCart
        • _setCart
        • _clearCart
        • _setCartListener
        • _removeFromCart
        • _calculateCart
      • Checkout
        • _startCheckout
        • _updateCheckout
        • _completeCheckout
      • Shopping Lists
        • _getShoppingLists
        • _updateShoppingList
        • _createShoppingList
        • _deleteShoppingList
        • _getShoppingListByAlias
      • Navigation
        • _getFooterMenu
        • _getHeaderMenu
      • Users
        • _getUserById
      • Utils
        • _calculateCurrency
        • _getCurrencySymbol
        • _getCulture
        • _subscribeToNewsletter
        • _findUnitsByIds
  • Noir
    • 0. Introduction
    • 6. FAQ
    • 1. Structure
      • _Overview
      • LayoutA.liquid
      • ComponentsList.liquid
      • Metas.liquid
      • CssVariables.liquid
      • Json.liquid
      • GoogleTagManager.liquid
      • StagingButton.liquid
    • 2. Components
      • _Overview
      • Announcement
      • BannerCarousel
      • BlogCategoryList
      • BlogList
      • BrandList
      • Breadcrumb
      • Cart
      • CategoriesList
      • ChangePassword
      • Checkout
      • CookieManager
      • FilterList
      • Footer
      • ForgotPassword
      • Form
      • IconBlock
      • Invitation
      • LastVisitedProducts
      • Login
      • Map
      • NavBar
      • ProductAttachments
      • ProductAttributes
      • ProductComparison
      • ProductDocumentation
      • ProductMixList
      • ProductsBlock
      • ProductsCarousel
      • ProductSingle
      • Profile
      • Quote
      • Register
      • RelatedProducts
      • SingleBlog
      • Stores
      • TextWithImage
      • ThankYouPage
      • TopBar
      • Wishlist
    • 3. Reusables
      • _Overview
      • Addresses
      • AddressForm
      • AnnouncementModal
      • BackToTop
      • BillingRetail
      • Company
      • General
      • Login
      • LoginModal
      • MonthlyTransactions
      • Orders
      • Payment
      • ProductAttachments
      • ProductAttributes
      • ProductComparisonButton
      • ProductComparisonFloatingButton
      • ProductGridItem
      • ProductListItem
      • ProductModal
      • ProfileInfo
      • PromptModal
      • Quotes
      • Register
      • Services
      • Shipping
      • ShoppingLists
      • ShoppingListsButton
      • ShoppingListsNavbar
      • Toast
      • Transactions
      • Users
      • VariantContent
      • WishlistButton
    • 4. Assets
      • Fonts
      • Images
      • Templates
      • Javascript
        • _Overview
        • theme.js
      • Css - Scss
        • _Overview
        • ThemeClasses
    • 5. SDK
      • _Overview
      • LiquidGlobals
      • ServicesSDK
  1. 3. Reusables

Addresses

Purpose

  • 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)

Delivery zones mode

The reusable reads GlobalData.Settings.deliveryZonesEnabled.

When enabled:

  • country input is a <select> bound to addresses[selectedAddressIndex].countryCode
  • postal code is a <select> with options loaded for the selected country
  • per-address validity is taken from each input item’s isValid
  • invalid addresses can trigger a toast flow (only on /profile/addresses route)

When disabled:

  • country and postal code are plain text inputs

Data contract (JS runtime)

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:

initComponent(addresses, countriesList, deliveryZonesEnabled, inValidAddressTitle, inValidAddressMessage, inValidAddressMessageButton, removeAddressErrorMessage, postalCodesErrorMessage)

  • Factory that normalizes the input addresses and returns the Alpine component state + methods.

    • array -> as-is
    • single object -> wrapped into [addresses]
    • falsy -> []
    • this.addresses = normalizedAddresses.map(a => a.address)
    • invoiceOption[i] = strings/non-empty check on invoice.companyName
  • Initializes validity:

    • if delivery zones enabled -> storedAddressesValidity = normalizedAddresses.map(a => a.isValid)
    • else -> all true

init

  • Runs 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:

    • If editing, calls cancelEditing(selectedAddressIndex, true) and exits edit mode.

clearInvalidAddressFields

startNewAddress

scrollToTop

startEditing(index)

  • Enters edit mode for an existing address, or prepares the draft last-row for creating a new one.

Key behaviors

  • Clears toasts.
  • If index == null, targets the last draft row; if it’s no longer empty, it calls startNewAddress() then uses the new last index.
  • Stores rollback snapshots:
    • originalData[index] = { address: {...}, invoice: {...} }
    • originalInvoiceOption[index] = invoiceOption[index]
  • Resets per-index UI state (touched, openPopup, validity booleans).
  • If editing an existing address:
    • delivery-zones mode -> calls onCountryChange(index) (fetches postal codes + validates selects)
    • else -> validates all address fields on next tick.

cancelEditing(index, skipToastReset = false)

  • Exits edit mode and restores the address/invoice from snapshots.

Key behaviors

  • Restores addresses[index], invoices[index], and invoiceOption[index] from originalData/originalInvoiceOption.
  • If skipToastReset is false, sets Alpine.store('sharedAddresses').runToast = true (so delivery-zones warnings can be recalculated).
  • Scrolls to top via scrollToTop().

saveAddress(index, errorMessage) (async)

  • Validates the form and creates or updates the user address via the SDK.

Key behaviors

  • Collects:
    • address fields: [id^="profile-address-edit-"]
    • invoice fields: [id^="company-profile-address-edit-"]
  • Validates every field via validateField(...) and updates errors.
  • Builds request payload:
    • address: this.addressData
    • invoice: invoiceOption[index] ? this.invoiceData : null
    • isValid: true
  • Create vs update decision:
    • missing/empty addresses[index].id -> createUserAddress(...) (and removes address.id from payload)
    • else -> updateUserAddress(payload, id)
  • On failure (non-2xx or success === false) shows a toast and triggers sharedAddresses.runToast = true.
  • On success:
    • updates addresses[index] and invoices[index]
    • sets validity flags to true
    • calls moveEmptyAddressToEnd() to keep the blank draft last
    • scrolls to top and triggers sharedAddresses.runToast = true

confirmAddressDeletion(index, modalTranslations)

  • Opens a confirmation modal via Alpine.store('modal') and delegates deletion to removeAddress(index).

removeAddress(index) (async)

  • Deletes an address via servicesreusabledefault.deleteUserAddress(id) and removes it from all parallel arrays.

Key behaviors

  • Sets loading flags (isRemoving[index], isAddressStateChanging) and disables interactions.

Array ordering helpers

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.

moveItem

moveToFront

Other helper/method names:

  • action(...) is a generic handler used by the Alpine state.
  • On failure shows removeAddressErrorMessage toast and restores loading flags.
  • On success calls removeAddressData(index).

setAddressAsPrimary(index, setAddressAsPrimaryErrorMessage) (async)

  • Sets an address as primary in the backend and then reorders all arrays so the selected item becomes index 0.

Key behaviors

  • Calls servicesreusabledefault.setUserAddressAsPrimary(id).
  • On success unshifts the selected item to index 0 across:
    • addresses, invoices, storedAddressesValidity, invoiceOption, etc.

validateField(field)

  • Implements field validation and writes error state to errors[field.name].

Key behaviors

  • Required fields:
    • input: invalid when empty
    • select: invalid when no value / first option
  • Email validation:
    • basic regex check -> invalidEmail
  • Adds/removes UI classes:
    • invalid / valid on inputs and .select-wrap
    • toggles .icon-state classes (ic-warning / ic-check-circle-fill)

handleInput(event, index) / handleInvoiceInput(event, index)

  • Marks the row as touched, validates the field, and recomputes addressValidity[index] or invoiceValidity[index].

isAddressModified(index)

  • Computes whether the Save button should be enabled.

Key behaviors

  • Compares current form buffers (addressData, invoiceData) with persisted arrays (addresses[index], invoices[index]).
  • Also detects invoice toggle changes (invoiceOption[index] vs originalInvoiceOption[index]).

onCountryChange(index) (async)

  • Delivery-zones mode helper: syncs countryCode <-> country and fetches postal codes.

Key behaviors

  • Resets postal state when no country is selected.
  • Sets errors[postalCode] = 'wait' while fetching.
  • Calls servicesreusabledefault.fetchPostalCodes(countryCode).
  • Maps errors to:
    • errorFetchingPostalCodes (unexpected response shape)
    • notFound (failed request / no data)
  • On success:
    • stores this.postalCodes
    • tries to keep a saved postal code from originalData[index] if still available
    • revalidates all address fields on next tick.

controlToastVisibility

resetToasts

  • Delivery-zones mode: shows/removes per-address invalid toasts, and wires toast “scroll to card” button.

Key behaviors

  • Uses invalidToastId[index] to avoid duplicates.
  • Builds toast extra HTML with a button that scrolls to .address-wrap-box at the same index.

moveEmptyAddressToEnd

  • Ensures the completely empty address object is always the last item.

Key behaviors

  • Finds the first address where all values are "" and moves it to the end across all parallel arrays.

Global Alpine stores

The reusable syncs shared state to Alpine.store('sharedAddresses'):

  • .addresses
  • .validAddresses
  • .invoices

It also uses:

  • .runToast flag to (re)trigger delivery-zones invalid-address toast logic.

It uses Alpine.store('toast') for errors and delivery-zone warnings.

Services / API calls

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.

Dependencies

  • Alpine.js (component state + watchers)
  • Alpine.store('toast')
  • Alpine.store('sharedAddresses')
  • servicesreusabledefault.createUserAddress(...)
  • servicesreusabledefault.updateUserAddress(...)

Notes

  • The “Add new address” card button is rendered with the disabled attribute in Liquid.
  • In list view the template uses addresses.slice(0, -1); the last address is expected to be the “draft” row.
  • Delivery zones mode uses countryCode + dynamically loaded postal codes; if input data has countryCode empty, the selects may be disabled until the user chooses a country.

Examples

Storefront model example (from Profile)

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
    }
  ]
}
Modified at 2026-05-12 08:31:00
Previous
_Overview
Next
AddressForm
Built with