grav modular page - separate content and sidebar - grav

I want to create a page in grav, where I have a content area and a sidebar area.
I want to use on modular.md root template in which I refer to a template that loops and displays both content and sidebar modules.
My problem is: how to distinguish between content and sidebar?
my template looks like this:
{% block body %}
{% block content %}
<section id="info">
<div class="container">
<div class="row">
<div class="col-sm-12">
<div class="row">
<div class="col-sm-8">
<div id="content" class="site-content bg">
<h2 class="heading">{{ page.title }}</h2>
{{ page.content }}
{% for module in page.collection() %}
{{ module.content }}
{% endfor %}
</div>
</div>
<div id="sidebar" class="col-sm-4 sidebar-inner">
{% for module in page.collection() %}
{{ module.content }}
{% endfor %}
</div>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% endblock %}
What I try to achieve is to use filters on both page.collections, so that in one case only "content" (taxonomy.tag was my guess here) and in the other case only sidebar is used.
I could live with naming conventions (sidebar modules have a fixed prefix), but I can't figure it out.

okay, I came along with a hack: I include 'sidebar' in the folder name of the module and filter on module.folder name. I am convinced there is a better solution, though!
<section id="info">
<div class="container">
<div class="row">
<div class="col-sm-12">
<div class="row">
<div class="col-sm-8">
{% if page.content %}
<div id="content" class="site-content bg">
<h2 class="heading">{{ page.title }}</h2>
{{ page.content }}
</div>
{% endif %}
{% for module in page.collection() %}
{% if 'sidebar' not in module.folder %}
{{ module.content }}
{% endif %}
{% endfor %}
</div>
<div id="sidebar" class="col-sm-4 sidebar-inner">
{% for module in page.collection() %}
{% if 'sidebar' in module.folder %}
{{ module.content }}
{% endif %}
{% endfor %}
</div>
</div>
</div>
</div>
</div>

What you can do (I think) is set a value in the fromtmatter of the module
sidebar: true
Then you can filter on that value in your collection like
{% for module in page.collection() %}
{% if page.header.sidebar %}
{{ module.content }}
{% endif %}
{% endfor %}

Related

Twig Wordpress Repeater within a repeater

