Nunjucks if statement - nunjucks

I am currently investigating if we can make a query string var work within a nunjucks if statement
The below hardcoded value works "5" and brings back the expected result:
{% block content %}
{% for client in clients %}
{% if client.id == 5 %}
{{client.client}}
{% endif %}
{% endfor %}
{% endblock %}
I have the current page id from the url stored in a variable but i can't seem to replace "5" with the actual var
Is this possible?
Edit added JQuery
$(document).ready(function () {
$.urlParam = function (name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
return results[1] || 0;
}
var newid = $.urlParam('id');
alert(newid);
});
Thanks a lot of any time spent.
TCP

Related

Nunjucks variables inside eleventy-img shortcode

I could find any solution about this problem. Basically I want to be able to get the data from a Nunjucks variables to be correctly rendered inside a shortcode.
{% for foo in bar %}
{% image "{{foo.src}}", "alt", "sizes", "imgClass" %}
{% endfor %}
but this results in an error
[11ty] Problem writing Eleventy templates: (more in DEBUG output)
[11ty] 1. Having trouble rendering njk template ./src/index.html (via TemplateContentRenderError)
[11ty] 2. (./src/index.html)
[11ty] Template render error: (...test.html)
[11ty] EleventyShortcodeError: Error with Nunjucks shortcode `image` (via Template render error)
[11ty] 3. ENOENT: no such file or directory, stat '{{foo.src}}.png' (via Template render error)
So it can not get the right variable value in there. But how can I do this?
You can use the variables directly in that case:
{% set images = [
{
src: "src/images/image-file.jpg",
alt: "image description",
imgClass: "center fit"
},
...
]
%}
{% for item in images %}
{% image item.src, item.alt, item.imgClass %}
{% endfor %}

How do I mark a filter as being safe?

I am trying to set up a syntax highlighting filter for nunjucks using highlight.js. It seems pretty easy to do. In my elevnety.js file I included:
const hljs = require('highlight.js');
eleventyConfig.addFilter('highlight', function(txt) {
return hljs.highlightAuto(txt).value;
});
It appears that highlight.js is a safe filter and will properly escape it's contents and add markup to control the highlighting, so there is nothing else to do.
In my njk page I try to use this with
{% filter highlight %}
<xmlstuff>
<myelements attr1="foo" />
</xmlsfuff>
{% endfilter %}
The highlighting markup is being generated correctly, but the whole result is being escaped (by nunjucks perhaps) so the resulting page renders all the markup code. Here is what is getting added to the output html page:
<span class="hljs-tag">&lt;<span class="hljs-name">xmlstuff</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">myelements</span> <span class="hljs-attr">attr1</span>=<span class="hljs-string">"foo"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">xmlsfuff</span>&gt;</span>
I know that nunjucks has a safe filter to prevent this from happening, but I don't know how to apply that to say that my filter block doesn't need escaping, and I could not find anything in the documentation. I tried a number of approaches, but they all failed:
{% filter highlight | safe %}
<xmlstuff>
<myelements attr1="foo" />
</xmlsfuff>
{% endfilter %}
{% filter highlight %}
<xmlstuff>
<myelements attr1="foo" />
</xmlsfuff>
{% endfilter | safe %}
{{ {% filter highlight %}
<xmlstuff>
<myelements attr1="foo" />
</xmlsfuff>
{% endfilter %} | safe }}
Is there any way to mark this filter block as safe?
Try to apply env.filters.safe before output.
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
env.addFilter('safeFilter', str => env.filters.safe(str));
env.addFilter('unsafeFilter', str => str);
var html = env.renderString(
`{{ str | safeFilter }}\n{{ str | unsafeFilter }}`,
{str: '<h1>Hello</h1>'}
);
console.log(html);
#aikon-mogwai posted the correct answer. However for eleventy a bit more is required because the filter needs to be set up in the eleventy.js file and we need to get access to the nunjucks environment. I'll add my complete solution here for posterity.
I does not look like eleventy provides access to the nunjucks environment, so we need to create one and set it to override the existing environment. After that it is pretty well what he said:
module.exports = function(eleventyConfig) {
eleventyConfig.addPassthroughCopy('src/images')
/*
* Create and register a Nunjucks environment, just so we
* can get access to the safe filter.
*/
let Nunjucks = require("nunjucks");
let nunjucksEnvironment = new Nunjucks.Environment(
new Nunjucks.FileSystemLoader("src/_includes"), { }
);
eleventyConfig.setLibrary("njk", nunjucksEnvironment);
/*
* Set up a syntax highlighting filter for code blocks
*/
const hljs = require('highlight.js');
eleventyConfig.addNunjucksFilter('highlight', function(txt, lang) {
var txt2;
if (lang == undefined)
txt2 = hljs.highlightAuto(txt).value;
else
txt2 = hljs.highlight(lang, txt).value;
return nunjucksEnvironment.filters.safe(txt2);
});
return {
dir: { input: 'src', output: 'dist', data: '_data' },
passthroughFileCopy: true,
templateFormats: ['njk', 'md', 'css', 'html', 'yml'],
htmlTemplateEngine: 'njk'
}
}
I achieved this with Eleventy#1.0.0-beta.8 like this:
eleventyConfig.addNunjucksFilter('doThing', function (value) {
return this.env.filters.safe(doThing(value));
})

