Allowing customers to edit their own custom fields

This article contains information about

  • Enabling customers to edit the information stored about them in custom fields
  • Enabling customers to edit the information stored in their orders' custom fields
  • Creating a customer account with or without custom fields
  • How to set up HTML element for different ACF field types, including file upload
  • How to handle repeatable fields
  • How to use the ACF proxy for form submission

This is an advanced tutorial requiring some knowledge of both HTML and Javascript. The examples also make basic use of jQuery for connecting the form and the proxy script

Enabling customers to edit the information stored about them in custom fields

To create a form for editing an existing customer's custom fields, you  must include a hidden field with the name "customer[id]" and set its value to that of the logged-in customer via {{ customer.id }}

<form id="metafields_form">  

  <input type="hidden" name="customer[id]" value="{{ customer.id }}" /> 

</form>

Each form element linked to a custom field must be named "metafield[ namespace.fieldname]". The namespace must be included even if you just use the default "accentuate".

<form id="metafields_form">  

  <input type="hidden" name="customer[id]" value="{{ customer.id }}" /> 
  <input type="text" name="metafield[accentuate.nick_name]" value="{{ customer.metafields.accentuate.nick_name }}" placeholder="Nickname" /> 
  <input type="text" name="metafield[accentuate.birthday]" value="{{ customer.metafields.accentuate.birthday }}" placeholder="Your birthday" /> 

</form>

In addition to custom fields, you can provide these fields as well to have them updated in Shopify:

  • customer[email]
  • customer[first_name]
  • customer[last_name]
  • customer[accepts_marketing] (defaults to false*)
  • customer[phone]
  • customer[tags]
  • customer[address.property] (where property is one of the valid address properties listed in the Shopify docs). When referencing an [address.property] you must always include the an address id via [address.id]
<form id="metafields_form">  

  <input type="hidden" name="customer[id]" value="{{ customer.id }}" /> 
  <input type="text" name="metafield[accentuate.nick_name]" value="{{ customer.metafields.accentuate.nick_name }}" placeholder="Nickname" /> 
  <input type="text" name="metafield[accentuate.birthday]" value="{{ customer.metafields.accentuate.birthday }}" placeholder="Your birthday" /> 

  <!-- Fields belonging to the customer object in Shopify --> 
  <input type="text" name="customer[first_name]" value="{{ customer.first_name }}" placeholder="First name" /> 
  <input type="text" name="customer[last_name]" value="{{ customer.last_name }}" placeholder="Last name" /> 
  <input type="checkbox" name="customer[accepts_marketing]" {% if customer.accepts_marketing %} checked {% endif %}/> Can't wait, send me stuff  

</form>

Custom fields referenced via the form elements' "name" attribute must be configured in advance for the customer scope in ACF before they can be updated

Enabling customers to edit the information stored in their orders' custom fields

The procedure here is exactly the same as above for customer custom fields, except that you must use a hidden field with the name "order[id]" and set its value to that of the relevant order via {{ order.id }}.

Having an "order[id]" field present in the form switches the scope of any provided custom fields to orders.

<form id="metafields_form">  

  <input type="hidden" name="order[id]" value="{{ order.id }}" />  
  <input type="text" name="metafield[accentuate.nick_name]" value="{{ order.metafields.accentuate.po_reference }}" placeholder="Purchase order reference" /> 

</form>

Custom fields referenced via the form elements' "name" attribute must be configured in advance for the order scope in ACF before they can be updated

Creating a customer account with or without custom fields

ACF can create new customer accounts on form submission AND update any provided custom fields after the fact.

To do this, set the value of "customer[id]" to an empty string and include these required fields as well:

  1. customer[email]
  2. one or both of customer[first_name] and customer[last_name]
<form id="metafields_form">  

  <input type="hidden" name="customer[id]" value="" /> 
  <input type="text" name="customer[email]" value="{{ customer.email }}" placeholder="Email" /> 
  <input type="text" name="customer[first_name]" value="{{ customer.first_name }}" placeholder="First name" /> 
  <input type="text" name="customer[last_name]" value="{{ customer.last_name }}" placeholder="Last name" /> 

  <input type="checkbox" name="metafield[accentuate.check]" {% if customer.metafields.accentuate.check %} checked {% endif %}/> Yes, send me a free gift on my birthday  

</form>

Optional fields for customer creation include:

  • customer[verified_email] (default to false*)
  • customer[send_email_invite] (defaults to false*)
  • customer[password] and customer[password_confirmation] (both must come together and have matching values)
  • customer[send_email_welcome] (defaults to false*)
  • customer[accepts_marketing] (defaults to false*)
  • customer[phone]
  • customer[tags]

* ACF converts a field value of "true", "yes" or "1" to a boolean "true" and all other values to "false". A checkbox as shown in the example will work just fine

Field types examples

This form example highlights some different ways to provide values for custom fields

