I'm new to Shopify and liquid (I have programming background). What i'm currently trying to achieve is adding some unique custom information in a HTML div into the product layout.
My approach: Because I couldn't directly edit the product.liquid file (was generating the same information for all products), I decided to make a hack, so I created liquid files into the snippets by joining the product id + product. The filename would look like "11598995151product.liquid".
Into the product.liquid, I then added the following:
{% if product.description %}<div class="col-sm-6 col-md-6 col-lg-6 col-xl-6 visible-xl" style="display:block !important">
{% assign z = x | append: product.id | append: "product" %}
{% include z %}
</div>{% endif %}
Is this the best practice or can it be done easier ?
I slightly confused, you can't directly update the product.liquid but yet you are adding an include there. So can you or you can't?
Here a few approaches how can you add custom unique information to products.
1) Use an metafeild application such as https://apps.shopify.com/metafields-editor where you can set fields for each product
2) Use shortcodes for example with this snippet https://github.com/culturekings/shopify-shortcodes
3) Create prefixed pages, for example you have a product with a handle test-1, and you will have a page with a handle product-test-1. You perform a check for each product if there is such a page.
4) Similiar to 3 but use a global navigation that will point to page, so that you don't have to prefix each one.
5) Use sections. Create blocks with a product field and a richtext field (or the information you need) and set the product and information for each product as blocks. Then on the product page you check if this product handle is equal to any of the block product field.
All of the above examples require you to modify the product.liquid page if you like for it to be unique for each product.
Related
I'm working on a Shopify store that wants engraving added to their products for a $25 up-charge. I have set it up so that a checkbox on the product page triggers an engraving dropdown where the user can customize the engraving (type style, text, and symbols) and all of those values are passed as line item properties. The checkbox also adds an additional engraving product to the cart that costs $25 to account for the up-charge and has the same exact line item properties. I realize that both products having the same exact line item properties is a perfect way for them to be linked to ensure that if the original product is removed from the cart, the linked engraving product will be removed as well. But I can't for the life of me figure out the way to accomplish this.
I wrote this Liquid code which effectively removes the engraving product from the cart if it is the only item in the cart.
{% if cart.item_count == 1 %}
{% for item in cart.items %}
{% if item.title == 'Custom Engraving' %}
<script>
$.post('/cart/change.js', { quantity: 0, id: {{ item.id }} });
$("#cart_form").submit();
</script>
{% endif %}
{% endfor %}
{% elsif cart.item_count > 1 %}
Please help with the code that supposed to go in here
{% endif %}
But what if there are 2 engraved jewelry products in the cart? If a user removes one of them, they will be left with 2 $25 engraving products and only one jewlery product. I need to automatically remove the associated engraving product for any jewelry product that is removed from the cart. Please help!
Note: A user seems to have a way to do this based on a couple forum answers I found. This one (https://ecommerce.shopify.com/c/ecommerce-design/t/conditional-logic-for-variants-based-on-line-item-properties-264417) he says " In the cart itself, the only thing you need to do is ensure that if the product itself is removed, you also remove the customization product. No biggy. That is a simple pattern and you can jazz it up too. Group a product with a customization through the properties for example, ensuring the two are "glued" together." That's exactly what I need! But unfortunately he doesn't share the simple pattern he's speaking of. He seems to have answered it on stack overflow as well (Chang the product price when custom checkbox is selected in Shopify) and again doesn't reveal the simple pattern he refers to.
Thanks in advance!
Short Answer:
Make the customization products different by adding a hidden property to make them unique.
Longer Answer:
When adding the product to the cart, generate a unique value - using the current date/time (via Date.now()) is usually a good choice. When adding the product to the cart, make sure this property is added as well (probably via hidden input).
Make sure that the name of extra property is prepended with an underscore - so something like '_uniqueID' - Shopify's checkout page knows to hide any line-item properties that have an underscore as their first character
Finally, make sure your theme knows to not display underscored properties in the cart. My favourite way to do this is by adding the following line at the beginning of your properties-printing loop (usually written as 'for p in item.properties')
{% if p.first.first == '_' %}{% continue %}{% endif %}
Now you have a unique ID field as a line-item property that only you can see, and can use that field to synchronize quantities, etc. Huzzah!
I'm new in Shopify and looking for suggestions to implement a specific size chart to add product page. Adding some code in sections->product-template.liquid to get size chart using this code to filter with the product.type
{% if product.type == 'mens' %}
// put your tank top sizing chart here
{{ pages.mens-size-chart.content }}
{% endif %}
{% if product.type == 'women' %}
// put your tank top sizing chart here
{{ pages.women-size-chart.content }}
{% endif %}
This work but, I don't want to filter with the product.type .There is any way to add some dropdown in admin new product panel and get this filed to show size chart without adding any app its possible.
Here is the step by step documentation to add size chart to product pages.
Shopify create size chart
I can think of at least 5 approaches you can use in this case.
Section
Create a section for the product with blocks.
Each block will have the following fields:
product field, where you will select the product
page field, where you will select the page you want
Then loop the blocks and look if the product field is equal to the product handle and use that page
Link List
Create a link list with products.
The title will be the page handle and the url will be the product.
You loop each link in the navigation and check if it's url is equal to the product handle. If yes, you use it's title to get the page content.
Prefix pages
Since you are creating pages based on the type, you can prefix them. So if the type is "mens" the page can be called product_mens and you will get the page based on the product type with the product_ prefix.
Settings
You can create a textarea with a similar syntax:
product-handle|page-handle
product-handle|page-handle
You use the filter newline_to_br, split the result by br, loop each item and split it by | and you got the product-handle and the page handle that you can check.
Tags
You can use prefixed tags, that can target the page. The same as the prefixed pages, here you create a tag with the page-handle and add some kind of prefix to it. Then you loop all of the tags and check if there is a page with any of the prefixed tags.
we have a simple Shopify store using the free "Meta Fields Editor" to add a simple custom field integer value to each of the products. Some products will have a meta value of 3, others 2 or 1. Many will have no meta value at all. We would like to be able to display the products in search results, by the meta field value entered for each product in descending order (weighted ordering). However, despite the ability to set meta values and display them in search results and collections pages, Shopify does not make it easy to Order or Sort products by a custom meta field. In the search results page I am able to call the custom meta value using
{{item.metafields.important.important}}
So far I have tried to order the products using this method
{% assign products = collection.products | sort:
'item.metafields.important.important' %}
{% for product in products %}
{{ product.title }}
{% endfor %}
But this results in null results.
Any insight anyone can give me in how to display products in order of a custom field would be much appreciated!
After attempting to find a Liquid solution I was finally able to solve this problem using custom metafields and jQuery. In the Shopify collection template, I added a data attribute to be included with each product like so:
<div id="product_list">
<div data-imp="{{product.metafields.important.important}}" class="product_block" /> PRODUCT #1 </div>
<div data-imp="{{product.metafields.important.important}}" class="product_block" /> PRODUCT #2 </div>
</div>
Then I used a simple jQuery function to sort the nodes by data attribute in descending order
var $wrapper = $('#product_list');
$wrapper.find('.product_block').sort(function (a, b) {
return +b.dataset.imp - +a.dataset.imp;
})
.appendTo( $wrapper );
}
Hope this helps somebody!
Nope. Doesn't work. Only fixed filters affect the sorting order. You can manually sort the products from the collection page in admin panel.
In shopify, I am creating 4 dropdowns.
Dropdown 1 - City
Dropdown 2 - Category of shops (such as Apparel, stationary etc)
Dropdown 3 - Shops available in the particular city and particular shop
Dropdown 4 - Items available in the particular shop
My requirement is, when a particular shop is selected (Dropdown 3) below the dropdown all the products in that shop should be listed (I will make the product part of collection and collection name will be same as shop name).
I realize that I need to use Ajax. I checked the APIs offered by shopify but somehow I am not able to connect dots. I am not able to use it. Can you please provide a pointer where it is explained how to achieve it.
Please note, i am new to web development and shopify.
EDITED*
I have created another template called "collection.alternate".
Tagged the collection to the new template.
In theme.liquid I have used the following code, to avoid header and footer from getting displayed in iframe
{% if template != 'collection.alternate' %}
{% include 'header-bar' %}
.....
{% endif %}
Used iframe to load the collection.alternate.
However in iframe the Header is still getting displayed. When I analyze the "Elements" in Chrome developer tools for the iframe, I see the header logic is generated for collection.template. How do I stop header from appearing in iframe.
You don't need AJAX for this functionality.
Each values in the four dropdowns should be added as tags in the products. Then you can use simple JavaScript command to change the url as required and display required products.
For ex., let's say you have New York as a City, and CK as a Shop. Now to display the products that are in CK store in New York, all the said products must contain New York and CK as tags. The final URL to display the said products will be - storename.myshopify.com/collections/all/new-york+ck
To call products from shopify collects you can simply use this loop:
{% for product in collections.shop_name.products limit: 4 %} /*********** shop_name is collections name limit is used to show no. of products ******/
<li><img src="{{ product.featured_image | product_img_url: 'medium' }}" alt=""></li>
{% endfor %}
Below is the link to shopify article that describes how to filter products based on tags.
https://docs.shopify.com/support/your-store/collections/filtering-a-collection-with-multiple-tag-drop-down
Thanks #Hymnz for guiding me to the article.
Just mention your collection name in collection array index
{% for product in collections['collection-name'].products %}
{{product.name}}
{% endfor %}
Looking for some advice on an interesting situation using Shopify. I'm building a site for a client that has Products that have free replacement parts available. Each replacement part has variant color options.
So far I have had the users at the company add all the replacement parts as products in the store. I have filtered the search results and the catalog results so the replacment parts are not show.
The only place they want the replacement parts to show is when a user visits a PRODUCT, they can click a button that says order replacement parts. Then a screen will show with all the replacement parts for that product.
A single replacement part may belong to several products and may have different color variants.
So what I have done thus far was had the client tag all parts with at least two tags. A tag called "part" that identifies the product as a part. And one or more tags like "link:SKU123" that links the part to one or more products. Then on my Product page I was using liquid to loop all parts and display the ones that matched the products SKU. Then I found out that the for loop has a 50 item limit...
So I looked at the product API, which would be ok, except that it has no way to filter by tags. Tags seem so handy and yet I don't see many ways to use them... So ultimately I'm looking for a way to display all replacement parts for a particular product. It doesn't have to involve tags, although the client has already tagged all the parts and I would hate to tell them it was a waste of time. But really any thoughts on how to accomplish this would be greatly appreciated.
The only way I can think to do it, is to return all of the replacement parts and then filter through them on the page. I see the API is limited to 250 products, but I suppose I could make multiple calls until I get them all. I'm not sure how many replacment parts there are total, but I suspect there could be upwards of 1000. Seems like a waste of network resources to have to pull them all down and then filter through them...
P.S. - the replacement parts are free, can they be run through the checkout with a zero dollar amount?
Ok so I have a couple different answers to this question. Not sure which one I will use yet.
Method 1: Filter Collection by Tag using URL
The first one was provided by Shawn Rudolph on the Shopify forums. It involves filtering a collection by tag using the URL. Shawn's post here explains it well: https://ecommerce.shopify.com/c/shopify-discussion/t/product-replacement-parts-270174
Method 2: Use paginate to get all products from collection using the AJAX API
This method is pretty cool. Yes it is more work than method one, but this maybe useful in a lot of scenarios. Out of the box Shopify does not allow you to retrieve all products from a given collection using the AJAX API. It can be done with the admin API but not the AJAX one to my knowledge. However, you can access all products from a collection with a for loop, but the for loop only allows up to 50 items to be looped at a time. That's where the paginate trick comes in. Basically I adapted the technique outlined by davecap here: http://www.davecap.com/post/9675189741/infinite-scroll-for-shopify-collections
So first you need your HTML/Liquid layout:
{% paginate collections.mycollectionname.products by 50 %}
{% for product in collections.mycollectionname.products %}
<div class="clone-node" id="product-{{ forloop.index | plus:paginate.current_offset }}">
{{ product.title }}
</div>
{% endfor %}
{% if paginate.next %}
<div class="clone-node next" title="{{ paginate.next.url }}"></div>
{% endif %}
<div id="insertion-point"></div>
{% endpaginate %}
So let's break it down a bit. First we are paginating are products by 50. This is the max amount a for loop will allow, so that's what we are going to use:
{% paginate collections.mycollectionname.products by 50 %}
Next we begin to loop our products. Every product is given a wrapper div with a class of "clone-node" this is very important. I also assign the div a unique ID, which isn't necessary for this to work, but may come in handy when trying to identify the product for later operations.
{% for product in collections.mycollectionname.products %}
<div class="clone-node" id="product-{{ forloop.index | plus:paginate.current_offset }}">
{{ product.title }}
</div>
{% endfor %}
We have to make sure to include the paginate.next URL. We also give this a "clone-node" class and we add a "next" class. I assign the paginate.next.url to the title attribute, but you could assign it to any number of attributes. You just need to be able to fetch it with jQuery.
{% if paginate.next %}
<div class="clone-node next" title="{{ paginate.next.url }}"></div>
{% endif %}
Then lastly we assign an insertion point. This is where we want our next set of 50 products to be inserted once we fetch them:
<div id="insertion-point"></div>
OK so now let's look at the JS code:
<script>
var prevUrl = ""; //this helps us know when we are done receiving products
function getParts() {
//get the last instance of the .next node. This will give us the next URL to query
var nextNode= $(".next").last(),
url = nextNode.attr("title"); //nab the URL
//send a get request to our next URL
$.ajax({
type: 'GET',
url: url,
dataType:'text',
success: function (data) {
//use a dummy div to convert the text to HTML, then find all of our clone-nodes, including our new "next" div which contains our next URL
var cloneNodes = $("<div>").html(data).find(".clone-node");
//insert our new clone-nodes on the page
cloneNodes.insertBefore("#insertion-point");
//if the URL's don't match let's grab the next 50!
if (prevUrl != url) {
prevUrl = url;
getParts();
}
}
});
}
//Call getParts for the first time to get the party started.
getParts();
</script>
What this will basically do, is get the URL for the next page of products from the title attribute of the div that's holding the paginate.next.url. Then using the jQuery ajax function we call that URL and it returns a page of HTML to use formatted just like our existing page, with the same "clone-node" classes we assigned, only it has the next 50 products embedded in it.
In davecap's example he used a dataType of HTML on his Ajax call, but that gave me some troubles. So instead, I used dataType text and used a dummy div created by jQuery to convert the text into HTML. Then jQuery grabs all of the divs with the "clone-node" class on them and inserts them on the page before our insertion-point. Remember the clone-nodes now hold the next 50 products so we just added the next 50 products to our page.
Lastly, we check if the previous URL is not equal to the current one. If it's not equal, that means it's a new URL and thus there must be more products to fetch, so we recursively call our getParts() function, which starts the process over and grabs the next 50. This continues until finally the URLs match, which means no more products to fetch, and the process stops.
There you have it! Of course if you have to fetch thousands and thousands of products this may be less then ideal because you are calling them 50 at a time. But for smaller numbers (maybe hundreds and hundreds...) it should work just fine.
I would create the parts as standalone products. Then each part will have variants (colours). Then you create metafields for this parts (product) which have a field with a list of all product ids which are 'mother' for this parts. This way you don't need to have strange tags and you keep concepts more separated, & everything cleaner. As per the zero amount, yes, you can go to checkout with zero amount (if your shipping settings allow you to do so).