Calling method in Twig, how to avoid the return showing as output - twig

I am calling a method in a pre-existing object from inside a Twig template, after properly authorizing it in policy:
{{ thisEmail.AddCC('me#example.com') }}
My problem is that the object method, which I use to produce a side-effect, not to return any text, returns true, and that leaves a visible "1" in my template output.
How to remove this output? I have this one working:
{{ thisEmail.AddCC('me#example.com')|trim('1') }}
But I feel it's not entirely elegant, and it can only remove that specific result. I would prefer to write something like |nul or |drop and have the filter drop everything, regardless of how much or what kind of stuff it receives as input.
I know how to do it by writing my own Twig extension filters, but my question here is: is there a nice way of achieving this with the core Twig language, un-extended?

If you want a twig statement not to output anything you can use the do tag, e.g.
{% do thisEmail.AddCC('me#example.com') %}
source

Related

How to run an object's method in twig without rendering what it returns [duplicate]

I have seen the twig documentation about do tag, but I don't understand its use/useful.
The docs says the follow:
The do tag works exactly like the regular variable expression ({{ ...
}}) just that it doesn't print anything:
and show an example:
{% do 1 + 2 %}
What to solve exactly this tag ?
Good question! I found a link on GitHub to when this was proposed which might add some further info:
Sometimes you want to do things, or call some stuff, and ignore the output. For example if you use a |shift filter to remove some data from an array, doing {{ arr|shift }} will output the removed item, which is not always desirable.
Of course it's possible to do {% set null = arr|shift %}, which won't
output anything, but it also looks weird.
The example in the docs is poor as it explains nothing at all, as you pointed out.

Flask | JINJA 2: render_template_string() with macro imported in context

I am looking to make macros available inside of the articles for my flask blog. When the body of my blog changes I run the following code to render the body into HTML:
target.body_html = bleach.linkify(bleach.clean(render_template_string(value),tags=allowed_tags, attributes=allowed_attr, strip=True))
render_template_string(value) is the part I'm concerned about. To use a macro inside of the template string (value), every single string has to include the following in order for me to use the macros from this file in the article body:
{% import "includes/macros.html.j2" as macros %}
This would not be reasonable to ensure that writers have access to the macros that I write in all of their articles. Is there any way to pass that argument via the render_template_string() function so that it doesn't have to be defined in every string? Or to otherwise create a template that the string is rendered inside of? Something like the following:
render_template_string(string,macros=function_to_import_macros("includes/macros.html.j2"))
While there does not appear to be a solution to pass an entire macros template as context, get_template_attribute does appear to allow you to pass individual macros one at a time. Example:
includes/macros.html.j2:
{% macro hello() %}Hello!{% endmacro %}
{% macro hello_with_name(name) %}Hello, {{ name }}!{% endmacro %}
Template rendering code:
hello = get_template_attribute("includes/macros.html.j2","hello")
hello_with_name = get_template_attribute("includes/macros.html.j2","hello_with_name")
return render_template("index.html", hello=hello,hello_with_name=hello_with_name)
index.html:
{{ hello() }}
{{ hello_with_name("Bob") }}
This solution will allow for adding individual macros to the context while rendering a template or template_string and can even be added to the global context via this method. If anyone knows how to add an entire template full of macros, that'd rock, but this allows the basic needed functionality.

PhpStorm code completing for Twig templates

When accessing a method via a Twig template the method name is always shortened and the prefix is removed.
For example: {{ object.getName }} gets completed to {{ object.name }} whenever I try to use the full method name, code completion will show nothing but if the full method name is finally entered manually it resolves the method just fine and I can jump to it.
I really really dislike this but could not find a way to disable the behavior. Is it even possible or someone can give me a hint what to do about it?

Wrap contextual html around a specific twig variable {{ product.name }}

