How can I detect the last item in a for loop in a Pebble Template? - pebble

How can I detect the last item in a list in a for loop in a Pebble Template (http://www.mitchellbosecke.com/pebble/home)?
I am using Pebble to generate JSON. I have a list of objects that I need to iterate over and I need to include a comma after each one except for the last one.
Here's the relevant template code where I tired to use the loop.index and loop.length in an IF statement., but it doesn't work (I'd need to check for loop.length -1 anyway).
Template:
"menu": {
"items": {
{% for menuItem in menuItems %}
"{{ menuItem.name }}": "{{ menuItem.value }}"{%- if loop.index < loop.length %},{% endif %}
{%- endfor %}
}
}
Example desired Output:
"menu": {
"items": {
"item1": "Item 1",
"item2": "Item 2
}
}
I've used the Jinja2 Python template engine before, which has a syntax similar to Pebble. Jinja2 also has an index.last property that is a boolean and can be used in an IF statement like this. I don't know of anything similar in Pebble.
Nathan

While inside of the loop, Pebble provides a couple of special variables to help you out:
loop.index: a zero-based index that increments with every iteration.
loop.length: the size of the object we are iterating over.
loop.first: True if first iteration
loop.last: True if last iteration
loop.revindex: The number of iterations from the end of the loop
Documentation is updated with missing variables here
https://github.com/PebbleTemplates/pebble/wiki/for

First of all, I think you should check out dedicated JSON libraries like Jackson or GSON. It may be more adapted to what you are tying to do.
That said, I ran into the same problem and the latest version includes in the loop the properties loop.first loop.last and loop.revindex.
I found it in the following test.
https://github.com/PebbleTemplates/pebble/blob/11dd4783d44afbeb269c9d98cf9579aad0e53394/pebble/src/test/java/com/mitchellbosecke/pebble/CoreTagsTest.java#L177

Related

Pass current route to template

I've been playing around with golang for a couple of weeks now, and recently started to look into the revel framework.
In other languages/frameworks that I've used, it's always been possible to check what the current route is from within the template. Something that made it easier for me to keep things like navigation in a separate template file, and just do this:
<!-- in header template -->
<ul class="nav">
{{ if eq .req.action "App.Index" }}
<li class="active"><a href="#">
{{ else }}
<li><a href="{{url "App.Index" }}">
{{end}}
Home</a></li>
<!-- other links here -->
</ul>
This isn't the actual code I'm writing, but I hope this makes it clear what the idea is: Have a template for the navigation handy, and set classes/links according to which action is the current one.
After some time going through the source code, and a number of golang template examples, I couldn't quite see any way in which the current action or anything else is exposed to the template.
To get around this, I'm currently using a func interceptor which automatically sets a render argument for me:
func prependRoute(c *revel.Controller) revel.Result {
c.RenderArgs["_current_route"] = c.Action
return nil
}
This is working fine, but it feels a bit hacky. I think I'm probably missing something really obvious that would allow me to reserve func interceptors for when I really need them.
The question, then, is simple: What is the correct, and most reliable way to find out what the current action is in a revel template?

How to remove line break after a tag

I use Django template system to do code generation (not only for HTML). I'm somehow troubled by redundant line breaks in django templates.
Here is an example. The template is as below:
// something
{% for element in elements %}
Element: {{ element.name }},
{% endfor %}
// something else
The rendered output will be:
// something
Element: foo
Element: bar
// something else
Expected rendered output should be:
// something
Element: foo
Element: bar
// something else
After googled a bit, I know I can use {% spaceless %} to remove any white spaces in rendered output. It is quite useful for HTML, but will not work for other languages. My current solution is to add a special string after a tag and replace them with empty string in output.
Is there any better solution to remove line break after a tag?
For your production environment you might consider minifying your html to get that little bit more performance. For example using https://pypi.python.org/pypi/django-htmlmin.
If you are only interested in the esthetics, then the .strip function as noted by e-nouri is probably your best answer.
You can use .strip on your attributes to strip/trim.
// something
{% for element in elements %}
Element: {{ element.name.strip }},
{% endfor %}
// something else
The line breaks in code shouldn't matter, but you can avoid them when you put everything in one line, which doesn't look that good anymore:
{% for element in elements %}Element: {{ element.name.strip }},{% endfor %}

Can't substring in Django template tag

I am trying to add a trailing 's' to a string unless the string's last character is an 's'. How do I do this in a Django template? The [-1] below is causing an error:
{{ name }}{% if name[-1] != "s" %}s{% endif %}
try the slice filter
{% if name|slice:"-1" != "s" %}
{% if name|slice:"-1:"|first != "s" %}s{% endif %}
Django's slice filter doesn't handle colon-less slices correctly so the slice:"-1" solution does not work. Leveraging the |first filter in addition, seems to do the trick.
The Django template system provides tags which function similarly to some programming constructs – an if tag for boolean tests, a for
tag for looping, etc. – but these are not simply executed as the
corresponding Python code, and the template system will not execute
arbitrary Python expressions.
Use the slice built-in filter.
Not sure if this is what you're looking for, but django has a built-in template filter that pluralizes words. It's called just that: pluralize.
You'd want something like this:
{{name | pluralize}}
Take a look at https://docs.djangoproject.com/en/dev/ref/templates/builtins/
{% if name|last != "s" %} does the job

django template filter

I have this code situation
{% if 1|floatformat in '7,10' %}
yes
{% else %}
no
{% endif %}
the return result is always set to "yes", how to make the result return "no"
please help.
thanks
It's hard to make out from your example what exactly is happening here, because you're substituting dummy data for what's really coming out of the database.
As you've written it, the result will always be "yes" because the string '7,10' contains the string '1'.
It sounds like what you're trying to achieve is:
If this number is in this list, then yes, otherwise no.
So let's rewrite this template to be a little more real:
{% if mynumber in yeslist %} yes {% else %} no {% endif %}
This assumes that:
mynumber is a number
yeslist is a list of numbers
I'm not sure what you're using floatformat for in this case.
If the above assertions aren't true, and you have to use strings, then your work is much harder, and you should be processing yeslist server-side. For example, if yeslist is just a string like "7,10,123,93,9,19,83", then figuring out if the number 8 is in the list will be needlessly difficult in templates. Much easier to do it in your view:
def myview(request):
ctx = {}
# ... do some work ...
# yeslist now has a string like "7,10,123,93,9,19,83"
ctx['yeslist'] = yeslist.split(',')
# ... do more work, and render the response ...
Now, {% if '8' in yeslist %} will no longer return a false positive, because it's not doing a substring match, it's doing list membership.
1|floatformat returns 1 and the condition checks if 1 is in '7,10', since django considers '7,10' as a string it returns True. Try passing a list and it will return no

Is there any reason why you can't compare a template filter to a string in Django template syntax?

I'm trying to do this, and failing. Is there any reason why it wouldn't work in Django template syntax? I'm using the latest version of Django.
{% ifequal entry.created_at|timesince "0 minutes" %}
It doesn't work because it's not supposed to work. What you're asking for is not part of the template language.
You can't apply a filter in the middle of tag like {% ifequal. When a template tag uses a variable, it doesn't expect an expression, it expects a variable, nothing more.
That kind of logic -- extracting time since, comparing, etc. -- is what you're supposed to do in your view function.
Your view function then puts a "zerominutes" item in the context for the template to use. Templates just can't do much processing.
They're designed to do the minimum required to render HTML. Everything else needs to be in your view function.
{% ifequal %} tag doesn't support filter expressions as arguments. It treats whole entry.created_at|timesince as identifier of the variable.
Quik workaround: introduce intermediate variable with expresion result using {% with %} like this:
{% with entry.created_at|timesince as delta %}
{% ifequal delta "0 minutes" %}
....
{% endifequal %}
{% endwith %}
See ticket #5756 and links in its comments for more information. A patch for Django in ticket #7295 implements this functionality. A broader refactoring of the template system based on #7295 is proposed in ticket #7806, and it would fix this issue among others.
I don't think making such comparisons to work would be against the design philosophy of Django templates.
I think you can, though I don't see any uses of it my code base. Maybe entry.created_at|timesince isn't producing the value you expect. Try putting
X{{entry.created_at|timesince}}X
into your template and seeing what value it produces. The X's are so you can see leading or trailing space, in case that is the problem.
I finally gave up on using the Django template language for anything other than the simplest pages. Check out Jinja2 for an almost-syntax-compatible alternative. And yes, you can choose which to use on a page-by-page basis.