Please note:

  • For fields where you need to send multiple values, define them as form "arrays", i.e. "metafield[ namespace.fieldname][ ]" (note the closing [ ]'s)    
  • If you use checkboxes to have your customer select one or more values for a Selection or Tags type field, be sure to include each field's value as a "data-option-value" attribute.
...
  <!-- Text examples --> 
  <input type="text" name="metafield[accentuate.nick_name]" value="{{ customer.metafields.accentuate.nick_name }}" placeholder="Nickname" /> 

  <!-- Checkbox to select on or off for a Checkbox field --> 
  <input type="checkbox" name="metafield[accentuate.check]" {% if customer.metafields.accentuate.check %} checked {% endif %}/> Yes, send me a free gift on my birthday  

  <!-- Checkboxes to select multiple predefined values for a Selection list or Tags field --> 
  <input type="checkbox" name="metafield[accentuate.multiselect][]" data-option-value="value-1" /> Select this to choose value 1 
  <input type="checkbox" name="metafield[accentuate.multiselect][]" data-option-value="value-2" /> Select this to choose value 2  

  <!-- Radio buttons to select a single value for a field (Text field or Selection list) --> 
  <input type="radio" name="metafield[accentuate.color]" value="Red" {% if customer.metafields.accentuate.color == "Red" %} checked {% endif %}/> Red
  <input type="radio" name="metafield[accentuate.color]" value="Blue" {% if customer.metafields.accentuate.color == "Blue" %} checked {% endif %}/> Blue
  <input type="radio" name="metafield[accentuate.color]" value="Green" {% if customer.metafields.accentuate.color == "Green" %} checked {% endif %}/> Green

   <!-- Single value selection list -->
  <select name="metafield[accentuate.preferred_color]">
    <option value="Magenta">Magenta color</option>
    <option value="Plum">Plum color</option>
    <option value="Beige">Beige color</option>
  </select>

   <!-- Multiple value selection list -->
  <select multiple name="metafield[accentuate.preferred_color][]">
    <option value="Magenta">Magenta color</option>
    <option value="Plum">Plum color</option>
    <option value="Beige">Beige color</option>
  </select>

  <!-- File upload example --> 
  <input type="file" name="metafield[accentuate.avatar]" accept="image/png, image/jpeg" />  <input type="submit"/> 

</form>

How to get the defined values from a Selection type field

Now, you may be wondering how you are going to keep your form's selection lists in sync with the allowed values defined for the field. Getting to those values is possible using special ACF metafields. Please see related article on Liquid access to field definitions below. Armed with this information, you can loop over the defined "preferred_color" values in Liquid like this:

...
  {% assign field = shop.metafields.acf_settings.product | where: "name", "preferred_color" | first %}
  {% assign values = field.value | split: '|' %}
  <select name="metafield[accentuate.preferred_color]">
  {% for value in values %}
    <option value="{{ value }}">{{ value }}</option>
  {% endfor %}
  </select>
...

Repeatable fields handling

If you need your form input to  add values to a repeatable field (e.g. register a newly bought product to an existing list of bought products) rather than overwriting the value, add this option to your form:

... 
  <input type="hidden" name="options.append" value="true" />
...

Similarly, if you need your form input to  remove values from a repeatable field (e.g. remove an element from a list) rather than overwriting the value, add this option to your form:

...
  <input type="hidden" name="options.remove" value="true" />
...

These options apply to all repeatable fields in your form. If you need to add or remove values for some fields, but not for others, consider creating two different forms

Don't use options.remove for a form with repeatable fields that "belong" together (like "name" and "age" for a customer) - this can cause undesired results

Submitting the form

In your theme.liquid file, near the closing </body> tag, be sure to include the ACF proxy script and one line of code to bind your form to the proxy. 

  ...
  {% if template contains 'customers/account' or template contains 'customers/order' %}  
  <script src="https://app.accentuate.io/dist/proxy.js"></script> 
  <script> Accentuate(jQuery("#metafields_form")); </script> 
  {% endif %}  
</body>

The Accentuate proxy script depends on jQuery to work, so make sure jQuery is loaded separately before the proxy script is invoked

Adjust the above selector of the form - jQuery("#metafields_form") - to match your actual code. Accentuate will automatically handle the form submission and determine the endpoint target for the form submission - therefore you don't need to define METHOD or ACTION attributes on the form itself (and they will be ignored, if present)

The "Accentuate()" function takes two optional parameters:

The first optional parameter is a callback function where you can take action after updates have been submitted to ACF. This callback is served with a "data" object containing the below properties, which you can use for inspecting whether the update succeeded or not and which:

data.status will contain either "OK" or "ERROR" 
data.message will contain a related message with details
data.errors in case of status = "ERROR", this will contain an object structure highlighting which parts of the request failed, e.g. { email: ["is already taken"], phone: ["is invalid"] }

Here is a simple example, where the results are just logged to the browser's console:

<script> 
  Accentuate(jQuery("#metafields_form"), function(data) { console.log(data.status, data.message); }); 
</script>

If your form contains file uploads (input type="file"), the file uploads are handled separately from the rest of the form's fields. This is handled automatically by the proxy script but you will experience that the callback function (if defined) is called twice, once for any files and once for all other fields

The second optional parameter is a callback function where you can validate your form prior to submitting it to ACF's handling. If you return false from the validation callback, the form will not be submitted. Any other value than false will allow for form submission.

Example of a validation function that checks for a non-zero length of a field with an id of "myfield":

<script>
  Accentuate(jQuery("#metafields_form"), null, function() { if ($("#myfield").val().length == 0) return false });
</script>
Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us