I want to automatically wrap some html, lets say <span data-id=".."> when I call {{ product.name }} in my twig template.
So when in a twig template, I use {{ product.name }}, I want the output to be: <span data-type="product" data-id="8" data-prop="name">My product name</span>. I cannot use twig filters or macros, since I really need the template syntax to be {{ product.name }}, so the end-user (template designer), does not have to care about it.
The reason I need this is because I am building an on-page editting tool for twig templates, so I need to know the contexts of those variables from within HTML.
I have tried to override the Compiler that the Twig_Environment uses, but I cannot seem to alter the output of the twig variable node.
How can I do this?
EDIT
I wanted to mention that I need this to use the {{ product.name }} syntax, since other designers will work with those templates outside of Symfony 2. I want to make almost all twig variables editable in the front-end, so a solution with filters or macros can indeed work, but it kills the usability and readability of the platform I am writing. There is no public API currently in twig that can achieve what I want, that is why I am fiddling with the twig compiler. I do not have the required knowledge of the Twig internals to achieve this. If someone could point me into a direction that would be great!
UPDATE 2
I have found a place where I can achieve what I want. Every GetAttr node is compiled to $this->getAttribute($someContext, "property"). So if I can change the subclass of compiled twig template, I can achieve what I want. By default all twig templates extend from Twig_Template. I want to extend this method.
How can I change the subclass of all compiled twig templates?
UPDATE 3
I've found a way to use my own base class for all compiled twig templates. I can simply set it as an option on the twig environment, see this link. I hope to get it working tomorrow and I will post a proper answer on how I solved it all together. Not sure how I will handle escaping, since that will happen after the $this->getAttribute() is called.
I think macros are the best candidates for those kind of wrappings.
For example:
main.twig
{% import "macros.twig" as macros %}
{{ macros.display_product(product) }}
macros.twig
{% macro display_product(product) %}
<span data-id="{{ product.id }}" data-prop="name">{{ product.name }}</span>
{% endmacro %}
Context
product:
id: 8
name: My Georgeous Product
Result
<span data-id="8" data-prop="name">My Georgeous Product</span>
See fiddle
I've solved it by wrapping the PrintNode that is created when parsing a VAR_START token (inside the twig parser) with my own EditablePrintNode. In that node I traverse the expression that is compiled by the print node and get the necessary property path and pass that as an argument to a wrapper function around the default twig escape function that is compiled by the PrintNode.
I suggest to write a custom filter. You can find the doc here about how to write and configure in your environment
// an anonymous function
$filter = new Twig_SimpleFilter('my_custom_product_filter', function ($product) {
return '<span data-id="'.$product->getId().'" data-prop="name">'.$product->getName().'</span>';
});
You need to register as described in the doc
then you can use as follow:
{{ myProduct|my_custom_product_filter}}
Hope this help

Passing string variables through include in twig and slim framework

I am using Slim Framework and Twig.
I want to apply the DRY by using partials. I have a form that is reused in several views with different variables such as titles and route (url) names.
I am struggling on how to make it work on url names.
For example, there is this link using the ´urlFor´ helper with parameter as follows inside a view:
The link
So that is the link I want to pass to the partial template since, it is different in each view, I want to use the partial form. I have tried several approaches but it does not work. I don't know how to pass this string containing ' inside.
For example, I have tried this partial call inside the parent view like this:
{% include 'partials/partial.php' with {'theUrl': "urlFor('route.name', {parameter: value})"} %}
And inside the partial like this:
Show more
It does not work because in the browser's url I see the following:
http://myproject.dev/pages/urlFor('route.name',%20%7Bparameter:%201%7D)
It looks like it is not being escaped properly. Any ideas how to fix this when passing route names with urlFor()?
Without the greatest possibility for testing i think i found the problem. You are simply adding the function inside a string and that does not give the parser an oportunity to resolve the method, because it's basicly just a string.
{% include 'partials/partial.php' with {'theUrl': "urlFor('route.name',{parameter: value})"} %}
Should be
{% include 'partials/partial.php' with {'theUrl': urlFor('route.name',{parameter: value})} %}

Resources