Jinja templates, how to do an import of all macros to follow the DRY principle? - macros

currently I have in many places:
{% from "macros/render_product_materials.html" import render_product_materials %}
{% from "macros/render_citation.html" import render_citation %}
{% from "macros/render_product_packages.html" import render_product_packages %}
{% from "macros/render_icon_explanation_section.html" import render_icon_explanation_section %}
{% from "macros/render_percentage_items.html" import render_percentage_items %}
this list goes on!
then i call the code in the template:
{{render_percentage_items('some args',2,34,55)}}
If I change a function name, I have to change it everywhere, if I add a new function, I have to go and import it each time everywhere
I rather just want to do this, something like this:
{% include 'macros/all_macros.html' %}
then I just put all imports into all_macros.html
But: the imports do not get available in the context
e.g.
{% include 'macros/all_macros.html' %}
{{render_percentage_items('some args',2,34,55)}}
doesn't work. render_percentage_items not found. Now what is the way to do this the jinja way?

Collecting related macros into a single file is a start. Then use import … as rather than include.
{% import 'macros/all_macros.html' as m %}
{{ m.render_percentage_items('some args', 2, 34, 55) }}
So, basically, exactly what SumanKalyan already said.
In many (most?) cases, you won't even need the with context, but you can refer here to decide if maybe you do. As mentioned in the documentaiton, with context disables caching, and usually imported templates only contain macros anyway, which can and should be cached.
Reference.

Related

DBT Macros to perform computations

i was trying to write a macros which would be capable of doing various computations and return the final result.
Does macros accept only Block level codes like if or for loops? and not set of individual statements?
please help me with this
Thanks
There's a lot you can do with macros. Most native python operations are possible, albeit in a clunky non-pythonic way (no list comprehensions here).
For example, one can call any method of a python builtin data type:
{% set my_base_models = [] %}
{% for i in graph.nodes.values() %}
{% if i["name"].startswith('base_model') %}
{% do my_list.append(i) %}
{% endif %}
{% endfor %}
Key points:
call {% do <obj>.<method>() %} with any state-altering method (eg. pop() , append(), etc) to execute it
Put results into variables with {% set <var> = <expression> %}
Check out out filters and tests in standard jinja to filter and test mappings (dicts) and lists
Check out the list of dbt specific jinja context variables and methods

symfony2 twig form theme for different subsites

I've got a symfony2 application with multiple styles. All 3 have their own html + css for rendering.
Something like "frontsite", "user dashboard" and "admin backend".
Now there is a difference in form theme to be used in all 3 subsites, so there is a (slightly) different theme for all of them.
Because of this I can set the theme in the symfony configuration, but for 2 of the 3 subsites it will be wrong.
I can set a theme manually using:
{% form_theme form 'MyUberCoolBundle:Form:theme.html.twig' %}
But I don't really want to do that for every form.
I could set that in a base-template, but then my form would always need to be called "form".
Is there a way to set the theme to be used in a base template so it will be used for all forms?
I was looking for something like that. Something to use a theme for FrontOffice and another one to use in the back office.
I got it. I use a normal configuration in the config.yml as normal for the FrontOffice like that:
twig:
globals:
frontend_form_theme: 'form/fields.html.twig'
And then I use in the main BackOffice themplate like layout.html.twig:
{% if oForm is defined %} {% form_theme oForm 'bootstrap_3_layout.html.twig' %} {% endif %}
So, bootstrap defined for all the forms with the name oForm.
So the other answers here work great when your pages only have one form and it's always named something constant, like form. In my scenario, however, I often have multiple forms with different names, so that approach doesn't work. Instead, what I did was create a custom twig function that applies the desired form theme to all forms passed into to the view:
Inside your WhateverTwigExtension.php: (see here for info on writing twig extensions)
class WhateverTwigExtension extends AbstractExtension
{
private $formRenderer;
public function __construct(FormRendererInterface $formRenderer)
{
$this->formRenderer = $formRenderer;
}
public function getFunctions()
{
return [
new TwigFunction('applyFormThemes', [$this, 'applyFormThemes']),
];
}
public function applyFormThemes(array $context, $themes)
{
foreach ($context as $variable) {
if ($variable instanceof FormView) {
$this->formRenderer->setTheme($variable, $themes);
}
}
}
}
Note that for symfony's autowiring to be able to inject the renderer into this extension, you'll have to alias it, so put something like this in your services.yml:
services:
Symfony\Component\Form\FormRendererInterface: '#twig.form.renderer'
Finally, in the base twig for your subsite, simply add:
{{ applyFormThemes(_context, 'my_form_theme.html.twig') }}
Also works for multiple themes:
{{ applyFormThemes(_context, ['my_form_theme.html.twig', 'other_form_theme.html.twig']) }}
And now all forms passed to that view will have your theme applied!
You can also still use the normal form_theme tag to override specific forms, as long as it comes after the applyFormThemes
to set a theme global for your application you can set it in the config
twig:
form_themes:
# Default:
- form_div_layout.html.twig
# Bootstrap:
- bootstrap_3_layout.html.twig
- bootstrap_3_horizontal_layout.html.twig
# Example:
- MyBundle::form.html.twig
See the full Twig config reference here
After looking around for an answer I came to the conclusion it is not possible to do this only in configuration.
We resorted to the following solution
in config.yml we configured a global twig variable:
twig:
globals:
frontend_form_theme: 'MyUberCoolBundle:Form:theme.html.twig'
Then we set the correct theme on the form itself just prior to rendering using the global variable.
This even allows us to use something different when desired.
{% form_theme form frontend_form_theme %}
{{ form_start(form) }}
This is possible, and works well. It assumes you are consistent with naming your root forms however.
First, setup a default form theme like described in the standard docs.
# twig.yaml
twig:
form_themes: ['form.html.twig'] # Default theme and front-end
Override the form theme for the primary form in your admin layout template.
# admin/layout.html.twig
{% if form is defined %}
{% form_theme form "admin/_form.html.twig" %}
{% endif %}
{% block content %}{% endblock %}
...
Optionally override the admin form theme in your final templates, by setting the form theme in the form block. It won't work if you define form theme outside the block. Your custom form theme should probably extend the admin form theme too.
# admin/user/edit.html.twig
{% extends "admin/layout.html.twig" %}
{% block content %}
{% form_theme form "admin/user/_form.html.twig" %}
{{ form(form) }}
{% endblock %}

