How to add nunjucks-date-filter? - json

I can't understand where should i put all this code ,
https://github.com/e-picas/nunjucks-date-filter
i have this structure
enter image description here
this is my template
<div class="container">
<div class="row">
<div class="col-5">
{% for date, block in block | groupby("date") %}
<div class="date">{{ date}}</div>
{% for name, block in block | groupby("name") %}
<div class="about">{{ name }}</div>
{% endfor %}
{% for id, block in block | groupby("id") %}
<div class="id">{{ id }}</div>
{% endfor %}
{% for blocks in block %}
<img onError="this.src='/img/no-photo.png'" src="{{blocks.image}}" alt="">
{% endfor %}
{% endfor %}
</div>
</div>
</div>
at this moment i have a date format from Json file like this dd-mm-yyyy , want to change it with filter to D-MMM.

I was in a similar situation. The nunjucks-date-filter readme recommends using the Nunjucks Environment API to add filters. But I found this doesn't work as the env object was never applied to the Nunjucks instance.
Here's how I got around it, assuming you're using express:
// main.js
const app = express();
const nunjucks = require('nunjucks');
const dateFilter = require('nunjucks-date-filter');
function setUpNunjucks(expressApp) {
let env = nunjucks.configure('views', {
autoescape: true,
express: app
});
// note that 'date' is the function name you'll use in the template. As shown in nunjucks-date-filter's readme
env.addFilter('date', dateFilter);
}
setUpNunjucks();
Then in your template just define the date format you want:
{% for date, block in block %}
<div class="date">{{ date | date("D MMM") }}</div>
{% endfor %}

Related

How to use multiple objects in a single Django template for loop? [duplicate]

So here's my problem: I've got a bunch of instances of a class. I would like to have a sort of table of these instance objects, so that there is a maximum of six in every row. In bootstrap terms, I would like each object to be represented by a thumbnail in a "div" of class "span2".
My initial impulse was to use a nested for loop, but I am having trouble manipulating my index variable in the template, and I can't figure out how to do so outside of my template.
Here is generally what the python/django template/pseudo code is I'm trying to figure out.
queryset = Class.objects.all()
set_length = queryset.count()
num_rows = set_length/6
#because I want 6 columns in each row, each with one instance
set_as_list = list(queryset)
# have a list so I can iterate through objects by index
for i in range(table_rows):
# make a row
<div class="row">
for j in range (i*6,(i+1)*6):
#make six or less columns
<div class="span2">
<p>set_as_list[j].attribute1</p>
<p>set_as_list[j].attribute2</p>
</div>
</div> # end row
I hope this flagrant mixing of django templating language, python, and html doesn't offend anybody too badly. just trying to express the idea of what I'm trying to do. I would appreciate any help someone may be willing to offer because I've been struggling with this for days and have done quite a bit of searching for a solution both within a template and outside.
I also realise that there will be need to be a final row with the remainder of objects after the integer division.
Have no time to explain, but I've had similar problem and until i closed this browser page here is a solution
{% for sub_article in articles %}
{% if forloop.first %}<div class="row">{% endif %}
<div class="col-xs-4">
<a href="#">
{{ sub_article.name }}
</a>
</div>
{% if forloop.counter|divisibleby:3 %}</div><div class="row">{% endif %}
{% if forloop.last %}</div>{% endif %}
{% endfor %}
Since forloop.counter starts the index with 1, divisibleby 3 does not work.
So use forloop.counter0 instead.
<div class="row">
{% for product in all_products %}
{% if forloop.counter0|divisibleby:3 %}
</div><br><div class="row">
{% endif %}
<div class="col-4"></div>
{% endfor %}
I would recommend to add a custom tag as_chunk. I think it makes the code prettier and more readable.
# app/templatetags/my_tags.py
from math import ceil
from django import template
register = template.Library()
#register.filter
def as_chunks(lst, chunk_size):
limit = ceil(len(lst) / chunk_size)
for idx in range(limit):
yield lst[chunk_size * idx : chunk_size * (idx + 1)]
# app/templates/your-template.html
{% load my_tags %}
...
{% for chunk in elements|as_chunk:6 %}
<div class="row">
{% for element in chunk %}
<div class="col-2">
{{ element.name }}
</div>
{% endfor %}
</div>
{% endfor %}
...
maybe too late but there is simple solution as follow
<div class="container">
<div class="row">
{% for product in products %}
{% if forloop.counter0|divisibleby:3 and not forloop.first %}<div class="w-100"></div>{% endif %}
<div class="col">{{product.title}}</div>
{% endfor %}
</div>
</div>
You could make the code a bit more generic. Here's the logic:
queryset = Class.objects.all()
set_length = queryset.count()
<div class="row">
{% for i in queryset %}
<div class="span2">
<p>i.attr</p>
<p>i.attr</p>
</div>
{% if forloop.counter|divisibleby:"6" or forloop.last %}
</div> <!--end row-->
{% endif %}
{% endfor %}
I hope this solves your problem :-)