how to use if condition to include in nunjucks?

so I have a page parameter sent from server.So I set the currentPage to be event if it is event otherwise default. I'm trying to include only when page is default.
The following code works when current page is not event i.e default but for event page it sends error as follows:
{% set currentPage = 'event' if page === 'event' else
'default' %}
{% include 'partials/default-scripts.njk' if currentPage === 'default' %}
Error:
Template render error:
Error: The `name` parameter is not specified:
I'm not sure what is wrong really.
{% set currentPage = ('event' if page === 'event' else 'default') %}
{% if currentPage == 'default' %}
{% include 'partials/default-scripts.njk' %}
{% endif %}
or
{% include ('partials/default-scripts.njk' if currentPage == 'default' else 'dummy') %}
Where dummy is an empty template.

Shopify Liquid Search.Results - How to Sort by Barcode?

we have been struggling with this problem for weeks and still have not found a solution.
We have a Shopify store with 2000 products and 300 vendors. Each vendor has a "ranking number" which we have assigned to them and entered into the "barcode" field in the variants of every single product.
The goal is that we want Shopify to be able to sort our search results by the Barcode using the liquid "sort" tag like so:
{% assign foo = search.results | sort:'title' %}
However, when we attempt to use this syntax to sort by "barcode" it doesn't return any results
{% assign foo = search.results | sort:'barcode' %}
We have attempted to use 'sort' with all the following arguments but none of them work:
{% assign foo = search.results | sort:'product.barcode' %}
{% assign foo = search.results | sort:'item.barcode' %}
{% assign foo = search.results | sort:'item.variants.first.barcode' %}
{% assign foo = search.results | sort:'items.variants.variant.barcode' %}
etc, but none of them work.
If ANYONE can point us in the right direction for which argument/syntax to use to sort our search results by variant.barcode, it would be MUCH APPRECIATED!!!!!
Thanks
In terms of native functionality you're out of luck.
You can do something like this though by rolling your own.
Manually sort your collections by vendor rank
Limit your search to collection results by creating your own search function
or
creat your own search function but make it a "filter" on your current collection and leave the native search alone.
Create Your Own Search:
Put your item results cell in a snippet. collection-product.liquid
Include that snippet in
your collecton liquid.
Make a new collection template
(collection.search-results.liquid)
collection.search-results.liquid looks something like the following. Your pagination size should match the pagination size of your main collection.
{% layout none %}
{% paginate collection.products by 50 %}
<div class="search-results">
{% for product in collection.products %}
{% include 'collection-product' %}
{% endfor %}
</div>
{% endpaginate %}
Now your search becomes an input box with some javascript. The fun part. The script below was pulled from a working site. I've trimmed it up somewhat to hopefully make the bones of it clearer but it probably won't run as is. I've removed references to adding a search because for your purposes you want the native custom search.
As to why not just sort the results. You could use this concept to do that but it returns results a page at a time. In order to custom search all the results you'd have to accumulate the results and sort them when the search is finished. Search time then depends heavily on the size of the collection and the user's network speed.
var defaultView = $(".filters-off"); // add this class to your main collection wrapper
var facetView = $("#filter_target"); // <div id="filter_target"></div> below your main collection wrapper
var currentRequest = 0;
function filterCollection(term){
//[[t1, t2], [t3,t4]]
// console.log('applying '+ JSON.stringify(facets));
var anyResults = false;
var resultsPage=0;
var PAGE_SIZE = 50;
var productsFound = 0;
var collectionPath = location.pathname;
console.log('get from '+ collectionPath);
currentRequest = new Date().getTime();
var viewLink = collectionPath+'?view=search-results'; // same as your layout none template
function applyFilter(myRequest){
resultsPage++;
if(resultsPage > 20) return false; // arbitrary abort for too many results
if(resultsPage > 1) viewLink+='&page='+resultsPage;
return $.get(viewLink).then(function(page){
if(currentRequest != myRequest){
console.log('mid abort');
return false;
}
var pageProducts = $("div[data-tags]", page); //some markup you added to collection-product snippet to help plucking the product elements
if(!pageProducts.length) return false;
console.log('found: '+ pageProducts.length);
var filteredProducts = pageProducts.filter(function(){
if($(this).text().indexOf(term) != -1) return true; // search the returned text
if($(this).attr('data-keywords').indexOf(term) != -1) return true;
return false;
});
if(filteredProducts.length){
if(!anyResults){
anyResults = true;
toggleView(true);
}
filterView.append(filteredProducts);
productsFound+= filteredProducts.length;
}
return (pageProducts.length == PAGE_SIZE && currentRequest == myRequest) ? applyFilter(myRequest) : false;
}).then(function(proceed){
if(currentRequest == myRequest){
if(!anyResults){
toggleView(false, true);
}
}
});
}
applyFilter(currentRequest);
}
function toggleView (showFacets, showNoGo){
facetView.empty();
$(".filter-progress").empty();
if(showFacets) {
defaultView.add('.pagination').hide();
}else {
if(!showNoGo){
defaultView.add('.pagination').show();
}else {
$(".no-facets").clone(true).appendTo(facetView).show(); // .no-facets is normally hidden chunk that allows for easy internationaliztion of "No results found" type messages
}
}
};

