I want to be able to import macros from one central file, but have each macro in a separate file.
My current approach is having a macros.twig, which imports all the other macros.
{% from 'foo-macro.twig' import foo %}
{% from 'bar-macro.twig' import bar %}
{% from 'baz-macro.twig' import baz %}
But when importing it in example.twig:
{% from 'macros.twig' import foo, bar, baz %}
{{ foo('foo arg') }}
{{ bar('bar arg') }}
{{ baz('baz arg') }}
Error: Macro "foo" is not defined in template "macros.twig".
I also tried including each macro in macros.twig via include. Same issue.
I there a way I could not find in the docs?
Here's a TwigFiddle
You need to import the macro in each file you want to use it.
This has been changed in Twig 2.0 and has been documented
Macros
As of Twig 2.0, macros imported in a file are not available in child templates anymore (via an include call for instance). You need to import macros explicitly in each file where you are using them.
source
Related
I'm having a bit of trouble with Symfony twig translations, and I hope someone here can clarify.
Say I have a file /templates/general/hello_world.twig , with the contents:
{% trans with {
'%name%': name,
} %}
"Hello %name%"
{% endtrans %}
When I call bin/console translation:update --output-format=yaml fr --force , the output file translations/messages+intl-icu.fr.yaml now contains the contents:
'"Hello %name%"': '__"Hello %name%"'
This is only the case with Twig files - I have other files in translations where they are locale-suffixed, and those are properly translated into french. I'm not clear what has to happen in order to get the translation:update command to recognize that the original content is in English?
I'm not asking how to choose a locale based on the user request. I'm specifically wanting to generate missing translations for contents in my twig templates, and I'd love to have this command available to get me started. The command works a treat for translations actually in the /translations/ folder.
Update 2
// hello.twig
{% trans with {
'%name%': name,
} %}hello_world{% endtrans %}
//messages.en.yaml
hello_world: "Hello %name%"
I created a complex state for API service, it involves git checkouts, python venv, uwsgi, nginx, etc etc. It works fine.
Now I would like to turn it into a template and execute it several times per minion, with variables supplied from pillar - i.e something like.
{% for apiserver in pillar.apiservers %}
include apiserver_template.sls, locals: apiserver.config
{% endfor %}
where apiserver_template will work with context supplied to it, with apiserver.config having all config data for each API instance. I know syntax is wrong but hopefully I am communicating the idea - ideally, something like executing ruby partials with supplying local variables.
How is it done properly in saltland?
It sounds to me like Jinja Macro is something you want to use for this. You can find more information about usage here: https://docs.saltstack.com/en/2015.8/topics/development/conventions/formulas.html#jinja-macros
In short what you will have in your case may look like:
{% macro api_server(git_repo, python_venv_path, python_venv_requirements) %}
{{python_venv_path}}:
virtualenv.managed:
- system_site_packages: False
- requirements: salt://{{python_venv_requirements}}
{{git_repo}}:
git.latest:
- name: {{git_repo}}
{% endmacro %}
Assuming you have a pillar apiservers where each api server has git_repo, python_venv_path and python_venv_requirements values, you can use the macro like this:
{% for server in salt.pillar.get('apiservers', []) %}
{{ api_server(server['git_repo'], server['python_venv_path'], server['python_venv_requirements']) }}
{% endfor %}
If you want - you can also put a macro in a separate state file and then import a marco as a regular salt resource.
Please also not that instead of pillar.apiservers I used salt.pillar.get('apiservers', []). This is a safer way to get data from pillar. If for some reason a pillar is unavailable - the later code will result in empty dict instead of failure in first case.
Using a twig macro on safe content seems to remove the safe flags
{% macro identity(value) %}
{{ value }}
{% endmacro %}
{{ "<br>"| raw }}
{{ _self.identity( "<br>"| raw) }}
{% autoescape false %}
{{ _self.identity( "<br>"| raw) }}
{% endautoescape %}
The code above show only 2 <br>. Is there a way to disable escaping, or marking my content as safe without editing my twig macro ?
You are indeed correct that you must put the raw filter in the macro in order to keep whatever tags inside the macro from escaping their input and this would, at least in my opinion, be the expected behavior.
In Twig, macros are not function or filters, they are simply shortcuts that keep you from repeating boilerplate tags and code. It helps, when you create and use a macro, to think of writing the whole code each time you will use it i.e.
Instead of thinking of your macro as a call {{ _self.identity( "<br>"| raw ) }} actually think that what you are writing is {{ {{ "<br>"| raw }} }} because you are passing the output of the macro's input as a literal, not the expression you have written.
If you would like your macros to operate using the provided input as a template, rather than, as designed, operating on the output of whatever you pass in, you may look at enabling the template_from_string function.
I'm relatively new to Symfony. I have a bundle with twig templates that are automatically loaded with annotations in a directory structure like:
src/bundle/Resources/views/Default/
One of my templates has a big chunk of code repeated a bunch of times (with a few minor changes each instance) that I think doing an include a few times formatted like this:
{% include 'form_include.html' with {'foo': 'bar'} %}
with different variables for each instance should work well. But the debugger is telling me that it's looking for the include file in
/app/Resources/
But the template is really specific to this bundle and I wouldn't want it kept elsewhere. I tried using the ../../src.... method to specify its location with no luck. Is there a way to do this?
You can provide a path using the bundle's name:
{% include 'YourBundleNameBundle:Default:form_include.html.twig' with {
'foo': bar
} %}
Where each part is separated by : and:
YourBundleNameBundle corresponds to src/YourBundleNameBundle/Resources/views/
Default corresponds to the /Default directory in this folder
form_include.html.twig corresponds to form_include.html.twig in this folder
So, the 'YourBundleNameBundle:Default:form_include.html.twig' value will load the src/bundle/Resources/views/Default/form_include.html.twig file.
This syntax works for the different Twig functions: include, extends, etc.
It is useful for allowing templates inheritance.
Have you considered a macro instead?
From: http://twig.sensiolabs.org/doc/tags/macro.html
Macros are comparable with functions in regular programming languages. They are useful to put often used HTML idioms into reusable elements to not repeat yourself.
Here is a small example of a macro that renders a form element:
{% macro input(name, value, type, size) %}
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
{% endmacro %}
Macros differs from native PHP functions in a few ways:
Default argument values are defined by using the default filter in the macro body;
Arguments of a macro are always optional.
If extra positional arguments are passed to a macro, they end up in the special varargs variable as a list of values.
But as with PHP functions, macros don't have access to the current template variables.
You can pass the whole context as an argument by using the special _context variable.
Macros can be defined in any template, and need to be "imported" before being used (see the documentation for the import tag for more information):
{% import "forms.html" as forms %}
The above import call imports the "forms.html" file (which can contain only macros, or a template and some macros), and import the functions as items of the forms variable.
The macro can then be called at will:
<p>{{ forms.input('username') }}</p>
<p>{{ forms.input('password', null, 'password') }}</p>
If macros are defined and used in the same template, you can use the special _self variable to import them:
{% import _self as forms %}
<p>{{ forms.input('username') }}</p>
When you define a macro in the template where you are going to use it, you might be tempted to call the macro directly via _self.input() instead of importing it; even if seems to work, this is just a side-effect of the current implementation and it won't work anymore in Twig 2.x.
When you want to use a macro in another macro from the same file, you need to import it locally:
{% macro input(name, value, type, size) %}
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
{% endmacro %}
{% macro wrapped_input(name, value, type, size) %}
{% import _self as forms %}
<div class="field">
{{ forms.input(name, value, type, size) }}
</div>
{% endmacro %}
I use the Symfony2.1 and have the default config.yml
Documentation said:
{# but static strings are never escaped #}
{{ '<h3>foo</h3>'|trans }}
But if I copy and paste it into the my empty template (without any additional autoescapes or another) I got the escaped string <h3>foo</h3>. What I do wrong?
Try it with the twig raw filter:
{{ '<h3>foo</h3>' | trans | raw }}
However, do not use the raw filter if you are processing any user input! It allows for cross-site-scripting attacks, according to the creators of Symfony. See this similar question for a secure but more tedious alternative.
Holding HTML stuff in translations is wrong, because translators usually break it. But if you really need it:
{% trans %}<h3>foo</h3>{% endtrans %}
https://github.com/symfony/symfony/issues/2713#issuecomment-12510417