Optional html class in twig template - symfony

I have a simple reusable twig (generic.html.twig) template which look like:
<ul>
{% for item in list %}
<li>
...
</li>
{% endfor %}
</ul>
I use it many times in my code like this:
{% include "#Toto/generic.html.twig" with {"list": toto} %}
My problem is that I sometimes want to include a CSS class on the <ul> generated by my twig template. So sometimes I would like for it to render like this:
<ul class="myclass">
while other times I'd like for it to render without the class, like this:
<ul>
How do I implement optional classes in my twig template?

I prefer the use of defined
<ul{% if ulClass is defined %} class="{{ ulClass }}"{% endif %}>
{% for item in list %}
<li>
...
</li>
{% endfor %}
</ul>

I would use something like:
{% include "#Toto/generic.html.twig" with {"list": toto, "status": true} %}
And check against status boolean
<ul {{ status ? 'class="myclass"' : '' }}>

Related

How to get current page in pages Symfony 5 without duplicate code?

I want get the current page and give an active class without duplicate code in symfony 5.
Here is the code example :
<li class="{{ app.request.get('_route_')=='home' ? 'active':''}}">
Home
</li>
<li class="{{ app.request.get('_route_')=='contact' ? 'active':''}}">
Contact
</li>
</ul>
As an extension of the answer of #DhiaDjobbi
I've you really want to reduce "duplicate" code, I'd go with defining the menu in an array as well
{% set active_page = app.request.get('_route') %}
{% for page in pages %}
<li {% if page == active_page %} class="active" {% endif %} >
{{ page }}
</li>
{% endfor %}
U can create a twig variable in Template its better readable.
{% set page = app.request.get('_route') %}
Then use If Condition to test.
<li {% if page == 'home' %} class="active" {% endif %} >
Home
</li>
<li {% if page == 'contact' %} class="active" {% endif %} >
Contact
</li>

How to interpolate a variable in a twig loop to render a file?

I am looping through a list of social profiles to render the logo. Each social item has a field of svg_icon_code that looks like "twitter" or "facebook". I want to use this code to render the correct file. Currently I am just hardcoding in "twitter" resulting in all logos being the Twitter svg. How can I use the icon_code to render the correct svg?
{% for item in options.social_media_links %}
<li class="header__social-media-links__item">
<a href="{{item.link}}" target="_blank">
{% include 'component/svg-twitter-icon.twig' %}
</a>
</li>
{% endfor %}
You need to concat the icon like this,
{% for item in options.social_media_links %}
<li class="header__social-media-links__item">
<a href="{{item.link}}" target="_blank">
{% include 'component/svg-'~item.svg_icon_code~'-icon.twig' %}
</a>
</li>
{% endfor %}
If an icon doesn't have a template or the template is missing you can do this to render a default icon
{% for item in options.social_media_links %}
<li class="header__social-media-links__item">
<a href="{{item.link}}" target="_blank">
{% include [ 'component/svg-'~item.svg_icon_code~'-icon.twig', 'component/svg-default-icon.twig' ] %}
</a>
</li>
{% endfor %}

What is the difference between include and block in Twig?

