Pass template literals into includes - twig

I'd like to create DRY code by passing template literals into a twig {% includes 'X' %} as variables so they can be used in the includes code (examples below).
I have seen that you can pass a variable into a includes, like the below:
{% include 'template.html' with {'foo': 'bar'} %}
But since my code is going to be using the same includes template multiple times on the same page I need a bit more customization here.
Ideal what I want to do is set the viable when calling the includes and have that echoed into the template, example:
in page file:
{% set contact_img = 'path to image location' %}
{% include "/IMPORT/Specific/full_page_img_bg" with contact_img %}
in template
{% set transformed_{{ VARIABLE NAME PASSED HERE }} = craft.imager.transformImage({{ VARIABLE NAME PASSED HERE }}_for_transform, [
{ width: 2000, jpegQuality: 89 },
{ width: 1300, jpegQuality: 84 },
{ width: 750, jpegQuality: 82 },
{ width: 500, jpegQuality: 80 },
], { allowUpscale: false, mode: 'fit'}) %}
<img sizes="
(max-width: 500px) 500px,
(max-width: 750px) 750px,
(max-width: 1280px) 1300px,
2000px"
srcset="{{ craft.imager.srcset(transformed_{{ VARIABLE NAME PASSED HERE }}) }}"
alt="{{ {{ VARIABLE NAME PASSED HERE }}.title }}"
class="full-background-image"/>
<div class="full-gradient"></div>
Basically its like how in JS you can set a functions argument and that argument is outputted to the function, but you can call that function multiple times with different arguments i.e.
function callName(name) {
console.log(name)
}
callName('DEV-BOY')
#DEV-BOY
callName('BOY-BE_DEV')
#BOY-VE_DEV
Feel free to ask any question, I'm sure there must be a way to do this,
thanks in advance - W

You can create macros (which will operate) like functions, see https://twig.symfony.com/doc/2.x/tags/macro.html
Examples:
{% macro input(name, value, type = "text", size = 20) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}"/>
{% endmacro %}
{% macro textarea(name, value, rows = 10, cols = 40) %}
<textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols }}">{{ value|e }}</textarea>
{% endmacro %}
In the examples above you can see that you enclose your macro between a macro and endmacro statement. The macro statement takes some parameters that you define, possibly with default values if needed. You can call them very much like functions, examples:
<p>{{ forms.input('username') }}</p>
<p>{{ forms.input('password', null, 'password') }}</p>
All examples were taken from the source linked at the start of this answer. You will need to place your parameterized includes into some macros. Ideally you would separate your parameterized include into a separate file where you could implement several macros of this type, import it and call the macros where you need them with the parameters you need.

Related

Gutenberg Blocks with Timber and ACF Flexible Content

We're creating a block.twig template as per:
https://timber.github.io/docs/guides/gutenberg/#how-to-use-acf-blocks-with-timber
to use ACF Flexible Content Field Types to create Gutenberg Blocks.
For reference the following sample code would be for Repeater Field Types:
{% for field in fields.repeater %}
Title: {{ field.title }} <br/>
Url: {{ field.url }}
{% endfor %}
which we tested and seems to work fine.
What would be the equivalent using Flexible Content fields?
Thanks.
#Rafael they're a little more complicated, just need to handle them like arrays. Let's pretend your Flexible Content field is called my_fc_field inside of it. You created 2 layouts, each has a single text field, one called tf_one, the other fc_two
{% for subfield in fields.my_fc_field %}
Layout name: {{ subfield.acf_fc_layout }}
Text Field Value: {{ subfield.tf_one ? subfield.tf_one : subfield.tf_two }}
{% endfor %}
doing a {{ dump(fields.my_fc_field) }} should make it clear what's inside there that you can manipulate/output

Twig direct include doesn't work, only "with"ing specific keys does

I'm using Pattern Lab with twig templates and having an issue with using objects directly in the with statement in includes.
This is my teaser-content-blocks organism:
{% set content_blocks = [
{
teaser_photo: {
url: 'https://img.buzzfeed.com/buzzfeed-static/static/enhanced/terminal01/2011/3/29/17/enhanced-buzz-14894-1301433714-5.jpg',
alt: 'Man sleeping on a cake',
title: 'Man sleeping on a cake',
height: '200px',
width: '400px',
},
}
] %}
<div class="teaser-content-blocks">
{% for content_block in content_blocks %}
{% include '#molecules/teaser-content-block.twig' with content_block only %}
{% endfor %}
</div>
This is the teaser-photo atom:
<div class="teaser-photo">
<img
src="{{ url }}"
alt="{{ alt }}"
title="{{ title }}"
/>
</div>
And this is what I'm attempting to do with the teaser-content-block molecule:
<div class="teaser-content-block">
<div class="left">
{% include '#atoms/teaser-photo.twig' with teaser_photo only %}
</div>
<div class="right">
{% include '#atoms/title.twig' with { title: 'Sleep on a cake', element: 'h3' } only %}
{% include '#atoms/teaser-body.twig' with { body: "When we say 'sweet dreams', this isn't quite what we mean! Check this mad lad not understanding the general concept of pillows! This utter banterboy has completely confused what he is meant to use as a confectionary-based celebration of one's birth and a soft item of bedding designed to cushion the head during sleep or light rest. Mental!" } only %}
</div>
</div>
It seems, however, that the with teaser_photo only statement causes twig to break and not compile, with an error of Catchable fatal error: Argument 1 passed to Twig_Template::display() must be of the type array, null given.
If I change that include statement to the following:
{% include '#atoms/teaser-photo.twig' with { url: teaser_photo.url, alt: teaser_photo.alt, title: teaser_photo.title } only %}
... it works fine, but this is way too verbose for my liking. I'd rather just pass the object down as it is. It just seems to not want to pick it up in the correct manner, even though theoretically it should work the same.
I'm not a twig expert so I may be missing something really obvious.
Any help much appreciated as always!
Discovered the issue. Pattern Lab of course tries to render every available pattern, so it was trying to render teaser-content-block.twig which is trying to reference the variable teaser_photo, which of course doesn't exist in this scope. So the cure was to replace that line with one that uses a default, like so:
{% include '#atoms/teaser-photo.twig' with teaser_photo|default({}) only %}
... or at least something that does a similar thing. Thanks to #DarkBee too for taking the time to have a look.