display data in liquid

I'm looking to display information from a csv file on a jekyll-generated site. I need to search for the appropriate category in the csv file, then display four of them on the page. Filtering to the selected category is no problem, but I'm having difficulty limiting the output to four.
Is there a way to apply a limit to an if statement? Or is there any other way to write this? I'm not that savvy in Liquid, so it's extremely likely that I'm missing an obvious solution.
Basic code to make all the applicable data show up on the screen:
{% for study in site.data.studies %}
{% if study.category contains "foo" %}
<div class="col-sm-3">
<h3>{{ study.title }}</h3>
<div class="list-of-attributes">
<h6>Attributes: </h6>
{{ study.attributes }}
</div>
</div>
{% else %}
{% continue %}
{% endif %}
{% endfor %}
I've also tried unless and tablerow, neither of which worked at all. Am I at least on the right track? How can I limit this forloop to stop at four items?
Thank you!
Ideally data should be filtered before rendering however you can also create a variable in liquid to hold the number of stuff rendered
{% assign rendered = 0 %}
{% for study in site.data.studies %}
{% if study.category contains "foo" %}
<div class="col-sm-3">
<h3>{{ study.title }}</h3>
<div class="list-of-attributes">
<h6>attributes: </h6>
{{ study.attributes }}
</div>
</div>
{% assign rendered = rendered | plus: 1 %}
{% if rendered == 4 %}
{% break %}
{% endif %}
{% endif %}
{% endfor %}
The ideal solution as I said would be to create your own filter which does all the work (filter by category and limit the number of results)
{% assign filtered = site.data.studies | my_custom_filter %}
{% for study in filtered %}
<div class="col-sm-3">
<h3>{{ study.title }}</h3>
<div class="list-of-attributes">
<h6>attributes: </h6>
{{ study.attributes }}
</div>
</div>
{% endfor %}
Presuming that your category is a string, not an array, you can do :
{% assign selected = site.data.studies | where: 'category','foo' %}
{% for study in selected limit:4 %}
<div class="col-sm-3">
<h3>{{ study.title }}</h3>
<div class="list-of-attributes">
<h6>Attributes: </h6>
{{ study.attributes }}
</div>
</div>
{% endfor %}
And if your category is a string like "foo, bar, baz" or and array of strings you can use the jekyll 3.2 where_exp filter like this :
{% assign selected = site.data.studies | where_exp:"item", "item.category contains 'foo'" %}

Include a twig template as an object to be passed into another template?