I'm trying to make a bootstrap theme for PhileCMS which uses Twig. Right now I'm working on the menu. I've been searching to find out how to make a page active, and I've been seeing stuff about blocks. Right now my index.html looks something like this
{% include 'header.html' %}
<body>
{% include 'nav.html' %}
<div class="container"}
{{ content }}
{% include 'footer.html' %}
My nav.html looks something like this:
<div class="header clearfix">
<nav>
<ul class="nav nav-pills pull-right">
<li role="presentation"><a class="{% if app.request.attributes.get('_route') starts with 'home' %}active{% endif %}">Home</a></li>
<li role="presentation"><a class="{% if app.request.attributes.get('_route') starts with 'about' %}active{% endif %}">About</a></li>
<li role="presentation"><a class="{% if app.request.attributes.get('_route') starts with 'contact' %}active{% endif %}">Contact</a></li>
</ul>
</nav>
<h3 class="text-muted">{{ site_title }}</h3>
</div>
Is this proper coding practice, or should I be doing something with blocks? I don't really understand how blocks work.
You can include whole new template with new blocks. - That is what include do. You inject a template or piece of template defined in other file. So:
{% include 'nav.html' %}
will inject whatever you have put there and it will replace this whole phrase, this line of code with content of nav.html.
On the other hand when you use {% block body %} for example you override this body block which is inherited from parent template. For example:
If you have block named body in base.html.twig and you will inherit from it like this in another template(let's say blog.html.twig):
{% extends 'base.html.twig' %}
and then do this:
{% block body %}
Hello World
{% endblock %}
You will put this hello world inside of body block in base.html.twig.
I hope it's now clear to you.
P.S
If you want to use twig make sure you use twig extension!
If you are asking for best-practices, then as mentioned in the Symfony's Templating documentation:
When building your application, you may choose to follow this method or simply make each page template extend the base application template directly (e.g. {% extends 'base.html.twig' %}). The three-template model is a best-practice method used by vendor bundles so that the base template for a bundle can be easily overridden to properly extend your application's base layout.
The idea behind this is to have:
1- a base template (level 1)
2-A layout template (level 2)
3-An individual template (level 3)
Here is a sample code that illustrates this (originally from the Symfony2) documentation
{# layout.html.twig #}
{% extends 'base.html.twig' %}
{% block body %}
<h1>Blog Application</h1>
{% block content %}{% endblock %}
{% endblock %}
{# index.html.twig #}
{% extends 'layout.html.twig' %}
{% block content %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}
p.s: Even tough you wouldn't be dealing with Symfony2, but IMHO the principle should be the same, since we are using the Twig templating engine.

Recursive parsing of hierarchical Twig layout with parents, children, grandchildren

In my SF2 project I have an entity (Category) which I am representing in a hierarchical format with a parent at the top, followed by children, grandchildren etc.
The Category entity has a getChildren method, which works and returns Category entity objects.
I'm trying to work out a way to make this layout more dynamic, rather than having to explicitly set children and grandchildren variables within the template.
Is there a better way to do this?
<ul class="sortable">
{% for cat in cats %}
{% set children = cat.getChildren %}
<li id="menuItem_{{ cat.id }}">
<div data-id="{{ cat.id }}">
<span>{{ cat.name }}</span>
</div>
{% for child in children %}
{% set grandchildren = child.getChildren %}
<ul>
<li id="menuItem_{{ child.id }}">
<div data-id="{{ child.id }}">
{{ child.name }}
</div>
{% for grandchild in grandchildren %}
<ul>
<li id="menuItem_{{ grandchild.id }}">
<div data-id="{{ grandchild.id }}">
{{ grandchild.name }}
</div>
</li>
</ul>
{% endfor %}
</li>
</ul>
{% endfor %}
</li>
{% endfor %}
</ul>
so what you are trying to achieve is recursive parsing of a tree in twig right?
If so, have a look at macros
.
{% import _self as macros %}
{% macro showChild(object) %}
{% import _self as macros %}
<ul>
{% for child in object.children %}
{{ macros.showChild(child) }}
{% endfor %}
<li id="menuItem_{{ object.id }}">
<div data-id="{{ object.id }}">
{{ object.name }}
</div>
</li>
</ul>
{% endmacro %}
<ul class="sortable">
{% for cat in cats %}
{{ macros.showChild(cat) }}
{% endfor %}
</ul>
that's all :)
let me know if you need help
EDIT 1:
If you want to use the macro in another file, remove the "import _self" line and just import it with an alias in another file:
index.html.twig:
{% import 'macro_file_name.html.twig' as macros %}
then you can use the same notation to call it

Is it possible to style posts in octopress according to its tags?

I need to style all the posts in Octopress which have the tag 'old' differently. Like, show only the title and no image in archives and keep them separated! How can I do this? (Note : There are nearly 1,500 posts with the old tag)
You could simply use the tags as css classes:
<ul>
{% for post in site.posts %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
That way you can style the link easily via css for any tag.
I'll assume you have a old class that differentiates the posts or that you can style a list of old posts with a old_posts class. You can create two separate lists:
<ul class="old_posts">
{% for post in site.tags.old %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
<ul class="new_posts">
{% for post in site.posts %}
{% unless post.tags contains 'old' %}
<li>{{ post.title }}</li>
{% endif %}
{% endfor %}
</ul>
Or you can create one list with the old posts receiving a special class old:
<ul>
{% for post in site.posts %}
{% if post.tags contains 'old' %}
<li>{{ post.title }}</li>
{% else %}
<li>{{ post.title }}</li>
{% endif %}
{% endfor %}
</ul>
Basically, site.posts, post.tags and site.tags.TAGNAME, together with Liquid's if-else, for and contains are able to do most of the tasks related to styling specifically tagged posts.

Resources