Set an object with Twig into a template thats imported via another template?

Im including a link.twig into block.twig, and block.twig into page.twig. in my set options is there a way I can change the link objects name to something like heroLink?
I need to set options within page.twig. link.twig is included into other templates so I dont want to change it (eg changing link.url to heroLink.url).
In my page:
{% set options = {
title: 'my title',
link: {
text: 'Search',
url: "www.google.com"
}
}
%}
{% include "block.twig" with options %}
In block.twig:
<div class="something">
<h2>{{ title }}</h2>
<div class="hero">
{% include "link.twig" with {'style': 'primary'} %}
</div>
</div>
In link.twig:
{{ link.text }}
The reason for this is that block.twig actually has other links. link.twig may be imported multiple times. As the mock object needs to be created in page.twig something like heroLink makes a lot more sense in this context.
In my page:
{% set options = {
action: {
text: 'Action',
url: "action.com"
}
}
%}
{% include "component.twig" with options %}
In component.twig:
{% import "link.twig" as mainLink %}
{{ mainLink.link(action.url, action.text) }}
In link.twig
{% macro link(url, text) %}
{{ text }}
{% endmacro %}

Swig (node.js) Include a File and pass an object

I am facing this problem.
I've got a page with an include of another like this:
index.html
{{ set pets = { pets : petsObject } }}
{{ include pets.html }}
petsObject is an object like this
petsObjects: [
{ name : "cat" },
{ name : "dog" }
]
When I try to render the page I get a blank page with only this:
[object Object]
I have no clue about what is going on :(
Thanks in advance!
Seems you'll need to use:
{% include pets.html with pets %}
According to docs for include:
Locally declared context variables are not passed to the included template by default.
It is also recommended for performance to use the only keyword after the included terms, like this:
{% include pets.html with pets only %}
Beyond that, it depends on the contents of pets.html, which you haven't included here. But, make sure that you're attempting to output the name:
{% for pet in pets %}
{{ pet.name }}
{% endfor %}
Or use a filter like json_encode() to format it:
{% for pet in pets %}
{{ pet|json_encode }}
{% endfor %}
Trying to output the Objects themselves will simply produce [object Object]:
new Object().toString() === "[object Object]"

Using twig variable to dynamically call an imported macro sub-function

I am attempting if use a variable to call a specific macro name.
I have a macros file that is being imported
{% import 'form-elements.html.twig' as forms %}
Now in that file there are all the form element macros: text, textarea, select, radio etc.
I have an array variable that gets passed in that has an elements in it:
$elements = array(
array(
'type'=>'text,
'value'=>'some value',
'atts'=>null,
),
array(
'type'=>'text,
'value'=>'some other value',
'atts'=>null,
),
);
{{ elements }}
what im trying to do is generate those elements from the macros. they work just fine when called by name:
{{ forms.text(element.0.name,element.0.value,element.0.atts) }}
However what i want to do is something like this:
{% for element in elements %}
{{ forms[element.type](element.name,element.value,element.atts) }}
{% endfor %}
I have tried the following all resulting in the same error:
{{ forms["'"..element.type.."'"](element.name,element.value,element.atts) }}
{{ forms.(element.type)(element.name,element.value,element.atts) }}
{{ forms.{element.type}(element.name,element.value,element.atts) }}
This unfortunately throws the following error:
Fatal error: Uncaught exception 'LogicException' with message 'Attribute "value" does not exist for Node "Twig_Node_Expression_GetAttr".' in Twig\Environment.php on line 541
Any help or advice on a solution or a better schema to use would be very helpful.
I just thought other people may want the answer to this, as provide by fabpot:
This is indeed something that is not supported: calling a macro with a dynamic name (I have added a proper exception to be clearer about the issue).
If you really want to do that, you can do so with the following code:
{{ attribute(forms, element.type, [element.name,element.value,element.atts]) }}
-fabpot
https://github.com/twigphp/Twig/issues/922#issuecomment-11133299
Dynamic macros may not be supported in Twig.
But there is a simple workaround since you can dynamically include other templates.
Example:
Let's say you have a bunch of content modules or content blocks (or however you wanna call them) for your site. And you have Twig macros responsible of rendering each of these modules.
{# modules.twig #}
{% macro module1(config) %}
<div>module one</div>
{% endmacro %}
{% macro module2(config) %}
<div>module two</div>
{% endmacro %}
{% macro module3(config) %}
<div>module three</div>
{% endmacro %}
Now, what you need to dynamically call these macros is to add an extra template for each, like so:
{# module1.twig #}
{% import "modules.twig" as modules %}
{{ modules.module1(config) }}
{# module2.twig #}
{% import "modules.twig" as modules %}
{{ modules.module2(config) }}
{# module3.twig #}
{% import "modules.twig" as modules %}
{{ modules.module3(config) }}
Finally, in your actual page template you just include the template instead of calling the macro.
{# template.twig #}
{# this is the macro's name to be called #}
{% set macro = 'module2' %}
{# this is just a config object to be passed to the macro #}
{% set config = {} %}
{% include macro ~ '.twig' with { config: config } only %}
Et voilá, (dynamically produced) output will be <div>module two</div>.

Resources