Use a Twig variable as a part of an other - variables

I would like to use a Twig value comes from a "for" loop as a key in another for loop.
I have 2 arrays: "tab" dans "stats".
"tab" array has multiple values, with one named "name", which is an other array with keys and values.
I would like to use the "tab.name" value as a loop variable for my stats array.
I tried with no success to "add" the value directly in my variable.
{% for elem in tab %}
{% for data in stats.elem.name %}
------ My code
{% endfor %}
{% endfor %}
With this I don't have any result because Twig search for "stats.elem.name" array, but in fact for example if in a loop elem.name = "intitule", the real array is stats.intitule.
I hope i'm clear enough… :)
Thanks!

this should work
{% for elem in tab %}
{% for data in stats[elem] %}
{{data}}
{% endfor %}
{% endfor %}
https://twigfiddle.com/mzpjh8

You can do this by changing the array reference for stats to the standard square brackets - stats[elem.name]
{% for elem in tab %}
{% for data in stats[elem.name] %}
<!-- code -->
{% endfor %}
{% endfor %}

Related

Shopify' s all_products[ ] does only accept title

For a client I'm building a way to choose what products are shown in the recommended products section on the product page. To achieve this I thought about inserting the handles from the products I'd like to display into the tag part of the product.
So I'm able to loop over all the tags from the product and then for every tag I'd take the product object and display it. The problem is this code doesn't seem to work:
{% for tag in tags %}
{% for single_product in all_products[tag] %}
{{ single_product }}
{% endfor %}
{% endfor %}
This code does work:
{% for tag in tags %}
{% for single_product in all_products[tag].title %} <---- Added .title
{{ single_product }}
{% endfor %}
{% endfor %}
Sadly, I need the entire product object to display it instead of only the product title. How do I achieve this in shopify?
Side info: This code is placed inside of the framework--product-recommendations.liquid file
The solution was using the collection instead of the all_product. I create a collection containing all product and was perfectly able to loop over it.
My code looks like this now:
{% for tag in tags %}
{% for single_product in all_the_products %}
{% if tag == single_product.handle %}
<div class="product-recommendations--item">
{%
render 'framework--product--item',
product: single_product,
view: 'grid'
%}
</div>
{% endif %}
{% endfor %}
{% endfor %}
all_the_products is assigned to a collection containing all the products.

paginating variant collection in liquid

In my collection template I'm showing product variants if applicable and products if not. This works very well, safe for the fact that it seems impossible to paginate. I've tried creating a custom array of variants/products, but paginate wants nothing to do with it.
Here's the code I'm currently using which works for outputting variants/products in a grid:
{% for product in collection.products %}
{% if product.variants.size == 0 %}
{% include 'product-card-grid2', max_height: max_height %}
{% else %}
{% for variant in product.variants %}
{% include 'product-card-grid2', max_height: max_height %}
{% endfor %}
{% endif %}
{% endfor %}
How do you paginate something like this? Essentially I'd like something like the excellent pimoroni.com.
Liquid seems exceptionally rigid in terms of array construction/manipulation.
I guess the alternative would be to output the list as json and then manually handling it in JS, but it would be nice if that's avoidable.
One page may show 200 and another may show 50. I'd like to normalize that to 20 per page - also to avoid hitting shopify's hard cap on collections.
You can't achieve this only with liquid.
You can paginate the products but the variants will throw the logic of.
So there is not a lot of you can do.
One hacky ways is to overwrite the paginate limit to something crazy to show all the products and create a JS paginate instead of a liquid one.
Example:
{% paginate collection.products by 9999 %}
{% for product in collection.products %}
{% if product.variants.size == 0 %}
{% include 'product-card-grid2', max_height: max_height %}
{% else %}
{% for variant in product.variants %}
{% include 'product-card-grid2', max_height: max_height %}
{% endfor %}
{% endif %}
{% endfor %}
{% endpaginate %}
But if you have a lot of products the load time will be crazy slow for the back-end to process the request.
Another option is to create a GraphQL query with the Storefront API and populate the page via JS. Bu this may be an overkill for such a thing.
Another option is to use some kind of an App that will pull the variants and create the pagination for you. ( via JS once again )
Last option is to describe this to your client and that you don't have an easy/optimised way to handle variants pagination because of Shopify limitations.
Concat only works on arrays. Luckily there are ways to handle that. Array doesn't have the slice method (only strings) so we need to construct that too. Modulo takes care of under/overflow.
At last, success:
{% comment %}
------------------------------------------------------------------------------
Consolidating the product list for later use.
NOTE: It will probably cap over 50 products!!!!!!!!!!!!!!!!!!!!!!!!!!!
Needs to be rewritten at some point to use page number to load only the needed
products.
{% endcomment %}
{% assign productList = "" | split: "" %}
{% assign counter = 0 %}
{% assign variantLimit = 30 %}
{% for product in collection.products %}
{% assign productList = productList | concat: product.variants %}
{% assign counter = counter | plus: product.variants.size %}
{% endfor %}
{% assign maxSize = productList.size %}
{% assign start = current_page | minus: 1 | times: variantLimit %}
{% assign end = current_page | times: variantLimit | minus: 1 %}
{% if end > maxSize %}
{% assign end = productList.size | modulo: variantLimit %}
{% endif %}
{% assign slice = "" | split: "" %}
{% for i in (start..end) %}
{% assign temp = productList[i] | where: "available", true %}
{% assign slice = slice | concat: temp %}
{% endfor %}
{% assign productList = slice %}
And this as product loop
{% for variant in productList %}
{% include 'product-card-grid2', max_height: max_height %}
{% endfor %}
Also remember the next button
{% unless end > maxSize %}
Next Page
{% endunless %}

