Hugo: how can I add a class for the last post? - css

I'm using this baseof.html to add the class of first-post to the body tag so that I can use specific CSS on the first post of a series of posts which are paginated:
<body class="
{{ with where site.RegularPages "Type" "posts" }}
{{ $first_post = index . 0 }}
{{ if eq $first_post $ }}
first-post
{{ end }}
{{ end }}
">
But how can I add a class for the last post? With this, I get the error “can’t iterate over *hugolib.pageState”:
<body class="
{{ with where site.RegularPages "Type" "posts" }}
{{ $last_post := last 1 $ }}
{{ if eq $last_post $ }}
last-post
{{ end }}
{{ end }}
">
Docs for last: https://gohugo.io/functions/last/

LE: I've re-read you're question and realised I had misunderstood your aim, initially. I've redone the answer, hopefully I got it right this time around.
For adding a CSS class to an item while iterating them, i.e. displaying multiple on the same page, I kept the old answer below.
For adding a class on an item's own page, depending on its position in the global list, try this.
The list & the sorting
Round up the items in $allPosts. By default, I think they are sorted by .Date, descending, i.e. newest first. To force your own order or criteria, you can use sort.
{{ $allPosts := where site.RegularPages "Type" "posts" }}
{{ $allPostsByDate := sort $allPosts ".Date" "asc" }}
The pages of interest
Get whichever items are special. For the first and the last, you can use their respective built-in functions; both first and last return arrays with one element (the page, in this case), so to extract the one element, you can use index.
{{ $firstPost := index (first 1 $allPostsByDate) 0 }}
{{ $lastPost := index (last 1 $allPostsByDate) 0 }}
Compare with the current page
All the code in this example must be included in a template like single.html, which runs for each page. So, for each page that is rendered, you have one last check to make, to see whether the current page is one of the special ones.
I don't know Hugo that well to say if there's a better way of comparing two pages, but .Permalinks seem good enough.
{{ if eq $firstPost.Permalink $.Permalink }} first-post {{ end }}
{{ if eq $lastPost.Permalink $.Permalink }} last-post {{ end }}
The whole thing
With the whole list displayed, for visualising what's what.
{{ $allPosts := where site.RegularPages "Type" "posts" }}
{{ $allPostsByDate := sort $allPosts ".Date" "asc" }}
{{ $firstPost := index (first 1 $allPostsByDate) 0 }}
{{ $lastPost := index (last 1 $allPostsByDate) 0 }}
{{/* on the single page */}}
{{ .Title }} —
{{ if eq $firstPost.Permalink $.Permalink }} first-post {{ end }}
{{ if eq $lastPost.Permalink $.Permalink }} last-post {{ end }}
<br><br>
{{/* on a list */}}
{{ range $allPostsByDate }}
{{ .Title }}
{{ if eq $firstPost.Permalink .Permalink }} first-post {{ end }}
{{ if eq $lastPost.Permalink .Permalink }} last-post {{ end }}
<br>
{{ end }}
Old answer
Using Hugo last
I think there might be a typo there? I can't test now, but I would say that you need to have a dot instead of a question mark. I don't know what the $ variable is, if it's something in Hugo. In fact, that would explain the error. last expects the second argument to be an array, and you're giving it a PageState. So it should probably be something like this:
{{ $last_posts := last 1 . }}
{{/* This will give you an array of length 1, over which you then have to iterate. */}}
{{ $last_post := index $last_posts 0 }}
{{/* or */}}
{{ range $last_posts }}
{{/* last post here */}}
{{ . }}
{{ end }}
Using Hugo len
Following the same pattern, you could get the last index via the array's length. Hugo's len function
{{ $last_index := (len .) - 1 }}
{{ last_post := index . $last_index }}
Using CSS
You can drop the custom treatment for the first and last posts altogether from the templates, and use CSS pseudoclasses, like
:first-child()
:last-child()
So your posts wrapper can have a .posts CSS class, and then, in your stylesheets, you can have something like
.posts:first-child {
/* first post, make it pop */
border-top: 1px dashed red;
}
.posts:last-child {
/* last post, make room */
border-bottom: 1px dashed black;
}
This way, however, you shift the computation over to the client. I don't really think it's that intensive a computation, especially with only a first/last, but it's something to consider.

Related

JSReport & Handlebars IF value is int

