Hot answers tagged

19

Fabien Potencier, the main developer of the Twig templating language answered the question like so: Macros: reusable markup across a lot of templates Includes: part of "pages" that are extracted for readability and reusability The reason for this, as far as I understand, is that you can only use the include tag to render the complete file ...


12

I think the issue with trying to set a variable in a macro is that the variable is limited to the scope of the macro you are triggering, so {% set parent = thisEntry %} is available within the scope of your macro, but doesn't define it for the rest of your template. Macros are comparable with functions in regular programming languages. They are useful to ...


9

A little bit late but maybe someone else has the same issue and want's to accomplish this. You can use this Plugin Twig Perversion to create macros that return values {% macro getParentEntry(thisEntry) %} {% import _self as self %} {% if thisEntry.parent|length %} {% set parent = thisEntry.parent %} {% else %} {% set parent = ...


9

I've been able to render CP templates in the front end using some methods in PathService and TemplatesService, which you could use inside your own Template Variable to render the macros: class YourVariable { public function renderFormMacro($macro, array $args) { // Get the current template path $originalPath = craft()->path->...


5

The first example makes sense because you are passing a multidimensional array (syName), looping through that array, assigns each array within to alt allowing you to then access the values through dot notation ({{ alt.name }}), because alt is an array. In your second example you are passing through just one array, so when you loop through it, the value in ...


5