Is it possible to add more values in a for in loop Liquid Shopify?

Here's my code
{% for filter_prefix in filter_prefixes %}
{{ filter_prefix }}
{% endfor %}
There are some values I want to add to the filter_prefixes, example: Type...
Can I do it like this?
{% for filter_prefix in filter_prefixes %}
{% filter_prefix = 'Type' %}
{{ filter_prefix }}
{% endfor %}
To list all the filter_prefixes and added the Type value as well.
Please help me with this.
Thank you!
Just output the value you want. There is no need to add it to the loop since it is just something you output anyway.
Type
{% for filter_prefix in filter_prefixes %}
{{ filter_prefix }}
{% endfor %}
Or use the Shopify Array filters split and join. So you join your existing array filter_prefixes into a string, append Type and then split that back into an array.

Django comparing form field value with selected value

I have a number of select box form fields which I need to output in the template with custom markup as a list. To do this I have created an include which gets passed the current form field as an argument.
I can loop over the list of options to create the list I want without any problems. My problem is that above the list of options I need to display either the form label, or the current selected value. This is the code I have to do this:
{% if field.value %}
{% for value, option in field.field.choices %}
{% if value == field.value %}
{{ option }}
{% endif %}
{% endfor %}
{% else %}
{{ field.label }}
{% endif %}
If for example value in the above code is 1, and field.value is also 1, the equality test doesn't work and the bit of code within the if statement does not get executed. I guessed it might be down to the data type of the two values I was comparing, so I tried casting both to an integer as follows:
{% if field.value %}
{% for value, option in field.field.choices %}
{% if value|add:"0" == field.value|add:"0" %}
{{ option }}
{% endif %}
{% endfor %}
{% else %}
{{ field.label }}
{% endif %}
This worked fine for the case where the values in the select box were integers. However, sometimes my select boxes have booleans as the value, so in this case the comparison won't work with the integer casting applied.
How can I safely compare the two values whatever the type of data is being compared?
I think I have found the answer.
The issue is that the selected value is always a string, and the value in the forloop could be an integer or a boolean (or potentially other data types). The answer is to cast the value in the forloop to a string:
{% if field.value %}
{% for value, option in field.field.choices %}
{% if value|stringformat:"s" == field.value %}
{{ option }}
{% endif %}
{% endfor %}
{% else %}
{{ field.label }}
{% endif %}

How to append a variable inside another vaiable name in liquid html

Here is the code:
{% for i in (0..5) %}
{% assign product = recommendations.top_related_products_{{i}}.products[0].title %}
{{ product }}
{% endfor %}
This code throws the error Liquid error: Cannot read property '0' of null
I have 6 arrays under recommendation like top_related_products_0...5.
How do I print the title variable which is inside the top_related_products_0..5?
Create a string containing the variable name, then use square bracket notation to access it.
For example:
{% for i in (0..5) %}
{% capture related_products %}top_related_products_{{i}}{% endcapture %}
{{ recommendations[related_products].products.first.title }}
{% endfor %}