I would like to ask for help regarding a repeater within a repeater on Wordpress using Twig. The Services section shows up correctly but the Features section within the Services section isn't showing up.
Here is a screenshot of the Wordpress ACF.Click Me
And right below is the code that I am currently using.
Please advise. Thank you!
{% extends "page.twig" %}
{% block additional %}
<div id="page-services">
<section id="services">
<div class="row small-up-1 large-up-1">
<div class="small-12 medium-11 large-9 columns small-centered">
<div class="services-grid animated fadeIn wow">
<p align="center">
{{post.services_desc}}
</p>
</div>
</div>
</div>
<div class="line centered"></div>
</div>
<center>
<div class="row">
<div class="small-12 medium-11 large-9 columns small-centered">
<div class="features-header animated fadeIn wow">
{% for item in post.get_field('services_ist') %}
<div class="column services">
<h2 class="capitalize bold">
{{item.services_title}}
</h2>
{% if item.services_subtitle %}
<h4 class="subtitle">
{{item.services_subtitle}}
</h4>
<div class="line thin"></div>
{% endif %}
{% if item.services_content %}
<div class="description">
{{item.services_content}}
<br><br>
</div>
{% endif %}
{% if feats.services_feat %}
{% for feats in post.get_field('services_feat') %}
<p>{{feats.feat_title}}</p>
{% endfor %}
{% if feats.feats_desc %}
<h4 class="feats description">
{{feats.feats_desc}}
</h4>
{% endif %}
{% endif %}
</div>
{% endfor %}
</center>
</div>
</div>
</div>
</section>
</div>
{% endblock %}
As the ACF Integration Guide says, you shouldn’t use get_field() again when you try to access nested repeater fields:
When you run get_field on an outer ACF field, everything inside is ready to be traversed. You can refer to nested fields via item_outer.inner_repeater
So instead of using:
{% for feats in post.get_field('services_feat') %}
You should use:
{% if feats.services_feat %}
{% for feats in feats.services_feat %}
<p>{{ feats.feat_title }}</p>
{% endfor %}
{# … #}
{% endif %}
I have never done twig before but a quick search got me something.
Change the inner repeater to this:
{% for feats in services_ist.get_field('services_feat') %}
<p>{{feats.feat_title}}</p>
{% endfor %}
This way the second repeater knows that its a child from the first repeater instead of a direct child to the post.

Using 3 templates at the same time with Twig

I am having troubles assembling 3 Twig templates, with {% extends %} and {% use %}.
I know how to assemble 2 templates, thanks to {% extends %} but I don't get how to add a third. I got two different types of errors :
Circular reference detected for Twig template "home.html.twig", path:
home.html.twig -> twtts.html.twig -> home.html.twig
OR
Template "twtts.html.twig" cannot be used as a trait.
I don't understand what's wrong in this :
{# home.html.twig #}
{% extends 'layout.html.twig' %}
{% use 'twtts.html.twig' %}
{% block assets %}
<script src="/assets/js/actions.js"></script>
{% endblock %}
{% block page_title %}Twttr | Home{% endblock %}
{% block content %}
<div class="container d-flex">
<div class="col-md-3"></div>
<div class="col-md-6">
<table class="table col-md-6 mt-5" id="twtts">
{% block twtts %}
{% endblock %}
</table>
</div>
<div class="col-md-3"></div>
</div>
{% endblock %}
{% extends 'layout.html.twig' %}
_
{# twtts.html.twig #}
{% block twtts %}
{% for twtt in twtts %}
<tr class="">
<td class="col-md-6">
<div class="media pr-4 pl-4 pt-4 pb-3">
<a href="/profile/{{twtt.author_name}}">
{% if twtt.pp_url is not empty %}
<img src="{{ twtt.pp_url }}" alt="{{ twtt.author_name }} profile picture" class="align-self-start twtt-thumbnail mr-3 ml-3">
{% else %}
<img src="/assets/pp/default_pp.png" alt="PP-Pro" class="align-self-start twtt-thumbnail mr-3 ml-3">
{% endif %}
</a>
<div class="media-body position-relative">
{% if twtt.rt_author_name is not null %}
<small class="text-muted position-absolute rtwtt-msg">{{ twtt.rt_author_name }} rtwtted :</small>
{% endif %}
<h5 class="mt-0 d-flex justify-content-between">
{{ twtt.author_name }}
<span>{% if twtt.rt_author_name is not null %}{{ twtt.post_time|date("d/m/Y H:i") }}{% else %}{{ twtt.date|date("d/m/Y H:i") }}{% endif %}</span>
</h5>
<p>{{twtt.content|nl2br}}</p>
<div class="media-footer d-flex justify-content-center">
<div class="actions d-flex justify-content-around">
<div class="rtwtt {% if twtt.rtwtted == true %}text-success{% endif %}" data-twtt={{ twtt.id }}><span class="rt-nbr">{{ twtt.rtwtts }}</span><i class="fas fa-retweet"></i></div>
<div class="fav {% if twtt.faved == true %}text-warning{% endif %}" data-twtt={{ twtt.id }}><span class="fav-nbr">{{ twtt.favs }}</span><i class="fas fa-star"></i></div>
</div>
</div>
</div>
</div>
</td>
</tr>
{% endfor %}
{% endblock %}
_
{# layout.html.twig #}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block page_title %}{% endblock %}</title>
{# ... #}
{% block assets %}{% endblock %}
</head>
<body>
<main>
<div class="container">
{% block content %}{% endblock %}
</div>
</main>
</body>
</html>
It used to work when the file twtts.html.twig was in home.html.twig but as I am going to reuse it for a lot of other pages, I was trying to shorten it, and make a file for this template.
If you want to simply render the content from twtts.html.twig, you should use include
{# home.html.twig #}
{% extends 'layout.html.twig' %}
....
{% block content %}
<div class="container d-flex">
<div class="col-md-3"></div>
<div class="col-md-6">
<table class="table col-md-6 mt-5" id="twtts">
{% include 'twtts.html.twig' %} <==== Include where you want to render
</table>
</div>
<div class="col-md-3"></div>
</div>
{% endblock %}
See the following thread for difference between include, use and extend.
Difference between Include, Extends, Use, Macro, Embed in Twig
Thanks!

How can I use loop functionality in twig template?

I want to use a looping functionality in region.html.twig file to I can
wrap elements that outputted from {{ content }} section.
Defult region.html.twig
{% if content %}
<div class="Parent">
{{ content }}
</div>
{% endif %}
The schematic generated Output:
<div class="Parent">
Item_1
Item_2
Item_3
</div>
I try to use below code to create a loop and add a wrapper around of each Item outputted from {{ content }}:
My code:
{% if content %}
<div class="Parent">
{% for item in items %}
<div class="child-wrapper">{{ item.content }}</div>
{% endfor %}
</div>
{% endif %}
The Final outpute that I want to achive:
<div class="Parent">
<div class="child-wrapper">Item_1</div>
<div class="child-wrapper">Item_2</div>
<div class="child-wrapper">Item_3</div>
</div>
If your data is: content = [1, 2, 3, ... , 100]
Then write
{% if content %}
<div class="Parent">
{% for item in content %}
<div class="child-wrapper">{{ item }}</div>
{% endfor %}
</div>
{% endif %}
If your data is array of assotiated arrays like this:
content = [
['content' => 1],
['content' => 2],
]
Then write
{% if content %}
<div class="Parent">
{% for item in content %}
<div class="child-wrapper">{{ item.content }}</div>
{% endfor %}
</div>
{% endif %}

Twig batch with condition

I send Cards(some data) from controller to twig and create row with 3 elems.
{% for elem in Cards|batch(3) %}
<div>
<div class="row">
{% include ':appviews/elements/Card:Card.html.twig' with {'elem': elem} %}
</div>
</div>
{% endfor %}
It works well, but now i need to add card with static html (not from Data), that must be rendered one time.
Is there way, to add only 2 elems in row for the first loop?
{% for elem in Cards|batch(3) %}
<div>
<div class="row">
{% if loop.first %}
<div class="col-md-4 noPadding margin">
<div class="square"> SOME TEXT</div>
</div>
{% endif %}
{% include ':appviews/elements/Card:Card.html.twig' with {'elem': elem} %}
</div>
</div>
{% endfor %}
You cannot do this with "batch" filter. I suggest you use some logical tricks. There can be many of them, depending on:
how often do you want to insert the hardcoded block
how much code/template piece is being reused
For example, if you only need this once in the beginning, you can slice your array once, and then slice it again:
{% for elem in Cards|slice(0, 1) %}
<div>
<div class="row">
{% if loop.first %}
<div class="col-md-4 noPadding margin">
<div class="square"> SOME TEXT</div>
</div>
{% endif %}
{% include ':appviews/elements/Card:Card.html.twig' with {'elem': elem} %}
</div>
</div>
{% endfor %}
{% for elem in Cards|slice(2, Cards|length) %}
...
{% endfor %}
If you think there's too much copy/paste going on, you can create a macro for the "row", with some parameters and have all the logic in it. Then you can control, how, when and what is rendered. But this can be an overload in simple cases, so you decide:
http://twig.sensiolabs.org/doc/tags/macro.html
Well, final code is:
<div class="row rowQuest">
<div class="col-md-4 noPadding margin">
<div class="square">HARDCODED CARD</div>
</div>
{% set firstPartOfQuests = quests|slice(0,2) %}
{% for elem in firstPartOfQuests|batch(2) %}
{% include 'appviews/elements/questCard/questCard.html.twig' with {'elem': elem} %}
{% endfor %}
</div>
{% set lastPartOfQuests = quests|slice(2,quests|length) %}
{% for elem in lastPartOfQuests|batch(3) %}
<div class="row rowQuest">
{% include ':appviews/elements/questCard:questCard.html.twig' with {'elem': elem} %}
</div>
{% endfor %}
PS i still don't understand why i can't use for cycle without batch.
{% for elem in first2Quests %}
{% include 'appviews/elements/questCard/questCard.html.twig' with {'elem': elem} %}
{% endfor %}

How to handle twig view and bootstrap 3 rows/columns?

I have an Article entity and I made a findAll() in the controller.
I rendered each article in a div with the col-md-6 class.
But foreach 2 articles I must wrap these divs in a row div.
How can I do this with twig ?
Thanks.
EDIT :
I tried your code (NHG) like this:
{% for article in articles %}
{% if loop.index % 2 == 0 %}
<div class="row"></div>
{% endif %}
<div class="col-md-6">
<article class="well well-sm">
<img src="{{ article.image }}" alt="{{ article.title }}" class="img-thumbnail">
<h2 class="h3 text-center">{{ article.title }}</h2>
<div class="alert alert-success well-sm">
{{ article.content|striptags|slice(0, 235) }}...
</div>
<a class="btn btn-default btn-sm pull-right" href="#">{{ article.comments|length }} Comments</a>
<div class="btn-group btn-group-sm">
{% for tag in article.tags %}
<a class="btn btn-default">{{ tag.name }}</a>
{% endfor %}
</div>
</article>
</div>
{% endfor %}
But it doesn't work.
I want to have something like this :
<div class="row">
<div class="col-md-6"></div>
<div class="col-md-6"></div>
</div>
<div class="row">
<div class="col-md-6"></div>
<div class="col-md-6"></div>
</div>
<div class="row">
<div class="col-md-6"></div>
<div class="col-md-6"></div>
</div>
UPDATED:
As #Maerlyn suggested:
{% for row in articles|batch(2) %}
<div class="row">
{% for article in row %}
<div class="col-md-6">
// your content
</div>
{% endfor %}
</div>
{% endfor %}
OLD way:
Use loop.index (doc: The loop variable), modulo (doc: Math operators) and if (doc: if statement )
{% for article in articles %}
{% if loop.index % 2 == 1 %}
<div class="row">
{% endif %}
<div class="col-md-6">
// your content
</div>
{% if (loop.index % 2 == 0 or loop.last) %}
</div>
{% endif %}
{% endfor %}
{% for row in articles|batch(2) %}
<div class="row">
{% for article in row %}
<div class="col-md-6">
// your content
{% if loop.index is divisibleby(3) %}
</div>
{% endif %}
{% endfor %}
</div>
{% endfor %}
I suggest :
<div class="row">
{% for article in articles %}
{% if (loop.index0 % 2 == 0) and not loop.first %}
</div>
<div class="row">
{% endif %}
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6">
// your content
</div>
{% endfor %}
</div>

Resources