I'm coding up a theme using Timber for Wordpress, which uses the twig templating engine.
I currently have different data needed for header sections on three pages.
Home Page > BG Slider & Title
Blog Page > BG & Pinned Post
Other Pages > Regular BG Image
The navigation is also supposed to be the same over each of these page headers.
I'm unsure of the best way to use twig to solve this problem.
In my base.twig I currently have:
{% block header %}
<div class="wrapper">
<h1 class="hdr-logo" role="banner">
<a class="hdr-logo-link" href="/" rel="home">{{site.name}}</a>
</h1>
<nav id="nav-main" class="nav-main" role="navigation">
<ul class="nav">
{% for item in menu.get_items %}
<li class="nav-item {{item.classes | join(' ')}}">
<a class="nav-link" href="{{item.get_link}}">{{item.title}}</a>
{% if item.get_children %}
<ul class="nav nav-drop">
{% for child in item.get_children %}
<li class="nav-drop-item">
<a class="nav-link" href="{{child.get_link}}">{{child.title}}</a>
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
</nav><!-- #nav -->
</div>
{% endblock %}
I could copy and paste this block onto each page, ie add it to
home.twig, blog.twig and other.twig
But i am repeating myself constantly, and it feels wrong. I need to feed through this on each page, without repeating my navigation each time i do it.
Does anyone have a suggestion as to how to fix?
What you want are Twig macros and/or Includes
Includes
can be used to include another .twig file in your template.
You can add variables to an Include also, but by default code inside the include have acccess to global context. eg. all template variables.
{# template.html will have access to the variables from the current context and the additional ones provided #}
{% include 'template.html' with {'foo': 'bar'} %}
{% set vars = {'foo': 'bar'} %}
{% include 'template.html' with vars %}
Macros
Are isolated from the global context, and can only work with variables/parameters that you supply to it.
{# defineit #}
{% macro input(name, value, type, size) %}
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
{% endmacro %}
{# then calling it #}
<p>{{ _self.input('username') }}</p>
With both Includes and Macros you can easily do infinitely nested trees, when you include itself in the loop.
Example of nested tree with include:
{# content of tree.twig file for include#}
{% for item in menu.get_items %}
<li class="nav-item {{item.classes | join(' ')}}">
<a class="nav-link" href="{{item.get_link}}">{{item.title}}</a>
{% if item.get_children %}
<ul class="nav nav-drop">
{# recursion #}
{% include 'tree.twig' with { menu : item.get_children } %}
</ul>
{% endif %}
</li>
{% endfor %}
Related
I'm currently using symfony 3.2. and now i have a link like this :
http://link.com?lang=en and in config/parameters I have allowed_locales -en, -ru
How could i create a language switcher in twig template like this :
EN<img src="{{ asset('assets/images/arrow-down.svg') }}" alt="arrow" class="arrow-down" />
<div class="locales-content" style="left:0;">
Russian
English
</div>
Here is what I did in my application, feel free to adapt it to your needs. It creates a dropdown with two links that redirect to the same page, but change the _locale parameter. In case there is no route in the request, it creates two links that redirect to the homepage.
<li class="dropdown">
{% if app.request.locale == 'ru' %}Russian{% else %}English{% endif %} <span class="caret"></span>
<ul class="dropdown-menu">
{# Check if there is a route and some parameters in the request #}
{% if app.request.get('_route') is not empty and app.request.get('_route_params') is not null %}
{# English #}
English
{# Russian #}
Russian
{# If there is no route in the request, redirect to the homepage #}
{% else %}
{# English #}
English
{# Russian #}
Russian
{% endif %}
</ul>
</li>
I'm using the following code to include a template for the data-prototype:
<ul class="factuurDetails list-unstyled"
data-prototype="{% filter escape %}{% include 'FactuurBundle:Default:prototypeCreditnotaDetail.html.twig' with {'form': form.creditnotaDetails.vars.prototype} %}{% endfilter %}">
{% for detail in form.creditnotaDetails %}
<li>
{{form_widget(detail) }}
</li>
{% endfor %}
</ul>
But how do I apply this same template prototypeCreditnotaDetail.html.twig to the {{form_widget(detail)}} ?
How do you create a navigation menu using Twig? Note that I am not using Sympony2.
Given the following array passed to a Twig template:
$menu_main=array(
'active'=>2,
'menu'=>array(
array('components_id'=>2,'name'=>'First Link','newwindow'=>0),
array('components_id'=>3,'name'=>'Second Link','newwindow'=>0),
array('components_id'=>7,'name'=>'Third Link','newwindow'=>1),
array('components_id'=>8,'name'=>'Forth Link','newwindow'=>0)
)
);
I am trying to create the following navigation menu:
<ul>
<li class="first active">First Link</li>
<li>Second Link</li>
<li>Third Link</li>
<li class="last">Forth Link</li>
</ul>
Below is my non-working attempt:
{#
menu_main is an associated array containing active key and menu key.
menu is an associated array of containing name, components_id, and whether it should open a new window
#}
<ul>
{% for item in menu_main.menu %}
<li
{% if item.components_id == menu_main.active %}
class="active"
{# How do I add first and last class? Maybe using length? #}
{% endif%}>
{{item[1]}}{% if item.newwindow %} target="_blank" {% endif%}>
</li>
{% endfor %}
</ul>
This should work for you.
As $main_menu['menu'] array is associative twig won't understand item[0] or item[1]
{#
menu_main is an associated array containing active key and menu key.
menu is an associated array of containing name, components_id, and whether it should open a new window
#}
<ul>
{% for item in menu_main.menu %}
{% set classes = "" %}
<li
{% if item.components_id == menu_main.active %}
{% set classes = classes~' active ' %}
{% endif%}>
{% if loop.first %}
{% set classes = classes~' first ' %}
{% elseif loop.last %}
{% set classes = classes~' last ' %}
{% endif %}
<a class="{{ classes }}" href="index.php?cid={{ item['components_id'] }}">{{item['name']}}</a>
</li>
{% endfor %}
</ul>
im frecuent user of this forum and always find answers from other users, but this time i need to ask my own question.
At this moment im editing a wp template based on twig that i bought, right now im facing a fancybox gallery which just call the first image, and i need to add the gallery rel to the other images included in the post. i already found the missing code which call the rest of images from the post ID, but i dont know how to express it by the twig structure and incorporate it to the original code of the page
this is the code which display the gallery:
{% if wp.get_post_meta(post.ID, '_property_slides', TRUE) %}
<div class="carousel property">
<div class="preview">
<a href="{{ wp.get_post_meta(post.ID, '_property_slides', TRUE).0.imgurl }}" class="fancybox">
<img src=" {{ wp.get_post_meta(post.ID, '_property_slides', TRUE).0.imgurl }}" alt="">
</a>
// the php code expressed on twig goes here //
</div>
<!-- /.preview -->
<div class="content">
<ul>
{% for slide in wp.get_post_meta(post.ID, '_property_slides', TRUE) %}
{% if loop.first %}
<li class="active" >
{% else %}
<li>
{% endif %}
<img src="{{ slide.imgurl }}" alt="">
</li>
{% endfor %}
</ul>
<a id="carousel-prev" href="#">{{ wp.__('Previous', 'aviators') }}</a>
<a id="carousel-next" href="#">{{ wp.__('Next', 'aviators') }}</a>
</div>
<!-- /.content -->
</div><!-- /.carousel -->
{% endif %}
This is the php code i need to implement on twig
add_filter(‘wp_get_attachment_link’,'add_gallery_id_rel’);
function add_gallery_id_rel($link){
global $post;
return str_replace(‘<a href’, ‘<a rel=”galeria’. $post->ID .’” href’, $link);
}
I really appreciate if you can help me with this (sorry about my weird english)
That code is a filter applied to the regular Wordpress functions, you are using a Twig Wrapper so that code won't work for you.
Try modifying the twig loop.
{% for slide in wp.get_post_meta(post.ID, '_property_slides', TRUE) %}
#... rest of the code
<a rel="galeria{{ post.ID }}" href="{{ slide.imgurl }}">
<img src="{{ slide.imgurl }}" alt="">
</a>
#... rest of the code
{% endfor %}
Skip the first slide using slice:
{% for slide in wp.get_post_meta(post.ID, '_property_slides', TRUE)|slice(1) %}
#... rest of the code same as above
I would like to check whether a block is overriden in a child template.
template.html.twig:
<html>
...
<nav class="menu">
{% block menu %}
{% endblock %}
</nav>
...
<div class="contents">
{% block contents %}
{% endblock %}
</div>
...
</html>
page1.html.twig -- contains a menu:
{% extends '::template.html.twig' %}
{% block menu %}
<ul>
<li>...</li>
</ul>
{% endblock %}
{% block contents %}
Hello World!
{% endblock %}
page2.html.twig -- does not contain a menu:
{% extends '::template.html.twig' %}
{% block contents %}
Hello World!
{% endblock %}
I would like to show the nav element in the template, only if it is overriden in the child template (the goal is to avoid an empty <nav /> element).
The Twig syntax has a is empty test, but it only applies to variables, not blocks.
What am I doing wrong?
You can set a variable in your child template check for that:
{# template.html.twig #}
{% if show_menu is not empty %}
<nav class="menu">
{% block menu %}
{% endblock %}
</nav>
{% endif %}
{# page1.html.twig #}
{% set show_menu = true %}
Maybe you should also consider putting this markup in the block definition for a more straight-forward approach.
You could add a wrapper to your nav element and then override it in page2 to make it blank when you don't want a menu. This doesn't require an extra variable.
template.html.twig
... snip ...
{% block nav %}
<nav class="menu">
{% block menu %}
{% endblock %}
</nav>
{% endblock %}
... snip ...
page2.html.twig
{% extends '::template.html.twig' %}
{% block nav '' %} {# here's the kicker ... #}
{% block contents %}
Hello World!
{% endblock %}
In any other page that has a menu you would continue to override the 'menu' block normally.