I have a block 'left_area' defined in a base appengine template
{% block left_area %}
<div class="span3">
Left area content
</div>
{% endblock %}
In a child template, I want to override this block inside an if
{% if not user %}
{% block left_area %}
<div class="span2">
</div>
{% endblock %}
{% endif %}
This is not working for some reason. Any suggestion?
In jinja you can solve it with super(), which renders the parent block:
{% block left_area %}
{% if not user %}
<div class="span2">
</div>
{% else %}
{{ super() }}
{% endif %}
{% endblock %}
Related
I need to do two things; add products with extra colours to the collections loop (which I've done below, via a snippet elsewhere), however I need to skip when the variant doesn't have its own image which I've yet to figure out.
The break I attempted is in the preview code:
{% for option in product.options %}
{% if option == 'Colour' %}
{% assign index = forloop.index0 %}
{% assign colourlist = '' %}
{% assign colour = '' %}
{% for variant in product.variants %}
{% capture colour %}
{{ variant.options[index] }}
{% endcapture %}
{% if variant.image.src %}
{% break %}
{% endif %}
{% unless colourlist contains colour %}
{% include 'product-grid-item' %}
{% capture tempList %}
{{colourlist | append: colour | append: " " }}
{% endcapture %}
{% assign colourlist = tempList %}
{% endunless %}
{% endfor %}
{% endif %}
{% else %}
<div class="grid-item">
<p>{{ 'collections.results.no_products' | t }}</p>
</div>
{% endfor %}
{% endfor %}
The keyword you're looking for, to skip the current iteration of the loop and move to the next, is {% continue %}
For example:
{% for variant in product.variants %}
{% if variant.featured_image == blank %}
{% continue %}
{% endif %}
<!-- HTML STUFF -->
{% endfor %}
I have a Django template page that looks something like this.
{% extends "base.html" %}
{% load wagtailcore_tags static %}
{% block body_class %}template-bidpage{% endblock %}
{% block content %}
{% for prp in page %}
<p>{{ prp }}</p>
{% endfor %}
{% endblock content %}
But loading this page up gives me the error 'MyPage' object is not iterable. How do I display each proeprty of my MyPage object?
You should write a method inside your model class to return the properties.
class MyPage(Page):
# your fields go here
def get_properties(self):
# return all properties
Then in your templates
{% extends "base.html" %}
{% load wagtailcore_tags static %}
{% block body_class %}template-bidpage{% endblock %}
{% block content %}
{% for prp in page.get_properties %}
<p>{{ prp }}</p>
{% endfor %}
{% endblock content %}
I want to specify the HTML for each streamfield block with a for loop in my template. However, the image part is getting complaints, and I'm sure I'm not doing it the right way.
Invalid block tag on line 15: 'image', expected 'elif', 'else' or 'endif'. Did you forget to register or load this tag?
{% extends "base.html" %}
{% load wagtailcore_tags %}
{% load wagtailuserbar %}
{% block content %}
<h1>{{page.title}}</h1>
<small>{{page.date}}</small>
{% for block in page.body %}
{% if block.block_type == 'heading' %}
<h2>{{ block.value }}</h2>
{% elif block.block_type == 'quote' %}
<blockquote class="blockquote text-center"><p class="mb-0">{{ block.value }}</p></blockquote>
{% elif block.block_type == 'image' %}
{% image block.value width-1200 class="img-fluid" alt="Responsive image" %}
{% endif %}
{% endfor %}
{% endblock %}
{% wagtailuserbar %}
I forgot to include:
{% load wagtailimages_tags %}
My bad :)
I'm using sonata admin in my project. In the list view sonata refresh the page when I clik to get the second list from pagination. That's the default behavior of sonata.Is there any way to use ajax call with list view pagination !!!
Same question when using sortable list view.
Thanks.
This is possible, but you have to overwrite some of the basic functionality of the sonata admin bundle.
Tested with
symfony 2.6.6
sonata admin-bundle dev-master (4f23e1a30e49681bf8ebdbbae549848784be7699)
1. Edit your bundles services.yml
You have to implement your own CrudController and a new list template. Tell this in services.yml
sonata.admin.youradmin:
class: Your\Bundle\Admin\YourAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: "groupname", label: "grouplabel" }
arguments:
- ~
- Your\Bundle\Entity\EntityClass
- YourBundle:YourNewCrud # <- add your crud class here
calls:
- [ setLabelTranslatorStrategy, ["#sonata.admin.label.strategy.underscore"]]
- [ setTemplate, [list, YourBundle:YourAdmin:list.html.twig]] # <- tell sonata you want this template for list
2. Add new twig template
Now add the following template to your bundles Resources/views/YourAdmin/list.html.twig:
{# extends from the normal base_list.html.twig, but when this template is
rendered via an ajax-call, extend from an ajax-list.html.twig, we'll
add later #}
{% extends app.request.xmlHttpRequest
? 'YourBundle:ajax-list.html.twig'
: 'SonataAdminBundle:CRUD:base_list.html.twig' %}
{# YourBundle:ajax-list.html.twig is the path where you have your template, if your template is inside views/Admin, then put it like this: YourBundle:Admin:ajax-list.html.twig #}
{# Here I copied the list_table block from the original list.html.twig
from sonata.. I added custom JS-code below to do the ajax call #}
{% block list_table %}
{# The code in this block is copied from sonata.. I just added the JS-code below, and added the 'actionList' id here #}
<div id="actionList" class="col-xs-12 col-md-12">
{% if admin.hasRoute('batch') %}
<form action="{{ admin.generateUrl('batch', {'filter': admin.filterParameters}) }}" method="POST" >
<input type="hidden" name="_sonata_csrf_token" value="{{ csrf_token }}">
{% endif %}
{# Add a margin if no pager to prevent dropdown cropping on window #}
<div class="box box-primary" {% if admin.datagrid.pager.lastPage == 1 %}style="margin-bottom: 100px;"{% endif %}>
<div class="box-body {% if admin.datagrid.results|length > 0 %}table-responsive no-padding{% endif %}">
{{ sonata_block_render_event('sonata.admin.list.table.top', { 'admin': admin }) }}
{% block list_header %}{% endblock %}
{% set batchactions = admin.batchactions %}
{% if admin.datagrid.results|length > 0 %}
<table class="table table-bordered table-striped sonata-ba-list">
{% block table_header %}
<thead>
<tr class="sonata-ba-list-field-header">
{% for field_description in admin.list.elements %}
{% if admin.hasRoute('batch') and field_description.getOption('code') == '_batch' and batchactions|length > 0 %}
<th class="sonata-ba-list-field-header sonata-ba-list-field-header-batch">
<input type="checkbox" id="list_batch_checkbox">
</th>
{% elseif field_description.getOption('code') == '_select' %}
<th class="sonata-ba-list-field-header sonata-ba-list-field-header-select"></th>
{% elseif field_description.name == '_action' and app.request.isXmlHttpRequest %}
{# Action buttons disabled in ajax view! #}
{% elseif field_description.getOption('ajax_hidden') == true and app.request.isXmlHttpRequest %}
{# Disable fields with 'ajax_hidden' option set to true #}
{% else %}
{% set sortable = false %}
{% if field_description.options.sortable is defined and field_description.options.sortable %}
{% set sortable = true %}
{% set sort_parameters = admin.modelmanager.sortparameters(field_description, admin.datagrid) %}
{% set current = admin.datagrid.values._sort_by == field_description or admin.datagrid.values._sort_by.fieldName == sort_parameters.filter._sort_by %}
{% set sort_active_class = current ? 'sonata-ba-list-field-order-active' : '' %}
{% set sort_by = current ? admin.datagrid.values._sort_order : field_description.options._sort_order %}
{% endif %}
{% spaceless %}
<th class="sonata-ba-list-field-header-{{ field_description.type}} {% if sortable %} sonata-ba-list-field-header-order-{{ sort_by|lower }} {{ sort_active_class }}{% endif %}">
{% if sortable %}<a href="{{ admin.generateUrl('list', sort_parameters) }}">{% endif %}
{{ admin.trans(field_description.label, {}, field_description.translationDomain) }}
{% if sortable %}</a>{% endif %}
</th>
{% endspaceless %}
{% endif %}
{% endfor %}
</tr>
</thead>
{% endblock %}
{% block table_body %}
<tbody>
{% include admin.getTemplate('outer_list_rows_' ~ admin.getListMode()) %}
</tbody>
{% endblock %}
{% block table_footer %}
{% endblock %}
</table>
{% else %}
<div class="info-box">
<span class="info-box-icon bg-aqua"><i class="fa fa-arrow-circle-right"></i></span>
<div class="info-box-content">
<span class="info-box-text">{{ 'no_result'|trans({}, 'SonataAdminBundle') }}</span>
<div class="progress">
<div class="progress-bar" style="width: 0%"></div>
</div>
<span class="progress-description">
{% if not app.request.xmlHttpRequest %}
{% include 'SonataAdminBundle:Button:create_button.html.twig' %}
{% endif %}
</span>
</div><!-- /.info-box-content -->
</div>
{% endif %}
{{ sonata_block_render_event('sonata.admin.list.table.bottom', { 'admin': admin }) }}
</div>
{% block list_footer %}
{% if admin.datagrid.results|length > 0 %}
<div class="box-footer">
<div class="form-inline clearfix">
{% if not app.request.isXmlHttpRequest %}
<div class="pull-left">
{% if admin.hasRoute('batch') and batchactions|length > 0 %}
{% block batch %}
<script>
{% block batch_javascript %}
jQuery(document).ready(function ($) {
$('#list_batch_checkbox').on('ifChanged', function () {
$(this)
.closest('table')
.find('td.sonata-ba-list-field-batch input[type="checkbox"], div.sonata-ba-list-field-batch input[type="checkbox"]')
.iCheck($(this).is(':checked') ? 'check' : 'uncheck')
;
});
$('td.sonata-ba-list-field-batch input[type="checkbox"], div.sonata-ba-list-field-batch input[type="checkbox"]')
.on('ifChanged', function () {
$(this)
.closest('tr, div.sonata-ba-list-field-batch')
.toggleClass('sonata-ba-list-row-selected', $(this).is(':checked'))
;
})
.trigger('ifChanged')
;
});
{% endblock %}
</script>
{% block batch_actions %}
<label class="checkbox" for="{{ admin.uniqid }}_all_elements">
<input type="checkbox" name="all_elements" id="{{ admin.uniqid }}_all_elements">
{{ 'all_elements'|trans({}, 'SonataAdminBundle') }}
({{ admin.datagrid.pager.nbresults }})
</label>
<select name="action" style="width: auto; height: auto" class="form-control">
{% for action, options in batchactions %}
<option value="{{ action }}">{{ options.label }}</option>
{% endfor %}
</select>
{% endblock %}
<input type="submit" class="btn btn-small btn-primary" value="{{ 'btn_batch'|trans({}, 'SonataAdminBundle') }}">
{% endblock %}
{% endif %}
</div>
<div class="pull-right">
{% if admin.hasRoute('export') and admin.isGranted("EXPORT") and admin.getExportFormats()|length %}
<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<i class="glyphicon glyphicon-export"></i>
{{ "label_export_download"|trans({}, "SonataAdminBundle") }}
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
{% for format in admin.getExportFormats() %}
<li>
<a href="{{ admin.generateUrl('export', admin.modelmanager.paginationparameters(admin.datagrid, 0) + {'format' : format}) }}">
<i class="glyphicon glyphicon-download"></i>
{{ format|upper }}
</a>
<li>
{% endfor %}
</ul>
</div>
-
{% endif %}
{% block pager_results %}
{% include admin.getTemplate('pager_results') %}
{% endblock %}
</div>
{% endif %}
</div>
{% block pager_links %}
{% if admin.datagrid.pager.haveToPaginate() %}
<hr/>
{% include admin.getTemplate('pager_links') %}
{% endif %}
{% endblock %}
</div>
{% endif %}
{% endblock %}
</div>
{% if admin.hasRoute('batch') %}
</form>
{% endif %}
</div>
{# When this request is not via ajax, add the following JS-code
this will trigger an ajax-request, and uses the normal a-href location #}
{% if not app.request.xmlHttpRequest %}
<script>
$('body').on('click', '.pagination li a', function(e){
var url = $(this).attr('href');
$.post(url,
{},
function(response){
if(response.code == 100 && response.success){
$('#actionList').replaceWith(response.content);
}
}
);
e.preventDefault();
return false;
});
</script>
{% endif %}
{% endblock %}
3. Add ajax template
We do this to prevent the original bundle to output everything we don't need:
YourBundle/Resources/views/YourAdmin/ajax-list.html.twig:
{% block list_table %}{% endblock %}
4. Add custom CrudController
Now the only thing left to do is, that we want to overwrite the original listAction so that we can return JSON in stead of pure HTML. Add YourCrudController.php in YourBundle/Controller/
Add the following
<?php
namespace Your\Bundle\Controller;
use Sonata\AdminBundle\Controller\CRUDController as Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
class YourCrudController extends Controller
{
public function listAction(Request $request = null)
{
// call original listAction function to get the right HTML
$response = parent::listAction($request);
if($request->isXmlHttpRequest()) {
// disable the profile, to prevent profiler code
$this->container->get('profiler')->disable();
// return JsonResponse
return new JsonResponse(array('success' => true, 'code' => 100, 'content' => $response->getContent()));
}
return $response;
}
}
Sit back, hit f5, click the pager and enjoy!
I would like to render a tree with an undetermined depth (children of children of children, etc.). I need to loop through the array recursively; how can I do this in Twig?
I played around with domi27's idea and came up with this. I made a nested array as my tree, ['link']['sublinks'] is null or another array of more of the same.
Templates
The sub-template file to recurse with:
<!--includes/menu-links.html-->
{% for link in links %}
<li>
{{ link.name }}
{% if link.sublinks %}
<ul>
{% include "includes/menu-links.html" with {'links': link.sublinks} %}
</ul>
{% endif %}
</li>
{% endfor %}
Then in the main template, call this (kind of redundant 'with' stuff there):
<ul class="main-menu">
{% include "includes/menu-links.html" with {'links':links} only %}
</ul>
Macros
A similar effect can be achieved with macros:
<!--macros/menu-macros.html-->
{% macro menu_links(links) %}
{% for link in links %}
<li>
{{ link.name }}
{% if link.sublinks %}
<ul>
{{ _self.menu_links(link.sublinks) }}
</ul>
{% endif %}
</li>
{% endfor %}
{% endmacro %}
In the main template, do this:
{% import "macros/menu-macros.html" as macros %}
<ul class="main-menu">
{{ macros.menu_links(links) }}
</ul>
Twig 2.0 - 2.11
If you want to use a macro in the same template, you should use something like this to stay compatible with Twig 2.x:
{% macro menu_links(links) %}
{% import _self as macros %}
{% for link in links %}
<li>
{{ link.name }}
{% if link.sublinks %}
<ul>
{{ macros.menu_links(link.sublinks) }}
</ul>
{% endif %}
</li>
{% endfor %}
{% endmacro %}
{% import _self as macros %}
<ul class="main-menu">
{{ macros.menu_links(links) }}
</ul>
This extends random-coder's answer and incorporates dr.scre's hint to the Twig documentation about macros to now use _self, but import locally.
Twig >= 2.11
As of Twig 2.11, you can omit the {% import _self as macros %}, as inlined macros are imported automatically under the _self namespace (see Twig announcement: Automatic macro import):
{# {% import _self as macros %} - Can be removed #}
<ul class="main-menu">
{{ _self.menu_links(links) }} {# Use _self for inlined macros #}
</ul>
If you're running PHP 5.4 or higher, there is a wonderful new solution (as of May 2016) to this problem by Alain Tiemblo: https://github.com/ninsuo/jordan-tree.
It's a "tree" tag that serves this exact purpose. Markup would look like this:
{% tree link in links %}
{% if treeloop.first %}<ul>{% endif %}
<li>
{{ link.name }}
{% subtree link.sublinks %}
</li>
{% if treeloop.last %}</ul>{% endif %}
{% endtree %}
First I thought this may be solved in a straightforward way, but it isn't that easy.
You need to create logic, maybe with a PHP class method, when to include a Twig subtemplate and when not.
<!-- tpl.html.twig -->
<ul>
{% for key, item in menu %}
{# Pseudo Twig code #}
{% if item|hassubitem %}
{% include "subitem.html.tpl" %}
{% else %}
<li>{{ item }}</li>
{% endif %}
{% endfor %}
</ul>
So you could use the special Twig loop variable, which is available inside a Twig for loop. But I'm not sure about the scope of this loop variable.
This and other information are available on Twigs "for" Docu!
Took flu's answer and modified it a little:
{# Macro #}
{% macro tree(items) %}
{% import _self as m %}
{% if items %}
<ul>
{% for i in items %}
<li>
{{ i.title }}
{{ m.tree(i.items) }}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
{# Usage #}
{% import 'macros.twig' as m %}
{{ m.tree(items) }}
The answers here lead my to my solution.
I have a category entity with a self-referencing many-to-one association (parent to children).
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
*/
private $children;
In my Twig template I am rendering the tree view like this:
<ul>
{% for category in categories %}
{% if category.parent == null %}
<li>
{{ category.name }}
{% if category.children|length > 0 %}
<ul>
{% for category in category.children %}
<li>
{{ category.name }}
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>