In Shopify, you can allow your customers to create logins and see stuff like past purchases and edit their details. There's a handful of templates expressly for this purpose inside the /templates/customers
directory. But if you're using Shopify Theme Kit it's likely that these files are blank by default.
Shopify have some documentation on what's supposed to be in the addresses.liquid
file, but it's ... lacking in detail. There's also some undocumented tricks in Shopify's (abandoned?) Starter Theme
, not all of which I could get to work. This article will explain what customer functionality I have got into a usable state.
Minimum viable product
Here's what I'd want to be able to do, as a customer:
- View a list of my addresses
- Edit old addresses (including selecting one address as the default)
- Add a new address
- Delete even older addresses
View a list of my addresses
Generating a list of a customer's addresses is as straightforward as initiating a for address in customer.addresses
loop. The address
object allows us access to the rest of the data we need.
All of this code is available to download at Finetune Partners Mannequin repo, on Github.
View addresses.liquid on Github
Edit old addresses
When it comes to editing current addresses, Shopify even have this nice table for you to refer to:
Input | Type | Required name attribute |
---|---|---|
First name | text | address[first_name] |
Last name | text | address[last_name] |
Company | text | address[company] |
Address 1 | text | address[address1] |
Address 2 | text | address[address2] |
City | text | address[city] |
Country | select | address[country] |
Province | select | address[province] |
ZIP/Postal Code | text | address[zip] |
Phone Number | tel | address[phone] |
We can generate an edit address form within the for loop we started above.
This all seems pretty straightforward, until you get to Country and Province select boxes. How do you populate them with the right values?
The country select box uses {{ country_option_tags }}
to generate a generic list of countries. This list does not automagically select the correct country for the current address. What about province? Surely that's tied to the country, right? What if the user updates their country and the province needs to reflect this?
Shopify suggests installing a whole JavaScript library to handle this, which seems to do a whole lot more than just populating a select box. Hmm.
There is, however, an easier way.
I couldn't be bothered reading the documentation, so I re-wrote the code from scratch
Inspect the output of those {{ country_option_tags }}
and you'll see it comes with a whole heap of extras. Each option
tag has a data-provinces
attribute, which mostly holds an empty object ([]
), but sometimes holds a lot more.
We need the following to happen:
- Loop through each of the user's addresses, then build an array of values for
Country
andProvince
(this is done by Liquid, before the page loads) - Loop through each
option
tag within each country select box and add aselected="selected
to the option which matches the current address (or remove the attribute, if it doesn't) - Check each country select box, to see if it has any province data hidden on the currently selected
option
- Update the corresponding province select box with this data
- Loop through each province select box and ensure that the correct province is selected by default (or
disable
the select box, if there are no provinces)
Building relationships
Shopify have already established that the countries select box must have a name
attribute of address[country]
and the provinces select box should have a name
attribute of address[province]
. But there might be potentially multiple addresses on the page. How do we establish a relationship between these two select boxes?
Notice that the country select box has a data-provinceid="province{{ forloop.index }}"
attribute. This will generate a string which matches the id
of the following select
box, establishing a means for the JavaScript to jump from one select box to the other (also, this imposes no other markup dependencies).
Using Shopify's attributes
Shopify's province data takes the form of two nested Array-like structures. The inner of these only has two values, which match the value
of the option
tag and the string of text which appears between the opening and closing option
tags.
These two strings are mostly identical, but not always. The script pulls them out by their index position.
Adding a new address
Before the JavaScript portion of addresses.liquid is the form which allows your customers to add a new address. This markup could be moved elsewhere in the file if you prefer, or even to a different template. The only restriction is that it should not be nested inside another form
tag.
Delete an address
Customers can delete addresses using a form
with a hidden input:
<input type="hidden" name="_method" value="delete">
As this form element would need to be a sibling of an existing edit form, this might impact the layout slightly (but you could argue that the delete address button should be corralled off to the side, in case the user hits it accidentally).
It looks like it should be possible to add:
<input type="submit" name="_method" value="delete">
... to the edit address form, but this does not delete the selected address. I suspect this is due to the form_type
hidden input which is added automatically by Shopify, when you use a {% form 'customer_address', address %}
liquid tag.
Note that in Shopify's documentation of this they add a JavaScript confirm box, before allowing the form to submit.
There's nothing to stop you from including this markup within any of the other places in the liquid file where the same for
loop takes place.