Create a navigation menu using Twig - php

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>

Related

Parsing current uri to get the current route params inside a partial. How to avoid?

To render TOC (categories tree) inside the base.twig view I call the render() function passing it the corresponding action url:
{% block sidebar %}
{{ render(url('toc_documents_categories')) }}
{% endblock %}
The matching partial view for the '/toc/documents' action (_toc.documents_categories.twig) is defined as follows:
{% set category_id = (current_uri|split('/'))[4] %}
{% macro recursiveCategory(category, active_category_id) %}
<li>
{% if category.children|length %}
<a><span class="icon icon-plus"></span>{{ category.name_displayed }}</a>
{% else %}
{% set active_class = active_category_id == category.id ? 'active' %}
{% set url = app.url_generator.generate('documents_by_category', {category_id: category.id}) %}
<a href="{{ url }}" class="{{ active_class }}">
{{ category.name_displayed }}
</a>
{% endif %}
{% if category.children|length %}
<ul>
{% for child in category.children %}
{{ _self.recursiveCategory(child, active_category_id) }}
{% endfor %}
</ul>
{% endif %}
</li>
{% endmacro %}
{% if categories %}
<div id="categories">
<ul>
{% for category in categories %}
{{ _self.recursiveCategory(category, category_id) }}
{% endfor %}
</ul>
</div>
{% endif %}
As you can see I'm extracting current category's id by parsing current url. This is preceded by setting the current_uri global:
$app->before(function(Request $request) use ($app) {
$app['twig']->addGlobal('current_uri', $request->getRequestUri());
});
Accessing the route information (global.request.attributes.get('_route')) inside the partial view shows the corresponding subrequest route name and not the actual request route name (master request).
Is there a way to avoid manually parsing the current uri and to get the current request route params inside the partial view?
Here's the solution.
render() issues a subrequest, so you have to use the master request context:
use Symfony\Component\HttpFoundation\Request
$app->get('/documents_categories', function(Request $request) use($app) {
$master_request = $app['request_stack']->getMasterRequest();
$current_route = $master_request->get('_route');
$active_category_id = null;
if($current_route === 'documents_by_category') {
$active_category_id = $master_request->get('_route_params')['category_id'];
}
// ... do things: parse toc tree ...
return $app['twig']->render('_toc.documents.categories.twig', array(
"categories" => $toc_tree,
"active_category_id" => $active_category_id
));
})->bind('toc_documents_categories');
Then inside the partial view you have to only reference the passed active_category_id parameter:
{% if categories %}
<div id="categories">
<ul>
{% for category in categories %}
{{ _self.recursiveCategory(category, active_category_id) }}
{% endfor %}
</ul>
</div>
{% endif %}
Thanks to #keyboardSmasher for 'Why not pass the category_id in the render function' comment. Yet I'm not sure if I did it the way he assumed.

symfony twig include form_widget template

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)}} ?

twig looping empty result

i'm building a navigation menu when I loop the results from database it's working properly with this code
foreach ($menu as $item) {
echo $item->name_english . ' ';
if ($item->childs->count()) {
foreach ($item->childs as $row) {
echo $row->name_english . ' | ';
}
}
}
and this id the output
first second child1| child2 | third fourth
when I use the same code in twig I get no results from childs loop
{% if menu %}
<ul>
{% for item in menu %}
<li>{{ item.name_english }}</li>
{% if item.childs.count() %}
<ul>
{% for stuff in item.childs %}
<li>{{ stuff.name_english }}</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
</ul>
{% endif %}
and this is the output
first
second
third
fourth
To check the count of an array in twig you use length filter. Moreover you should be more specific when using twig. use menu is not null instead of menu.
{% if menu is not null and menu|length > 0 %}
<ul>
{% for item in menu %}
<li>{{ item.name_english }}</li>
{% if item.childs|length > 0 %}
<ul>
{% for stuff in item.childs %}
<li>{{ stuff.name_english }}</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
</ul>
{% endif %}

Multiple Header Styles In Timber/Twig

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 %}

Shopify don't show certain items in menu

This is the code for my header menu:
<ul>
{% for link in linklists.main-menu.links %}
<li class="{% if forloop.first %}first{% elsif forloop.last %}last{%endif%}">
{% assign child_list_handle = link.title | handleize %}
{% if linklists[child_list_handle].links != blank %}
<div class="has-dropdown">
<a href="{{ link.url }}" class="clearfix">
<span class="nav-label">{{ link.title | escape }}</span>
<span class="nav-arrow"></span>
</a>
<ul>
{% for childlink in linklists[child_list_handle].links %}
<li>{{ childlink.title | escape }}</li>
{% endfor %}
</ul>
</div>
{% else %}
{{ link.title | escape }}
{% endif %}
</li>
{% endfor %}
</ul>
I am not sure what lanugage this is written in but I want to change it so that any item named "featrued" is not shown in the menu. The sudo code would be something like this:
if name!="featured" then { ... }...
This is what I tried:
{% if link.title != featured %}
...
{% endif %}
Could anyone please help me understand what adjustment would need to be made?
You're close! You just need to enclose the link's title in quotation marks.
Try this:
<ul>
{% for link in linklists.main-menu.links %}
{% if link.title != 'featured' %}
<li class="{% if forloop.first %}first{% elsif forloop.last %}last{%endif%}">
...
</li>
{% endif %}
{% endfor %}
</ul>

Categories