Shopify themes, liquid, logos, and UX
Hi There,
I'm currently switching my shop to the new Dawn theme and my original theme was based on Debut. It seems that the Dawn theme uses an Ajax cart to submit an add to cart action, but I'm unable to add the chosen line item properties. Can anybody help me with the code I need to change, or add to the product-form.js file to achieve this?
Thank you in advance.
Solved! Go to the solution
This is an accepted solution.
Hi,
To add the line in the Dawn theme, you can follow these steps below
1. Go to file sections/main-product.liquid, find <div class="product-form__input product-form__quantity"> and insert the following under that line
<div>
<style>
.product__info-container--sticky {
position: static !important;
}
</style>
<label class="form__label" for="pinfel-select">Pinfel pack</label>
<div class="select" id="pinfel-select">
<select required class="select__select required" id="pinfel">
<option class="fruta" value="Fruta">{{ 'products.product.fruit' | t }}</option>
<option class="jogos" value="Jogos">{{ 'products.product.games' | t }}</option>
<option class="rock" value="Rock">Rock</option>
<option class="espaco" value="Espaço">{{ 'products.product.space' | t }}</option>
</select>
{% render 'icon-caret' %}
</div>
</div>
</div>
2. Go to the file assets/product-form.js, find const body = JSON.stringify({ and add the following code under that line
properties: { Pinfel: $('#pinfel-select select').val() },
Hi @LuckyNigam ,
Thanks for your rapid reply, but I'm looking for a solution that we can share here with others who face the same issue, so if you could post a fix for the Dawn theme to handle line item properties, it would be much appreciated.
Thanks in advance!
Hi,
Could you share your change on the Debut theme? So we can understand your idea closer.
Hi @LamQSolutions ,
Here's the line item property I have added to the product template which works in the old Debut theme, but doesn't work in the new Dawn theme, because the old Debut theme didn't use Ajax and the new Dawn theme has a complete new way of handling the add to cart functionality:
<label class="form__label" for="pinfel-select">Pinfel pack</label>
<div class="select">
<select required class="select__select required" id="pinfel" name="properties[Pinfel]">
<option class="fruta" value="Fruta">{{ 'products.product.fruit' | t }}</option>
<option class="jogos" value="Jogos">{{ 'products.product.games' | t }}</option>
<option class="rock" value="Rock">Rock</option>
<option class="espaco" value="Espaço">{{ 'products.product.space' | t }}</option>
</select>
{% render 'icon-caret' %}
</div>
Thanks in advance!
This is an accepted solution.
Hi,
To add the line in the Dawn theme, you can follow these steps below
1. Go to file sections/main-product.liquid, find <div class="product-form__input product-form__quantity"> and insert the following under that line
<div>
<style>
.product__info-container--sticky {
position: static !important;
}
</style>
<label class="form__label" for="pinfel-select">Pinfel pack</label>
<div class="select" id="pinfel-select">
<select required class="select__select required" id="pinfel">
<option class="fruta" value="Fruta">{{ 'products.product.fruit' | t }}</option>
<option class="jogos" value="Jogos">{{ 'products.product.games' | t }}</option>
<option class="rock" value="Rock">Rock</option>
<option class="espaco" value="Espaço">{{ 'products.product.space' | t }}</option>
</select>
{% render 'icon-caret' %}
</div>
</div>
</div>
2. Go to the file assets/product-form.js, find const body = JSON.stringify({ and add the following code under that line
properties: { Pinfel: $('#pinfel-select select').val() },
Hi @LamQSolutions ,
I had to make a few small adjustments to make it work, but you directed me in the right direction and it works now! Thanks!
Hi @gunnar-1
Glad to hear that the problem was solved! Could you please share your change so it can help other people?
Hi @LamQSolutions ,
Ok, here's my working solution, it's not pretty and I'm sure it could be done better, but it works and hopefully people that need it can adjust it to their needs:
main-product.liquid:
{%- if product.tags contains 'pinfel' -%}
<div class="product-form__input product-form__input--dropdown product-form__pinfel">
<label class="form__label" for="pinfel-select">Pinfel pack</label>
<div class="select" id="pinfel-select">
<select required class="select__select required" id="pinfel" data-type="pinfel-select">
<option class="fruta" value="Fruta">{{ 'products.product.fruit' | t }}</option>
<option class="jogos" value="Jogos">{{ 'products.product.games' | t }}</option>
<option class="rock" value="Rock">Rock</option>
<option class="espaco" value="Espaço">{{ 'products.product.space' | t }}</option>
</select>
{% render 'icon-caret' %}
</div>
</div>
{%- else -%}
<span value="0" data-type="pinfel-select"></span>
{%- endif -%}
product-form.js:
onSubmitHandler(evt) {
evt.preventDefault();
this.itemProperty = document.querySelector('[data-type="pinfel-select"]');
this.cartNotification.setActiveElement(document.activeElement);
const submitButton = this.querySelector('[type="submit"]');
submitButton.setAttribute('disabled', true);
submitButton.classList.add('loading');
const body = JSON.stringify({
properties: { Pinfel: this.itemProperty.value },
...JSON.parse(serializeForm(this.form)),
sections: this.cartNotification.getSectionsToRender().map((section) => section.id),
sections_url: window.location.pathname
});
fetch(`${routes.cart_add_url}`, { ...fetchConfig('javascript'), body })
.then((response) => response.json())
.then((parsedState) => {
this.cartNotification.renderContents(parsedState);
})
.catch((e) => {
console.error(e);
})
.finally(() => {
submitButton.classList.remove('loading');
submitButton.removeAttribute('disabled');
});
}
Thanks for sharing!
This solution works fine but requires an update to product-form.js
Does anyone know how line item properties can be used by theme app extensions? In other words, how can a section on the product page contribute to the line item properties without modifying the theme files?
Thanks!
I second this.
While I was launching a preorder app, the dawn theme came out, and of course upon testing, the add-to-cart js literately doesn't support lineitem properties in the form.
Which means that any merchant using this theme can not have line-item properties added to their cart forms without customizing a main js asset file. Is this going to be fixed via Shopify, or is it what it is?
This is great and very helpful, I was able to add your example to my store with some tweaks using my select options.
Now, does anyone how how to add more than 1 option? I can't get them to both show up in the cart.
What is the reason why adding attributes to the dawn 2.0 theme in this way does not take effect?
It would require one of these pull requests to be merged:
If you're working on a single store, you can amend the theme file global.js with one of these code changes
I found that the answer they gave was to take and modify the global.js file. Can you not modify this file or add attributes, because the original intention of the theme of 2.0 is not to operate on the theme of the customer's store through application extension
If Pull request #391 (or something similar) is to be implemented, would that retroactively update every stores dawn theme, or, will this be a caveat for any merchant who would like to add lineitem properties in their forms going forward?
No idea, sorry. According to the docs (https://help.shopify.com/en/manual/online-store/themes/managing-themes/updating-themes) it might depend on whether there has been changes made to the theme.
So all I had to do in the end was change 1 line in 'product-form.js' and all my line items work great. Even with different line items on different products.
Under const body = JSON.stringify({
Add: properties: { YourNewField: document.getElementById('field').value }
And here is an example of my line items on the product page (just adds a radio button and a text field):
<div class="custom-preferences-box">
<p><strong>Add-On Details</strong> (required)</p>
<label>Is this an add-on for a current subscription?</label><br />
<input required class="required" type="radio" name="properties[Delivery Type]" value="Current Subscription Add-on" data-type="custom-1"> <span>Yes</span>
<input required class="required" type="radio" name="properties[Delivery Type]" value="One-Time" data-type="custom-2"> <span>No, it's a one-time delivery</span>
<br /><br />
<label for="email-address">If YES, please provide the order # or recipients name:</label>
<input id="email-address" type="text" name="properties[Subscriber Info]" data-type="custom-3" class="required grey-input" />
</div>
Full js file:
class ProductForm extends HTMLElement {
constructor() {
super();
this.form = this.querySelector('form');
this.form.addEventListener('submit', this.onSubmitHandler.bind(this));
this.cartNotification = document.querySelector('cart-notification');
}
onSubmitHandler(evt) {
evt.preventDefault();
this.cartNotification.setActiveElement(document.activeElement);
const submitButton = this.querySelector('[type="submit"]');
submitButton.setAttribute('disabled', true);
submitButton.classList.add('loading');
const body = JSON.stringify({
properties: { YourNewField: document.getElementById('field').value }
...JSON.parse(serializeForm(this.form)),
sections: this.cartNotification.getSectionsToRender().map((section) => section.id),
sections_url: window.location.pathname
});
fetch(`${routes.cart_add_url}`, { ...fetchConfig('javascript'), body })
.then((response) => response.json())
.then((parsedState) => {
this.cartNotification.renderContents(parsedState);
})
.catch((e) => {
console.error(e);
})
.finally(() => {
submitButton.classList.remove('loading');
submitButton.removeAttribute('disabled');
});
}
}
customElements.define('product-form', ProductForm);
Hello! Have not been able to get this product-form.js change to work. Is
properties: { YourNewField: document.getElementById('field').value }
literally what we need to add so are those placeholders that we need to fill in for each property we want to add?
If my property is Example, would I add:
properties: { Example: document.getElementById('Example').value }
Thanks for clarifying.
OK, so I was able to get this to work ONE property. For example:
properties: { 'Hardware Color': document.getElementById('hardware-color').value },
However, I need to do this for MULTIPLE properties on the same product and not all products have the same properties.
Also, it appears to work only for the last property on the page. I actually have to remove from the JS file the other properties or add to cart just causes the cursor to spin.
SO, can anyone direct me on how to do this for multiple properties, please?
Adding THIS is not working:
properties: { 'Hardware Color': document.getElementById('hardware-color').value },
properties: { 'Glass Color': document.getElementById('glass-color').value },
properties: { 'Controller Cutout': document.getElementById('fan-cutout').value },
properties: { 'Install Location': document.getElementById('pull-out-enclosure').value },
properties: { 'Free Space Above': document.getElementById('pull-out-vertical-pos').value },
properties: { '_Estimated Ship Date': document.getElementById('estimated-ship-date').value },
THIS worked:
properties: {
'Hardware Color' : document.getElementById('hardware-color').value,
'Glass Color' : document.getElementById('glass-color').value,
'Controller Cutout' : document.getElementById('fan-cutout').value,
'Install Location' : document.getElementById('pull-out-enclosure').value,
'Free Space Above' : document.getElementById('pull-out-vertical-pos').value,
'_Estimated Ship Date': document.getElementById('estimated-ship-date').value
},
so long as in the template, I made sure that the element is ALWAYS present.
Some products have SOME of the line item properties but not all, so in the liquid block where I had an IF that controlled whether the property was added, I added an ELSE to add the element as Hidden with no value.
Example:{%- else -%}
<input type="hidden" id="pull-out-vertical-pos" type="text" name="" value="">
{%- endif -%}
In other words:
properties: { 'FirstPropName' : document.getElementById('FirstPropID').value,
'SecondPropName' : document.getElementById('SecondPropID').value},
@AlanAdler wrote:so long as in the template, I made sure that the element is ALWAYS present.
...
You would be much better of checking if the element exists in javascript an assignining it when it does.
There's also just more simply serializing any property elements into an object instead of having to create a manual object.
Contact paull.newton+shopifyforum@gmail.com for the solutions you need
Save time & money ,Ask Questions The Smart Way
Problem Solved? ✔Accept and Like solutions to help future merchants
Answers powered by coffee Thank Paul with a ☕ Coffee for more answers or donate to eff.org
Thanks for the reply. It works for me too.
const body = JSON.stringify({
properties: { YourNewField: document.getElementById('testSaveField').value },
...JSON.parse(serializeForm(this.form)),
@acerill wrote:It would require one of these pull requests to be merged:
That change has been merged so update your themes https://github.com/Shopify/dawn/pull/509
Contact paull.newton+shopifyforum@gmail.com for the solutions you need
Save time & money ,Ask Questions The Smart Way
Problem Solved? ✔Accept and Like solutions to help future merchants
Answers powered by coffee Thank Paul with a ☕ Coffee for more answers or donate to eff.org
I used the following and no changes to either product-form.js or global.js files needed
form="product-form-{{ section.id }}"
full example
{% if product.type == 'Coffee' %}
<div id="MilkOptions">
<p>
<strong>Milk Options</strong><br>
<label><input type="radio" name="properties[Milk Option]" value="Almond" form="product-form-{{ section.id }}"> Almond</label>
<label><input type="radio" name="properties[Milk Option]" value="Coconut" form="product-form-{{ section.id }}"> Coconut</label>
<label><input type="radio" name="properties[Milk Option]" value="Macadamia" form="product-form-{{ section.id }}"> Macadamia</label>
<label><input type="radio" name="properties[Milk Option]" value="Oat" form="product-form-{{ section.id }}"> Oat</label>
<label><input type="radio" name="properties[Milk Option]" value="Soy" form="product-form-{{ section.id }}"> Soy</label>
</p>
</div>
{% endif %}
Working page
Hello
Can you tell me what I added to make it appear in cart-notification?
Any notice required isn't working with text input fields?
<p class="line-item-property__field">
<label for="childsName">Child's Name:</label>
<input required="required" class="required" id="childsName" type="text" name="properties[Child's Name]" form="product-form-{{ section.id }}">
</p>
Thank you for answering!
I was able to display it on the cart screen by this method.
However, I don't know how to display the properties in the popup window that appears when I add it to the cart. Also, I don't know how to display it in the auto-reply email.
I misunderstood that it could be implemented on the demo site.
I wish I could display the property in an auto-reply email or a pop-up window without editing product.js.
@akira2 Is your pop-up cart window using the ajax cart template if so, you need to add the following to your ajax code in side the {{#items}} code in here some where {{/items}} .... if it still does not display you need to find the code in your theme.js that build cart items and add properties i.e. (var prodName = cartItems.title or var itemQty = cartItems.quantity) etc.. and add var prodProps = cartItems.properties & add to the item object i.e ( item = { key: cartItem.key, url: cartItem.url, properties: prodProps, } )
{{#properties}}
{{#each this}}
{{#if this}}
<span class="ajaxcart__product-meta2">{{@key}}: {{this}} <br></span>
{{/if}}
{{/each}}
{{/properties}}
I am using Impulse theme but having the same issue. How do I get line items to work on my theme?
@lncuster Could you give a little more context to your issue? Have you tried the above suggestions and they did not work? Can you post some of the code you have tried and where you put i.e. product template, product-form, theme.js etc..
How do you do this with custom fields, like this clothing embroidery example: https://shopify-demo.webwizards.nz/products/short-sleeve-t-shirt?variant=31619799449711
I have a store where some of my products are able to be customised and the customer would need to enter in a name.
this is a great tip! Just installed it and setup a custom field for a product and it works well! thank you so much.
Thank you @Scott-WebWizard this worked out great! Much simpler and less messy 🙂
I was having the same issue as others here: Adding the product to cart didn't work if I had with several forms, and, in case I had different product types/pages that did not require that specific form, I had to include the form code in that page anyway, but hide through css in order to work. With Scott's answer was much simpler and works with all types of forms. My code below for a dropdown menu and a text form:
{% if product.type == 'ProductExample' %}
<div class="product-form__input product-form__input--dropdown">
<p>
<label class="form__label">Choose a card</label>
<select class="select__select" id="Card" name="properties[Card]" form="product-form-{{ section.id }}">
<option value="Love" >Love</option>
<option value="Friends">Friendship</option>
<option value="Blank">Blank</option>
<option value="Custom">Custom</option>
</select>
</p>
</div>
<p class="line-item-property__field ">
<label class="form__label">Custom Message</label>
<input class="select__select" id="Message" type="text" name="properties[Message]" form="product-form-{{ section.id }}">
</p>
{% endif %}
This is far and away the easiest and safest way to do this. You can then just drop custom liquid blocks into your templates and away you go. No mucking around with core files. Thanks Scott!
Hi,
I cn't find const body = JSON.stringify({ line in product-form.js
"2. Go to the file assets/product-form.js, find const body = JSON.stringify({ and add the following code under that line"
As 2024 wraps up, the dropshipping landscape is already shifting towards 2025's trends....
By JasonH Nov 27, 2024Hey Community! It’s time to share some appreciation and celebrate what we have accomplis...
By JasonH Nov 14, 2024In today’s interview, we sat down with @BSS-Commerce to discuss practical strategies f...
By JasonH Nov 13, 2024