Have been struggling with JSreport couple hours today making PDF report for my app. Now met the problem in if statement, where I need to compare index number to integer.
In Javascript I can compare like this if (partKey === 6), but with handlebars this is not working and I'll tried these with no luck:
{{#each computer.parts as |part partKey|}}
<div class="item">
<span>{{ part.productName }}, {{ part.weight }}g</span><br></br>
{{ #if (eq partKey 6) }}
<div style="page-break-before: always"></div>
{{ /if }}
</div>
{{/each}}
I just want to change page after six objects. Also tried these; {{ #is partKey 6 }}, {{ #if_eq partKey 6 }}, {{ #equals partKey 6 }}.
Any ideas which should work?
If i print partKey like this {{ partKey }} it prints nicely. When I try to use it in if it gives a parse errpr, says partKey is Invalid.

twig form multiple block in symfony4

I have a form with block of the different input field.
When I use, standard twig function I get one block:
{{ form(form) }}
If I want to change something inside form I use, for example:
{{ form_start(form) }}
{{ form_widget(form.firstName) }}
{{ form_widget(form.lastName) }}
{{ form_end(form) }}
And everything is fine with this, but here I use JS for adding multiple block of the same field in the form (like can be possible to add multiple person in one form). When I want to edit data, I catch all data from the DB, of course, and want to show blocks in the twig.
{{ form_start(form) }}
{# somehow start loop data from the DB here #}
<div class='block'>
{{ form_widget(form.firstName) }}
{{ form_widget(form.lastName) }}
</div>
{# somehow end loop data from the DB here #}
{{ form_end(form) }}
Is it possible in the Twig, or I should use old school here?
For all who google for same question, answer is here:
http://symfony.com/doc/current/form/form_collections.html

How to passe an array to jquery in twig

I have the following code :
{% set aSessionKeys = app.session.get('aBasket')|keys %}
onkeyup="calculatePrice({{ product['product_price'] }},{{ product['product_id'] }},{{ aSessionKeys }})
And I have an error :
Notice: Array to string conversion
Can you help me please? Thx in advance. So exist a solution to passe this array?
I do like this :
{% set aKeys = aSessionKeys|join(',') %}
{{ dump(aKeys) }}
It shows good, but if I passe to jquery :
calculatePrice({{ product['product_price'] }},{{ product['product_id'] }},{{ aKeys }})
When I do in js methode calculatePrice() :
function calculatePrice(price, product_id, aKeys) {
console.log(aKeys);
var x = document.getElementById("product_quantity_"+product_id);
var total_price = price * x.value;
$("#total_price_"+product_id).html(total_price+" <small> MDL</small>");
sum = 0;
$("#total_price_basket").html();
}
It shows only the first value of aKeys
You have very popular error. You are trying to display array. But how do you imagine that? You can use {{ dump(aSessionKeys) }} for that purpose or use first element: {{ aSessionKeys.0 }} (if it is not an array or object).
But if you want to print all values from this array you can run foreach loop:
{% for element in aSessionKeys %}
{# something here #}
{{ element }}
{# something here #}
{% endfor %}
Obviously (as you are using the |keys filter) aSessionKeys contains an array, and by using {{ aSessionKeys }} in the template Twig will try to output the array as a string, which doesn’t work (or at least is not what you want).
To convert the array to a string, you should use Twig’s join() filter, which will concatenate the array values using a given delimiter string. So, if – for instance – you want to join the values using a comma, this would be the code:
{{ aSessionKeys|join(',) }}

Make Twig syntax from string be parsed

I stored a format of shopping order in database, it's like #{{ number }} (it's just a string), how can use this {{ number }} as a execution of Twig.
For example, in my Controller, when I render the view, I also pass a $number variable:
return $this->render('MyBundle:View:index.html.twig', array('number' => 123));
and in my index.html.twig file, it's something like
{% set orderFormat = some_function_to_get_order_format() %}
// orderFormat will be #{{ number }}
// What can I do to print orderFormat to #123
Try template_from_string function.
{{ include(template_from_string("Hello {{ name }}") }}

How to assign class to invalid Symfony 2 form field, rendered with Twig?

Lets assume that few fields values of my form, build on Symfony 2 and rendered with Twig are not valid and I received validation errors. I want not only to see this errors, but also assign special class to each invalid field.
For example:
<input type="text" class="error">
How can I do that? As I understand, there is need to redeclare my form template. Is there any working example how to assign attributes in case of concrete field validation failure.
All I found now, is that I need to set this class in form template:
{% set attr = attr|merge({'class': attr.class|default('') ~ (errors|length > 0 ? ' error' : '') }) %}
But what I don't understand is how to specify exact field? Any help appreciated.
This works for me:
<div class="input{{ form_errors(form.expiry) == '' ? '' : 'error' }}">
{{ form_widget(form.expiry) }}
</label>
You could also do
{{ form_widget(form.expiry, {'attr': {'class': form_errors(form.expiry) == '' ? '' : 'error'}}) }}
If you use
{{ form(form) }}
for showing your form, I am quite sure you can not accomplish what you want, or at least I am not aware of the possiblity.
If you use something like this
{{ form_row(form.task) }}
{{ form_row(form.dueDate) }}
I am still quite sure you can not get what you want.
My solution for what you need would be to make something like this:
{{ form_start(form) }}
{{ form_errors(form) }}
<div>
{{ form_label(form.task) }}
{{ form_errors(form.task) }}
{{ form_widget(form.task) }}
</div>
<div>
{{ form_label(form.dueDate) }}
{{ form_errors(form.dueDate) }}
{{ form_widget(form.dueDate) }}
</div>
<input type="submit" />
{{ form_end(form) }}
and to simply get information about validation errors from form object and then to replace {{ form_widget(form.task) }} with something like this
{{ form_widget(form.task, {'attr': {'class': 'error'}}) }}
in case that field task failed the validation.
Even more slow and time consuming solution would be to make small twig files that each and every one would actualy represent "your" design for view of each form field and then to call those "little twigs" with an argument which would again come from form object which contains those data about bad validation.
You can read more about form rendering where you actualy make your own form field designs here
http://symfony.com/doc/current/cookbook/form/form_customization.html

Resources