Can I use a filter on all compress blocks except for one?

In my settings for compressor I'm using SlimIt for most of my javascript:
COMPRESS_JS_FILTERS = ['compressor.filters.jsmin.SlimItFilter', ]
Some of my js files shouldn't go through SlimIt though because the file is already minified, or the javascript throws some error when its minified with other files. My template block ends up looking like this:
{# code that I minify #}
{% block compressed_libs %}
{% compress js %}
<script src="/static/js/compress_this.js"></script>
<script src="/static/js/also_compress_this.js"></script>
...
{% endcompress %}
{% endblock %}
{# code that shouldn't minify #}
{% block non-compressible_libs %}
<script src="/static/js/already.min.js"></script>
<script src="/static/js/breaks-everything.js"></script>
{% endblock %}
Can I set different compress filter rules for different blocks/files so that my "non-compressible" files can still be concatenated together by compressor while skipping SlimIt?
As approxiblue said, it doesn't look there's a way to specify which filters to use per compress block in a template (in Compressor 1.5).
I'll update this answer someone comes up with something.
It seems like this could be solved by adding a parameter to the compress template tag to allow it to return a CompressorNode with a flag to skip the filter in base.py hunks()
I'll see what the Compressor community thinks about this, but let me know if you have any ideas.

Customize form_row for different form types

I want to set different form_row layout for different form type. I found in templates block named "choice_widget_collapsed", but it render only select tag with options.
I cannot find where this block is being used. Actually it seems that it is rendered instead of form_widget block. I suppose there is somewhere switch/if structure which checks form type and renders appropriate block, but i dont know where to find this switch, or dont know how to check input type inside form_row block.
I know that block type can be found inside form.vars.block_prefixes array, but this sux, because its position may change in the future as it was already.
So the question is: how can i make form_row display different thing depending on form field type?
You should override the normal block in a theme of yours. This block should work like this:
{% block form_appropriate_block %}
{% spaceless %}
{% if form.vars.widget = 'myIntendedWidgetType' %}
[yourTemplate]
{% else %}
{{ parent() }}
{% endif %}
{% endspaceless %}
{% endblock form_appropriate_block %}
And then, in your template, activate your theme using:
{% form_theme form 'MyBundle:Form:formTheme.html.twig' %}
In this way, your form theme is used only when needed, and if the type is not the one you want, it falls back to the normal behaviour.

symfony2 twig form registration included from external template

Be patient is my first question and my english is also poor ;P
Btw... im using fos for my website and all work fine, actually my problem is that i have the "pages" template made with twig and it have, at the bottom, a call to action button that slideDown an hidden div where i want to put my registration form.
I setup the hidden div and try to put inside my include:
{% block fos_user_content %}
{% include "FOSUserBundle:Registration:register_content.html.twig" %}
{% endblock fos_user_content %}
obviously it dosn't work:
Variable "form" does not exist in kernel.root_dir/Resources/JuliusUserBundle/views/Registration/register_content.html.twig at line 2
probably for some reasons related to routing or firewall or security?
anyone have a solutions, suggestions or ideas for that?
thanks and cheers!
As error said, you need to define 'form' variable in your action, or you could try to render FOSUser registration action instead of this.
For example:
{% render(controller(FOSUserBundle:Registration:register")) %}
If you need to use include then you have to pass the form variable to your included template. Optionally you can render that template also from its corresponding controller.
So for the first case you have:
{% include("FOSUserBundle:Registration:register_content.html.twig") with {'form':form} %}
Where form here is the variable you pass from your own controller. Your second choice would be to render the FOSUB template like so:
{% render(controller("FOSUserBundle:Registration:register")) %}
Apparently i found a good solution using Edge Side Includes (ESI) includes that give me also additional benefits in cache control:
<esi:include src="http://localhost:8004/login" />