Im using gulp-twig: https://github.com/zimmen/gulp-twig
I have a twig file for my container component:
{# container.twig #}
<div class="container">
{% for item in items %}
<div class="container__item">
{{ item }}
</div>
{% endfor %}
</div>
I also have a snippet file:
{# snippet.twig #}
<div class="snippet">
<h2>{{ title }}</h2>
</div>
Im demoing these in page.twig. I need to render the snippet as the {{ item }} within the container. So when viewing page.twig this should be the output:
<div class="container">
<div class="container__item">
<div class="snippet">
<h2>title</h2>
</div>
</div>
<div class="container__item">
<div class="snippet">
<h2>title</h2>
</div>
</div>
<div class="container__item">
<div class="snippet">
<h2>title</h2>
</div>
</div>
</div>
Now here is where it gets tricky. container.twig and snippet.twig are being pulled into another application. As such {{ item }} within container.twig cant be changed to something like {{ itemRenderer(item) }}.
However page.twig is not being used anywhere else so I can edit it however I like. Is there a way in page.twig to render container.twig with snippet.twig as it's item, without modifying container.twig or snippet.twig?
This is my gulp task:
var gulp = require('gulp'),
config = require('../config'),
utilities = require('../build-utilities'),
src = config.path.src,
dest = config.path.dest,
opts = config.pluginOptions,
env = utils.getEnv(),
plugins = require('gulp-load-plugins')(opts.load);
var compile = function() {
var notProdOrTest = env.deploy && !env.prod && !env.test,
deployPath = env.deployPath,
sources = (env.deploy) ? ((env.styleguide) ? src.twig.styleguide: src.twig.testing): src.twig.all;
return gulp.src(sources, {base: 'src/'})
.pipe(plugins.twig({
data: {
component: utils.getDirectories('src/component/'),
deploy : env.deploy,
test : env.test,
prod : env.prod
}
}))
.pipe(plugins.htmlmin(opts.htmlmin))
.pipe(plugins.tap(function(file){
file.path = file.path.replace('testing/', '');
}))
.pipe((notProdOrTest) ? plugins.replace(/src="\//g, 'src="/' + deployPath.root + '/'): plugins.gutil.noop())
.pipe((notProdOrTest) ? plugins.replace(/href="\//g, 'href="/' + deployPath.root + '/'): plugins.gutil.noop())
.pipe((notProdOrTest) ? plugins.replace(/srcset="\//g, 'srcset="/' + deployPath.root + '/'): plugins.gutil.noop())
.pipe((notProdOrTest) ? plugins.replace(/url\('\//g, 'url(\'/' + deployPath.root + '/'): plugins.gutil.noop())
.pipe(gulp.dest((env.deploy) ? deployPath.markup: dest.markup));
},
watch = function() {
gulp.watch(src.twig.watch, ['twig:compile']);
};
module.exports = {
compile: compile,
watch : watch
};
This could be done with macros:
{# macros.html.twig #}
{% macro thisItem(item) %}
<div class="this-snippet">
<h2>{{ item.title }}</h2>
</div>
{% endmacro %}
{% macro thatItem(item) %}
<div class="other-snippet">
<h2>{{ item.title }}</h2>
</div>
{% endmacro %}
{% macro container(itemRenderer, items) %}
<div class="container">
{% for item in items %}
<div class="container__item">
{{ itemRenderer(item) }}
</div>
{% endfor %}
</div>
{% endmacro %}
And then in the template:
{# template.html.twig #}
{% from "macros.html.twig" import thisItem as itemRenderer, container %}
{% container(itemRenderer, items) %}
And in another template:
{# template2.html.twig #}
{% from "macros.html.twig" import thatItem as itemRenderer, container %}
{% container(itemRenderer, items) %}
The same thing can be achieved with regular includes, although both offer the same possibilities, I think the macro solution is cleaner.
{# snippet.html.twig #}
<div class="this-snippet">
<h2>{{ item.title }}</h2>
</div>
{# container.html.twig #}
<div class="container">
{% for item in items %}
<div class="container__item">
{% include snippetTmpl with { 'item': item } only %}
</div>
{% endfor %}
</div>
{# page.html.twig #}
{% include "container.html.twig" with { 'snippetTmpl': 'snippet.html.twig', 'items': items } only %}
I don't see how it could be possible without modifying container.html.twig, since you are trying to render {{ item }}, which is intended to be HTML, without the raw filter, which is mandatory to mark the content of {{ item }} as HTML-safe.
If you are the owner of the container.html.twig file origin (not sure what you meant by
container.twig and snippet.twig are being pulled into another
application
), maybe you could change {{ item }} to {{ item|raw }}. Then you would just need to be sure the items parameter passed to container.html.twig contains HTML generated by a renderView of snippet.html.twig. Then just be careful container.html.twig is not used somewhere else with HTML-unsafe items.
If you really don't have your hands on it, you may also try to render your template with a Twig environment that has autoescape disabled.
Hope this helps!
EDIT: Since you must do this using gulp-twig, what about something like this:
var titles = ['First snippet', 'second snippet'];
var i, items;
for (i = 0; i < titles.length; i++) {
gulp.src('/path/to/snippet.html.twig')
.pipe(plugins.twig({
data: {
title: titles[i]
}
}))
.pipe(plugins.intercept(function(file){
items[i] = file.contents.toString();
return file;
}));
}
gulp.src('/path/to/container.html.twig')
.pipe(plugins.twig({
data: {
items: items
}
}))
.dest('/path/to/dest.html');

Include a file, but only if it exists?

I'm creating a style guide in Jekyll and using Collections to define different elements of the guide. For example, headings, lists, etc.
I'm trying to separate the Sass into files that match up with the partials, one to one, and I'd like to render the Sass files as part of each collection.
So, something like:
{% if _includes/_sass/{{ entry.title | append: ".scss"}} %}
{% highlight sass %}
{% include _includes/_sass/{{ entry.title | append: ".scss" }} %}
{% endhighlight %}
{% endif %}
Basically, what I want is "Include a file in this directory that has the same name as this entry in my collection. If it doesn't exist, don't break."
How do I do this? I've explored storing the file path in a variable but can't seem to get that to work.
Thanks in advance.
It can be done.
This works on Jekyll 3 but it can certainly be ported to Jekyll 2.
Starting from a base install (jekyll new)
_config.yml
collections:
guide:
sasssamples:
Style guide files
Our samples will be grouped in the _guide collection.
Example file : _guide/header/header1.hmtl
---
title: Header level 1
---
<h1>Header level 1</h1>
SCSS samples
We want our SCSS samples to be included in our css/main.scss and use variables defined in our other SCSS files. Our samples will be integrated at the end of our css/main.scss
We don't want our SCSS samples to render as css so no .scss extension. Switch to .txt extension
We want to access SCSS samples from a list. Let's put them in a sasssamples collection.
Example file : _sasssamples/header/header1.txt
---
---
h1{
color: $brand-color;
border: 1px solid $brand-color;
}
SCSS samples integration
Add this code at the very end of you bootstraping scss file (css/main.scss on a base Jekyll install)
css/main.scss
[ original code ... ]
{% comment %} Selecting a collection the Jekyll 3 way. See https://github.com/jekyll/jekyll/issues/4392 {% endcomment %}
{% assign scssCollection = site.collections | where: 'label', 'sasssamples' | first %}
{% comment %}
Printing documents in sasssamples collection.
All SCSS from style guide are sandboxed in .guide class
This allows us to apply styles only to style guide html samples
{% endcomment %}
.guide{
{% for doc in scssCollection.docs %}
{{ doc.content }}
{% endfor %}
}
The style guide
<h2>Style guide</h2>
{% comment %}Selecting a collection the Jekyll 3 way. See https://github.com/jekyll/jekyll/issues/4392 {% endcomment %}
{% assign guideCollection = site.collections | where: 'label', 'guide' | first %}
{% assign scssCollection = site.collections | where: 'label', 'sasssamples' | first %}
{% comment %} Looping hover style guide samples {% endcomment %}
{% assign samples = guideCollection.docs %}
{% for sample in samples %}
<article>
<h3>Element : {{ sample.title }}</h3>
<h4>Render</h4>
<div class="guide">
{{ sample.content }}
</div>
<h4>html code</h4>
{% highlight html %}{{ sample.content }}{% endhighlight %}
{% comment %}
Changing a path like : _guide/headers/header1.html
to : _sasssamples/headers/header1.txt
{% endcomment %}
{% assign scssPath = sample.path | replace: '_guide', '_sasssamples' %}
{% assign scssPath = scssPath | replace: '.html', '.txt' %}
{% comment %} Try to find a SCSS sample with equivalent path {% endcomment %}
{% assign scssSample = scssCollection.docs | where: 'path', scssPath | first %}
{% comment %}We print SCSS sample only if we found an equivalent path{% endcomment %}
{% if scssSample != nil %}
<h4>SCSS code</h4>
{% highlight css %}{{ scssSample.content }}{% endhighlight %}
{% endif %}
</article>
{% endfor %}
Done!
Seems it only miss on assigning the correct path
{% if _includes/_sass/{{ entry.title | append: ".scss"}}
Need to be replaced to relative path to the scss file:
{% assign scssPath = 'relative/path/to/your/scss/' %}
{% if {{ entry.title | append: ".scss" | prepend: scssPath }} != nil %}

Check if an array is not empty in Jinja2

I need to check if the variable texts is defined or not in index.html.
If the variable is defined and not empty then I should render the loop. Otherwise, I want to show the error message {{error}}.
Basically this in PHP
if (isset($texts) && !empty($texts)) {
for () { ... }
}
else {
print $error;
}
index.html
{% for text in texts %}
<div>{{error}}</div>
<div class="post">
<div class="post-title">{{text.subject}}</div>
<pre class="post-content">{{text.content}}</pre>
</div>
{% endfor %}
How do I say this in jinja2?
I think your best bet is a combination of defined() check along with looking at the length of the array via length() function:
{% if texts is defined and texts|length > 0 %}
...
{% endif %}
To test for presence ("defined-ness"?), use is defined.
To test that a present list is not empty, use the list itself as the condition.
While it doesn't seem to apply to your example, this form of the emptiness check is useful if you need something other than a loop.
An artificial example might be
{% if (texts is defined) and texts %}
The first text is {{ texts[0] }}
{% else %}
Error!
{% endif %}
Take a look at the documentation of Jinja2 defined(): http://jinja.pocoo.org/docs/templates/#defined
{% if variable is defined %}
value of variable: {{ variable }}
{% else %}
variable is not defined
{% endif %}
Is it clear enough? In your case it could look like this:
{% if texts is defined %}
{% for text in texts %}
<div>{{ error }}</div>
<div class="post">
<div class="post-title">{{ text.subject }}</div>
<pre class="post-content">{{ text.content }}</pre>
</div>
{% endfor %}
{% else %}
Error!
{% endif %}
As mentioned in the documentation, you could also write:
{% for text in texts %}
<div class="post">
<div class="post-title">{{text.subject}}</div>
<pre class="post-content">{{text.content}}</pre>
</div>
{% else %}
<div>{{ error }}</div>
{% endfor %}
It handles both the case where texts is undefined, and the case where texts is empty.
This is a neat and simple solution that worked well for me!
{% if texts is defined and texts[0] is defined %}
...
{% endif %}
It's possible that texts could be defined but contain a single list element which is an empty string; For example:
texts = ['']
In this case, testing if texts is defined will produce a true result so you should test the first element instead:
{% if texts[0] != '' %}
..code here..
{% endif %}
You might also want to combine that with the |length filter to make sure it only has one element.
This worked for me when working with the UPS API where if there is only one object in a parent object the child is just an object, but when there is more than one child it's a array of objects.
{% if texts[0] %}
..code here..
{% endif %}
This is what worked for my use case in my Django app:
I needed to pass a queryset as context to an html template and display the block only if the queryset had values
Queryset:
events = Event.objects.filter(schedule_end__gte=date.today()).order_by('-created_at')
Passed context dictionary as follows:
{ "events" : events }
HTML template
{% if events %}
<h3>Upcoming Events</h3>
<ul>
{% for event in events %}
<li><h4>{{ event.title }}</h4></li>
{% endfor %}
</ul>
{% endif %}
This works for me ( But I make sure to return an empty array [] and not None if its empty )
{% if array %}
<table class="table">
...
</table>
{% endif %}
We can check if array is not empty by writing below jinja code.
where the content2 is an array defined under py file. #app.route("/<name>") def home(name): return render_template("index.html", content=name, content2=[])
{% if content2 %}
<div>
<h2>Array elements are available</h2>
{% for con2 in content2 %}
<p> {{con2}} </p>
{% endfor %}
</div>
{% endif %}
Thanks