You're close! If there's one thing I've learned about Twig, it's easy to overthink it... Create an array with the first date, then merge the rest of dates into the same array with a for loop. Just increment the day in the range: {% set rangeOfDates = [ now|date("l dS F Y") ] %} {% for i in range (1,13) %} {% set rangeOfDates = rangeOfDates|merge( ...


5

To answer your title question... macros in Craft 3 work exactly the same as they do in Craft 2. :) The problem you are hitting is here: {{ macros sizedImage... Seems like a straightforward typo. I'm assuming you meant to write it like this: {{ macros.sizedImage... For that reason (among others), I would recommend not naming your imported macros as "...


4

If I had to guess, I'd say a macro would be faster because it's accessing the file system less. In any case, unless you're dealing with hundreds of thousands of calls, I'd also guess that's the difference is going to be negligible for most use cases.


4

We've done this on a few sites and you're right it's extremely helpful. The only way I've found to get it to work is to simply copy the "_includes/forms" folder from craft/app/templates to the sites craft/templates It works but I'd love a less hacks way to do it, either through a plugin or some built-in means.


4

Merge operates on arrays or array-like things, and you probably handed it the string "card", which is why you got that error. I think what you want to do here is use the transform in the control panel to define the aspect ratio, but use the img macro to determine which sizes to transform to. You can calculate the aspect ratio like this: {% set aspectRatio ...


4

I figured it out! I ditched the macro and just included the following in my template: {% for field in courseFields %} <section class="col-xl mb-3"> <h2 id="{{ field.handle }}">{{ field.title }}</h2> {{ entry[field.handle] }} </section> {% endfor %}


3

The better approach would be to pass the icon as a parameter to your macro. So I would do something like {% macro renderIcon(type) %} {% switch type %} {% case 'video' %} {% include 'video.twig' %} {% case 'audio' %} {% include 'audio.twig' %} {% default %} {% include '_generic.twig.twig' %} ...


3

Edit: I changed it to your variables, not sure if I set the array brackets 100% correct but the idea should work. You could set the defaults in the macro as you already did, then in your template call the macro and pass the options e.g. : {% set image = block.slide.first() %} {# macro #} {% set imgParam = [ thumb: { quality: 60, }, tSM: { mode: '...


3

You aren't able to pass an import into a macro. So helpers needs to be imported into each macro file that uses it. You can however pass the cart to your macros. As a side note, it's definitely best to set the cart variable in a global template - this means you're only ever setting it once. All templates that extend this global template then inherit cart. ...


3

You don't want braces within braces. You want something like this instead: {% set index = craft.entries.section(sectionName ~ 'Index').first %} Same needs changing in the for loop.


3

You can't nest a for loop within a switch, but outside of a case. Try this for brevity: {% macro backgrounds(backgrounds, fieldName) %} {% for background in backgrounds %} {{ fieldName == background.value ? background.class : '' }} {% endfor %} {% endmacro %} Also, your backgrounds array won't work sitting in the macro file. It needs to be ...


3

I see a few problems. The first is that {{ grid.item(entry.image) }} is actually an ElementCriteriaModel (i.e. which behaves like an array of images) and not an ElementModel (i.e. a single image), even if only one image is defined. To get the first image you can use entry.image.first or entry.image[0]. The second problem is that in your macro, <img src="{...


3

Try using an array syntax instead of the object syntax: {% set string = entry['fieldname' ~ 1] %}


3

Hard to say for sure, but here's what I suspect... You may have multiple instances of firstImage being called from your main template. For some of those instances, entry.sliderMatrix.total is greater than zero. For other instances, entry.sliderMatrix.total is equal to zero. And of course, when entry.sliderMatrix.total is equal to zero, your if statement ...


3

This is a change in how macros work in Twig 2.0, not a Craft CMS issue (though Craft 3 did signal a move to Twig 2.x). 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. https://twig.symfony.com/doc/1.x/...


2

Your syntax is close, but not quite right and you're setting a variable as a string and later setting it as an Element Model. I think you're searching for something closer to this: {% macro firstImage(entry) %} {% if entry.sliderMatrix.total() %} {% set block = entry.sliderMatrix.first %} {% set image = block.image.first %} {% endif ...


2

You could factor out that repeated class=... like so: {% macro _btnClass(type, colour, size, fullWidth) %} class="btn {{ type }} {{ colour }} {{ size }}{% if fullWidth %} btn-block{% endif %}" {% endmacro %} {% macro span(type, text, colour, size, fullWidth, url) -%} {% import _self as self %} <span {{ self._btnClass(type, colour, size, ...


2

This is definitely not a great answer, but I'm not sure anyone's going to be able to really answer this. You'd have to set up some sort of unit tests or something. And it will probably depend a lot on your server environment. But think of all the class files that need to get loaded just to handle a simple request. Personally, I think that the performance hit ...


2

Here’s a Craft 3-friendly way of achieving what Mike suggested. This assumes your app already has a custom twig extension, if it doesn’t you can easily create one. Add use craft\web\View; to the top of your TwigExtension, then in getFunctions() add your function definition: new \Twig\TwigFunction('field', [$this, 'renderFormMacro'], ['is_safe' => ['html'...


2

Searching for uses of renderMacro in the Craft code I see that it is always called like: craft()->templates->renderMacro('_includes/forms', 'textield', array( array( 'name' => 'find', 'placeholder' => Craft::t('Find'), ) )); So, what you want is probably $html = craft()->templates->renderMacro($template, $macro, array($args));...


2

The format of the include tag is {% include 'template' with {var1: value1, var2: value2} only %} What you need after the with is an associative array (an array with keys and values). In your specific case you want this: {% include "_macros/elements/listing" with {config: config} only %}


2

I had the same issue and managed to get it working like so: {{ forms.selectField({ options :[ {optgroup:'North Island'}, {label : 'Foo', value: 'foo'}, {label : 'Bar', value: 'bar'}, {optgroup:'South Island'}, {label : 'Baz', value: 'baz'} ], name: name, value : value })}} So I guess in php (if working from a plugin)...


2

So I took another approach to my problem and ended up creating a plugin variable allowing me to set data inside a macro and have it accessible in the template. It's not complicated and does the trick. class MyPluginVariable { /** * Store data for use within both * templates and macros. Easy way around * macros having their own context * @var [...


2

Not tested, but I think you can test if it is a pagination page. So {% if (craft.request.getSegment('2') == '') or (craft.request.getPageNum() != 1) %}active{% endif %}


2

product is not defined inside your macro. As the twig macro documentation says: as with PHP functions, macros don't have access to the current template variables. So you will need to pass this one in: {% macro teaser(product, imgSize, className) %} And when you call it: {{ m_product.teaser(product, 300, "product-teaser--in-list") }} I would also add ...


Only top voted, non community-wiki answers of a minimum length are eligible