What is the scrope of varibles in block in jinja? - scope

First, I am not asking about something about loop (for)!
In Jinja, I feel confused about the valid scope of variables defined inside and outside a block. For example, If I define {% set a = ... %} inside a block, can I use it in another block? If I defined them in super-class, in the child class, in what kind of range can I use them? I found sometimes it is passed from one block to another (or from here to there), and sometimes not. I do not find any document to explain that.
(What I mean "pass" is to use {{a}} directly and it returns the valid value.)

Variables in block, for, macro and filter are all local. They are valid only inside these block.
Detail can be found here. An example given there is:
.. sourcecode:: jinja
{% macro angryhello name %}
{% set angryname = name|upper %}
Hello {{ name }}. Hello {{ name }}!
HELLO {{ angryname }}!!!!!!111
{% endmacro %}
The variable angryname just exists inside the macro, not outside it.

Related

Can we access twig inner property on construction?

Let's say I'm constructing a Twig object and I want to access an inner property defined previously:
{% set my_object =
default_width: 12,
default_height: default_width * 2
%}
This throws a Variable "default_width" does not exist error, which make sense since the object was not entirely declared yet but is there a way to make this work without two instructions?
This behavior isn't possible, the best way to do it is to declare a variable before the construction and reusing it here.

Call a service from twig template in symfony 4

I would like to know how I can call a service from twig template directly on Symfony 4. I am using it in each action like this:
public function indexAction(TranslatorInterface $translator, NavigationGenerator $navigationGenerator)
{
return $this->render('index/index.html.twig', [
'navigationItems'=>$navigationGenerator->getNavigation(self::class)
]);
}
In the template I call this:
{% for navigationItem in navigationItems.topNavigation['left'] %}
{{ navigationItem.label }}
{% endfor %}
In earlier bootstrap versions I could define the service as a global object in config.yml and use it directly from twig like this:
{ NavigationGenerator.getNavigation(ControllerName) }
Any hint how to do this in Symfony 4? There is no config.yml anymore.
You can still define twig global variables - they would just go in with the rest of the Twig configuration in config/packages/twig.yaml.
An alternative, and maybe better place to put that could be a Twig function. The service that defines the function (or filters) are as much a service as the controllers, and so you would type-hint your NavigationGenerator and anything else you needed, in the constructor, for use in the function that is being called from Twig.

Array of multiple path is not working in Nunjucks for templates and partials

I'm using this gulp plugin to use nunjucks to make HTML management easier.
https://github.com/carlosl/gulp-nunjucks-render
gulp.task('default', function () {
return gulp.src('src/templates/*.html')
.pipe(nunjucksRender({
path: ['src/templates/'] // String or Array
}))
.pipe(gulp.dest('dist'));
});
I want to keep my templates and partials in different folders so I tried this to keep as path
path: ['src/templates', 'src/partials']
but it's not working.
Template render error: (unknown path)
Error: template not found: partials/side_nav.nunjucks
My setup
As far as I can see, the issue is in your include. You're already defining your base paths as 'src/templates' and 'src/partials'. Now nunchucks is trying to import src/templates/partials/side_nav.nunjucks and afterwards src/partials/partials/side_nav.nunjucks, which don't exist.
Solution 1
So you would have to include it without the partials part:
{% include "side_nav.nunjucks" %}
Solution 2
If you want to be explicit about your folders (both templates and partials), you could just set you base path to src instead, and include your files like you did:
{% extends "templates/layout_with_sidenav.nunjucks" %}
...
{% include "partials/side_nav.nunjucks" %}

Escaping the double curly braces {{ }} in Swig templates for AngularJS

Swig templates and AngularJS both use the double curly brace notation. How can the double curlies be escaped in Swig for Angular?
Double curlies can be escaped with
{% raw %}
eg: {% raw %}{{ foobar }}{% endraw %}
Forces the content to not be auto-escaped. All swig instructions will be ignored and the content will be rendered exactly as it was given.
See Swig manual/tags/raw.
Why not replacing the {{}} with [[]] in the templates by configuring AngularJS to accept [[]] as the new {{}}. Try this in your Angular-App-Config (tried with angularjs-1.2.4):
config(['$interpolateProvider',
function($interpolateProvider) {
// Swig uses {{}} for variables which makes it clash with the use of {{}} in AngularJS.
// Replaced use of {{}} with [[]] in AngularJS to make it work with Swig.
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
}
])
Instead of replacing interpolation sign of angular. Change defaults for swig. Following Code will do it.
var swig = require('swig');
swig.setDefaults({
varControls: ['[[', ']]']
});

global variables not parsing as parameters

I am having troubles with global variables not parsing when passed as parameters.
{exp:channel:entries
disable="categories|category_fields|member_data|pagination|trackbacks"
dynamic="no"
entry_id="{structure:child_ids_for:21}"
}
(0.012500 / 3.36MB) Tag: {exp:channel:entries disable="categories|category_fields|member_data|pagination|trackbacks" dynamic="no" entry_id="{structure:child_ids_for:21}" }
The same result is produced with and without parse="inward"
However this works fine and grabs the data I need
{exp:channel:entries
disable="categories|category_fields|member_data|pagination|trackbacks"
dynamic="no"
entry_id="{exp:query sql='SELECT exp_structure.entry_id,
exp_structure.parent_id,
exp_structure.lft
FROM exp_structure
WHERE parent_id = 21
ORDER BY exp_structure.lft ASC'}{entry_id}|{/exp:query}"
parse="inward"
}
But, then if I add in a global variable author_id="{logged_in_member_id}" it fails to work, if I hard code that value as 1 then it functions.
Any thoughts as to what could be happening here?
You can avoid the overhead of embeds by using Stash for this sort of thing. It has the insanely useful ability to let you explicitly state your preferred parse order for different chunks of code. In this case the first thing you'd do is store the two variables via {exp:stash:set}, then you can retrieve them in the second chunk of code via {exp:stash:get}. The magic bit is the parse priority tag; because the first item has a priority of 10 it will be executed first, which ensures the vars are available for use as channel entries parameters in the second {exp:stash:parse} tag.
{exp:stash:parse priority="10" process="end"}
{exp:stash:set}
{stash:structure_ids}{structure:sibling_ids}{/stash:structure_ids}
{stash:logged_in_member}{logged_in_member_id}{/stash:logged_in_member}
{/exp:stash:set}
{/exp:stash:parse}
{exp:stash:parse priority="20" process="end"}
{exp:channel:entries
disable="categories|category_fields|member_data|pagination|trackbacks"
dynamic="no"
entry_id="{exp:stash:get name='structure_ids'}"
author_id="{exp:stash:get name='logged_in_member'}"
parse="inward"
}
...
{/exp:channel:entries}
{/exp:stash:parse}
I can't speak for Structure's global variables, but {logged_in_member_id} is a late-parsed global variable, meaning you can't use it in a module tag parameter. I can only only assume that the same goes for the Structure variables.
You can use the CURRENT_USER constant in the author_id parameter though (docs).
Unfortunately, the solution for your {structure:child_ids_for:21} issue is to pass that as an embed variable, and put your Channel Entries loop in an embed. (I say unfortunately because embeds do incur some overhead.)
One note: parse="inward" has no effect on module tag pairs - they always parse inward. It only affects plugin tag pairs.

Resources