rebuild content of a Div tag in complete function of $.ajaxt

I have a 4 column table of products in template,each item in table has an anchor with onclick like this:
<div id="tablepro">
<table>
<tr>
{% for product in cat.products.all %}
{% if forloop.counter|divisibleby:4 %}
</tr>
<tr>
<td><center>delete</br><img style="width:200px;height:200px;" class="magnify" src="{{product.image.url}}" /></center></td>
{% else %}
<td><center>delete</br><img style="width:200px;height:200px;" class="magnify" src="{{product.image.url}}" /></center></td>
{% endif %}
{% endfor %}
</table>
</div>
in remove function I have :
function remove(id)
{
var URL='{% url CompanyHub.views.catDetails company.webSite,cat.id %}';
URL+='delpro/'+id+'/';
$.ajax({
url:URL,
type:'POST',
complete:function(){
var str='<table><tr>';
{% for product in cat.products.all %}
{% if forloop.counter|divisibleby:4 %}
str+='</tr><tr>';
str+='<td><center>delete</br><img style="width:200px;height:200px;" class="magnify" src="{{product.image.url}}" /></center></td>';
{% else %}
str+='<td><center>delete</br><img style="width:200px;height:200px;" class="magnify" src="{{product.image.url}}" /></center></td>';
{% endif %}
{% endfor %}
str+='</table>';
$('#tablepro').html(str);
},
error:function(){
alert('Error');
}
});
}
in views.py :
def deleteProduct(request,key,cat_id,pro_id):
try:
company=Company.objects.get(webSite__iexact=key)
except Company.DoesNotExist:
Http404
cat=Category.objects.get(pk=cat_id)
if pro_id:
try:
product=Product.objects.get(pk=pro_id)
product.delete()
except Product.DoesNotExist:
Http404
return render_to_response('CompanyHub/Company/%s/cat_details.html'%(company.theme),{'company':company,'cat':cat}, context_instance=RequestContext(request))
as U see I've returned cat object that now a product object has removed from its list,but I can't get my Div updated in template!that sounds like cat object has not been updated in template tag.
any suggestion is appreciated
Template are compiled on server side and the browser renders the HTML.
To update your div after ajax call, you'd need to update it using javascript code in complete method. Your server side view can return JSON, XML, HTML or any other data type and your complete method has to render that data. Here is an example of how your complete method should look like if your server side returns partial html (i.e. just the table):
complete:function(data) {
$('#tablepro').html(data);
},
Remember that templates are compiled on the server side and the resulting HTML is then passed to the client. This means that the template code you have within your complete function (in the ajax call) will always be the same - in other words, every time you delete an element (and remove is called), you are redisplaying all the original categories again, as the HTML generated within the for loop is created on the server side once - not asynchronously

Resources