I have the following code for accessing the values in dictionary using jinja template
names={'ant':['abc',2456789,28-02-1991]}
{% for key,value in names.items %}
{% for val in value %}
<p class="lead">{{ val }} </p>
{% endfor %}
{% endfor %}
For every value,it is printed I want to append a string.My values are "abc","2456789","28-02-1991".I want the following output:
Name:abc
Id:2456789
Date: 28-02-1991
How could I do it using jinja template?
You can use it like this with your current data structure.
{% for key, value in names.items %}
<p class="lead">Name: {{ value.0 }}</p>
<p class="lead">Id: {{ value.1 }}</p>
<p class="lead">Date: {{ value.2 }}</p>
{% endfor %}
But it would be more readable if change your data structure into this:
my_dict = {
'ant': {
'id': 2,
'name': 'abc',
'date' : datetime.now()
}
}
then your template would look like this:
{% for key, person in my_dict.items %}
<p class="lead">Id: {{ person.id }} </p>
<p class="lead">Name: {{ person.name }} </p>
<p class="lead">Date: {{ person.date }} </p>
{% endfor %}
In Jinja-2 you can subscript in the variables:
{% for key,vals in names.items %}
<p class="lead">Name: {{ vals[0] }}</br>
Id: {{ vals[1] }}</br>
Date: {{ vals[2] }}</p>
{% endfor %}
Related
I am trying to update the product price when a variant is selected. So far i have the below; but just cant get it to work.
I have added this script to the theme.liquid
{{ 'option_selection.js' | shopify_asset_url | script_tag }}
I have the price in the product-template.liquid
<div class="product-price"><span id="price-field">{{ product.price | money }}</span></div>
<select
v-model="form.id"
name="id" id="variant-id"
class="minimal mt-2 mb-2 {% if hide_default_title %}hidden{% endif %}">
{% for variant in product.variants %}
{% if variant.available %}
<option
{% if variant == current_variant %}selected="selected"{% endif %}
{% unless variant.available %}disabled="disabled"{% endunless %}
data-inventory-quantity="{{ variant.inventory_quantity }}"
data-price="{{ variant.price | money | strip_html }}"
value="{{ variant.id }}"
class="js-variant-radio">{{ variant.title }}
</option>
{% endif %}
{% endfor %}
</select>
And the callback function here
<script>
// <![CDATA[
var selectCallback = function(variant, selector) {
if (variant) {
if (variant.available) {
// Selected a valid variant that is available.
$('#add').removeClass('disabled').removeAttr('disabled').val('Add to Cart').fadeTo(200,1);
} else {
// Variant is sold out.
$('#add').val('Sold Out').addClass('disabled').attr('disabled', 'disabled').fadeTo(200,0.5);
}
// Whether the variant is in stock or not, we can update the price and compare at price.
if ( variant.compare_at_price > variant.price ) {
$('#price-field').html('<span class="product-price on-sale">'+ Shopify.formatMoney(variant.price, "") +'</span>'+' <s class="product-compare-price">'+Shopify.formatMoney(variant.compare_at_price, "")+ '</s>');
} else {
$('#price-field').html('<span class="product-price">'+ Shopify.formatMoney(variant.price, "") + '</span>' );
}
} else {
// variant doesn't exist.
$('#add').val('Unavailable').addClass('disabled').attr('disabled', 'disabled').fadeTo(200,0.5);
}
}
// initialize multi selector for product
jQuery(function($) {
new Shopify.OptionSelectors("variant-id", { product: , onVariantSelected: selectCallback });
});
// ]]>
</script>
Looking at the code, it looks like you're just missing a product object when you try to initialize the Shopify.OptionSelectors
new Shopify.OptionSelectors("variant-id", { product: , onVariantSelected: selectCallback });
Try adding the product object to this line to see if that fixes the problem. The updated line should look like this:
new Shopify.OptionSelectors("variant-id", { product: {{ product | json }}, onVariantSelected: selectCallback });
After select2 manipulates the dropdown field the regular use of form.owner_id.data yields None. How can I extract the selected option from a select2 field.
If I disable the select2 javascript, wtforms will work just fine and I will be able to use form.owner_id.data. (but it looks ugly)
screenshot of rendered form
forms.py
class ProjectForm(FlaskForm):
owner_id = SelectField('Owner:', [validators.Required()], choices=[], render_kw={"placeholder": "Owner company *"})
(rest of form has been truncated for simplicity)
views.py
#app.route('/new_project/<int:company_id>', methods=('GET', 'POST'))
def new_project(company_id):
membership = Membership.query.filter_by(user_id=current_user.id, company_id=company_id).first()
company = Company.query.filter_by(id=company_id).first_or_404()
form = ProjectForm()
form.owner_id.choices = [(str(comp.id), repr(comp)) for comp in Company.query.all()]
form.client_id.choices = [(str(comp.id), repr(comp)) for comp in Company.query.all()]
form.contractor_id.choices = [(str(comp.id), repr(comp)) for comp in Company.query.all()]
form.membership_id.choices = [(str(comp.id), repr(comp)) for comp in Company.query.all()]
if request.method == 'POST':
flash(str(form.owner_id.data), 'success')
if form.validate_on_submit():
project = Project()
connection = Assignment()
# PROJECT DETAILS
project.title = form.title.data
project.description = form.description.data
project.owner_id = int(form.owner_id.data)
_macro
{% macro render_select_field(field, placeholder='Select...', label='Select an option below') %}
<div class="form-group">
<label>{{ label }}</label>
<select data-placeholder="{{ placeholder }}" class="select-size-xs">
<option></option>
{% for choice in field.choices %}
<option value="{{ choice[0] }}">{{ choice[1] }}</option>
{% endfor %}
</select>
{% if field.errors %}
{% for error in field.errors %}
<span class="help-block text-danger"><i class="icon-cancel-circle2 position-left"></i>{{ error }}</span>
{% endfor %}
{% endif %}
</div>
{% endmacro %}
html
<form method="POST" action="{{ url_for('new_project', company_id=company.id) }}" enctype="multipart/form-data" role="form">
<div class="panel panel-body login-form">
<div class="text-center">
<div class="icon-object text-muted"><i class="icon-plus2"></i></div>
</div>
{{ form.hidden_tag() }}
<div class="text-center form-group"><span>Project details</span></div>
{{ new_render_field(form.title, icon="icon-quill2", class_='form-control') }}
{{ new_render_field(form.description, icon="icon-stack-text", class_='form-control', rows=10) }}
{{ render_select_field(form.owner_id, placeholder="Who's doing the work?", label="Select the owner company") }}
{{ render_select_field(form.client_id, placeholder="Where do the bills go?", label="Select the client company") }}
{{ new_render_field(form.default_client_rate, icon="icon-price-tag", class_='form-control') }}
{{ new_render_field(form.default_contractor_rate, icon="icon-price-tag", class_='form-control') }}
<!-- THE REST OF FORM HAS BEEN TRUNCATED FOR SIMPLICITY -->
<div class="form-group">
<button type="submit" class="btn bg-pink-400 btn-block">Create</button>
</div>
</div>
</form>
So after analyzing the select field in the inspect window, it became apparent that the select field is missing the name="{{ field.name }}" that wtforms requires in order to validate the form. The simple change made in my _macro was from this:
<select data-placeholder="{{ placeholder }}" class="select-size-xs">
To this:
<select data-placeholder="{{ placeholder }}" class="select-size-xs" name="{{ field.name }}">
With this addition wtforms can now validate, and find the selected option returning the proper id for form.owner_id.data.
I'm trying to build on caroline's solution for related products with metafields to build related product variants. I.e when you click on the white color variant for a desk, you will see the white variant of a chair as a related product. (As opposed to linking the desk product to the chair product regardless of variant.) Caroline's solution is here: https://gist.github.com/carolineschnapp/1003334 and below is my code. Right now it's putting the same product twice on page load, and nothing happens when a different vairant is selected. The way I am formatting the metafield value for each variant is by putting "related-product-handle-1, variant-id-1, related-product-handle-2,variant-id-2, related-product-handle-3, variant-id-3" instead of just the product handles.
{% assign image_size = 'compact' %}
{% assign heading = 'Related Products' %}
{% if product.selected_or_first_available_variant.metafields.recommendations.productHandles %}
<h3>{{ heading }}</h3>
<ul class="related-products"></ul>
{% endif %}
<script>!window.jQuery && document.write('<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"><\/script>')</script>
{{ 'api.jquery.js' | shopify_asset_url | script_tag }}
<script type="text/javascript" charset="utf-8">
$(document).ready(function(){
setTimeout(function() {
var dd = $('.single-option-selector#product-select-option-0');
var vId = location.search.substring(9);
switchRelated(vId);
dd.on('change', function() {
$('ul.related-products').empty();
var vId = location.search.substring(9);
switchRelated(vId);
});
function switchRelated(vId) {
var list = $('ul.related-products');
var vIdd = parseInt(vId);
{% for variant in product.variants %}
{% if variantId == vIdd %}
{% if variant.metafields.recommendations.productHandles %}
recommendations = jQuery.trim({{ variant.metafields.recommendations.productHandles | json }}).split(/[\s,;]+/);
for (var i=0; i < (recommendations.length); i+=2 ) {
var j = (i + 1);
if (recommendations.length && recommendations[i] && recommendations[j] !== '') {
jQuery.getJSON('/products/' + recommendations[i] + '.js', function(product) {
product.variants.forEach(function(variant) {
if (variant.id == parseInt(recommendations[j])) {
list.append('<li><div class="image"><img src="' + variant.featured_image.src + '" /></div><h4>' + product.title + '</h4></li>');
}
});
});
}
}
{% endif %}
{% endif %}
{% endfor %}
}
}, 1);
});
</script>
Answer edited: The first was extremely helpful once I made some corrections to syntax errors and a couple of short additions. Here is my edited version of the answer for anyone who may need it:
Product.liquid:
{% for variant in product.variants %}
{% capture metafield_data %}{% endcapture %}
{% assign related_products = variant.metafields.recommendations.productHandles | split: '|' %}
{% for related_product in related_products %}
{% assign metafield_items = related_product | split: ',' %}
{% assign r_p = metafield_items[0] %}
{% assign r_v = metafield_items[1] | plus: 0 %}
{% assign r_n = all_products[r_p].title %}
{% for related_variant in all_products[r_p].variants %}
{% if related_variant.id == r_v %}
{% assign r_i = related_variant.image.src | img_url: 'small' %}
{% endif %}
{% endfor %}
{% capture metafield_data %}{{metafield_data}}{{ r_p }},{{ r_v }},{{ r_i }},{{ r_n }}{% unless forloop.last %}|{% endunless %}{% endcapture %}
{% endfor %}
<option id="{{ variant.id }}" data-metafield="{{ metafield_data }}" {% if variant == product.selected_or_first_available_variant %} selected="selected" {% endif %} value="{{ variant.id }}">{{ variant.title }} - {{ variant.price | money }}- {{ related_products.size }}</option>
{% endfor %}
And related-variants javascript snippet:
$(document).ready(function(){
setTimeout(function() {
var dd = $('.single-option-selector#product-select-option-0');
if(location.search.substring(9) != ''){
var vId = location.search.substring(9);
}
else {
var vId = {{ product.selected_or_first_available_variant.id }};
}
switchRelated(vId);
$('#product-select option').each(function(index, element){
$(".single-option-selector#product-select-option-0 option:eq(" + index + ")").attr('id', element.id);
$('.single-option-selector#product-select-option-0 option:eq(' + index + ')').attr('data-metafield', $(element).attr("data-metafield"));
$('#product-select option:eq(' + index + ')').attr('id', '');
});
dd.on('change', function() {
var list = $('ul.related-products');
$(list).children().remove();
$(list).empty();
if(location.search.substring(9) != ''){
var vId = location.search.substring(9);
}
else {
var vId = {{ product.selected_or_first_available_variant.id }};
}
switchRelated(vId);
});
function switchRelated(vId) {
var list = $('ul.related-products');
$(list).children().remove();
$(list).empty();
var vIdd = parseInt(vId);
console.log(vIdd)
var variant_matches = $('#' + vId).attr('data-metafield').split('|');
for (var i=0; i < variant_matches.length; i++) {
var items = variant_matches[i].split(',');
list.append('<li><div class="image"><img src="'+items[2]+'" /></div><h4>' + items[3].replace('_','') + '</h4></li>');
}
}
}, 1);
});
The only thing I'm nervous about is the fact that I am copying over the data from the 'product-select' dropdown over to the 'single-option-selector' dropdown. I am doing this because there is no template for rendering the single-option-selector, it seems to be getting added through javascript. If anyone has any insight into manipulating the single-option-selector in liquid, please let me know. Thank you!!
The fastest way to implement this is to let Shopify servers structure the elements for to pull links and image URLs with any AJAX calls. Here's how to do it.
From your example of productHandles, I'm guessing the 3 items in the list are related to a particular variant id xxxxxx. Structure the metafield value this way
rph-1,rph-v-id-1|rph-2,rph-v-id-2|rph-3,rph-v-id-3
Now in product liquid find this section
<select class="product-select" id="product-select" name="id" .... </select>
Change the inside html to the one below -
{% for variant in product.variants %}
{% assign related_products = variant.metafields.recommendations.productHandles | split: '|' %}
{% for related_product in related_products %}
{% assign metafield_items = related_product | split: ',' %}
{% assign r_p = metafield_items[0] %}
{% assign r_v = metafield_items[1] | plus: 0 %} {% comment %} converting string to number {% endcomment %}
{% assign r_n = all_products[r_p].title | replace: ' ','_' %}
{% for related_variant in all_products[r_p].variants %}
{% if related_variant.id == r_v %} {% comment %} fails if r_v is a string {% endcomment %}
{% assign r_i = related_variant.image.src }}
{% endif %}
{% endfor %}
{% capture metafield_data %}{{metafield_data}}{{ r_p }},{{ r_v }},{{ r_i }},{{ r_n }}{% unless forloop.last %}|{% endunless %}{% endcapture %}
{% endfor %}
<option id="{{ variant.id }}" metafield-data={{ metafield_data }}{% if variant == product.selected_or_first_available_variant %} selected="selected" {% endif %} value="{{ variant.id }}">{{ variant.title }} - {{ variant.price | money }}</option>
{% endfor %}
"metafield_data" will contain all the related products information (product,variant,variant image).
JS to replace "switchRelated(vId)"
function switchRelated(vId) {
var list = $('ul.related-products');
var vIdd = parseInt(vId);
list.children().remove()
var variant_matches = $('#vId').attr('metafield-data').split('|')
for (var i=0; i < variant_matches.length; i++) {
var items = variant_matches[i].split(',')
list.append('<li><div class="image"><img src="'+items[2]+'" /></div><h4>'+items[3].replace('_','')+'</h4></li>');
}
}
In layman terms, you are taking the product handle and variant id from the metafields and adding title and image to them using liquid (server side function). You are then matching them to variants and assigning a data variable in the element which you are using again to change the html elements.
P.S. The code is long and not properly aligned and I may have missed code punctuations here and there. Please check them. The logic is simple and the entire weight of AJAX is removed and shifted to normal HTML calls.
I work on a product configurator that works with liquid.
Liquid uses a combination of tags, objects, and filters to load dynamic content.
Every Product has two configuration options. Color and material.
Both options appear as clickable swatches.
We would like to add the effect, that the material swatch change the background color to the color of the selected color variant.
Take place here: http://printnil.com/products/new-standard
swatch.liquid:
{% if swatch == blank %}
{% else %}
{% assign found_option = false %}
{% assign is_color = false %}
{% assign option_index = 0 %}
{% for option in product.options %}
{% if option == swatch %}
{% assign found_option = true %}
{% assign option_index = forloop.index0 %}
<style>
#product-select-option-{{ option_index }} { display: none; }
#product-select-option-{{ option_index }} + .custom-style-select-box { display: none !important; }
</style>
<script>$(window).load(function() { $('.selector-wrapper:eq({{ option_index }})').hide(); });</script>
{% assign downcased_option = swatch | downcase %}
{% if downcased_option contains 'color' or downcased_option contains 'colour' %}
{% assign is_color = true %}
{% endif %}
{% endif %}
{% endfor %}
{% unless found_option %}
{% else %}
<div class="swatch clearfix swatch-{{swatch}}" data-option-index="{{ option_index }}">
{% assign values = '' %}
{% for variant in product.variants %}
{% assign value = variant.options[option_index] %}
{% unless values contains value %}
{% assign values = values | join: ',' %}
{% assign values = values | append: ',' | append: value %}
{% assign values = values | split: ',' %}
<div data-value="{{ value | escape }}" class="swatch-element {% if is_color %}color {% endif %}{{ value | handle }}">
{% if is_color %}
<div class="tooltip">{{ value }}</div>
{% endif %}
<input id="swatch-{{ option_index }}-{{ value | handle }}" type="radio" name="option-{{ option_index }}" value="{{ value | escape }}"{% if forloop.first %} checked{% endif %}/>
{% if is_color %}
<label for="swatch-{{ option_index }}-{{ value | handle }}" style="background-color: {{ value | split: ' ' | last | handle }};">
</label>
{% else %}
<label for="swatch-{{ option_index }}-{{ value | handle }}">
{{ value }}
</label>
{% endif %}
</div>
{% endunless %}
{% endfor %}
</div>
{% endunless %}
{% endif %}
<script>
var mat = jQuery(".swatch.clearfix.swatch-Material");
var colors = jQuery(".swatch-element.color");
var colChanger = function() {
mat.css("background-color", jQuery(this).css("background-color"));
};
colors.each(function(e) { jQuery(this).click(colChanger); });
mat.css("background-color", "#222");
</script>
I am looking to do conditional tests in the html part of my code
html
<p class= "rep3" width="100" id="val1" data1={{ family }} style= "display:none;">{{ family }}</p>
<p class= "rep4" width="100" id="val2" data2="" style= "display:none;"></p>
{% if data2 == "general" %}
criteria1
{% else %}
{% if data2 == "ceramic" %}
criteria2
{% else %}
criteria
{% endif %}
{% endif %}
javascript
<script type="text/javascript">
$(document).ready(function() {
var element = document.getElementById('val1');
laval1 = element.getAttribute('data1');
var element2 = document.getElementById('val2');
element2.setAttribute('data2', laval1);
});
</script>
I read data1 in the script
I would like to send this data in data2 and after to do the test
but its not working
How to do that ?
This code won't work because:
JavaScript code is run by the client, long after the template tags ({% if data2 == "general" %} etc. ) are compiled in the server.
You can't send data from JS to django like that for data2. (Although you could use AJAX), but that is not required in this case.
Why don't you could do something like this, instead of relying on Javascript?
<p class= "rep3" width="100" id="val1" data1={{ family }} style= "display:none;">{{ family }}</p>
<p class= "rep4" width="100" id="val2" data2="" style= "display:none;"></p>
{% if family == "general" %}
criteria1
{% else %}
{% if family == "ceramic" %}
criteria2
{% else %}
criteria
{% endif %}
{% endif %}