1

I'm sure there must be a tidier and more efficient way of doing this so thought I'd throw it out there for some opinions.

Building a site that has the usual lists (in grid format) for Blog, Gallery, Meet the Team, etc... I'm calling these 'cards', and I'm including them with various attributes:

{% include "_modules/card"
    with {
        author: true,
        badge: true,
        date: true,
        magnify: true,
        meta: true,
        thumbnail: true,
    }
%}

Then in my card I've got:

<div class="card">

{% if badge is defined %}
    <div class="badge">
        <img src="/assets/images/icon-new.svg" height="14px" width="14px" alt="New!">
    </div>
{% endif %}

{% if thumbnail is defined %}
    <a href="#">
        <img src="http://placehold.it/320x210" 
             srcset="http://placehold.it/640x420 750w, 
                     http://placehold.it/320x210 280w" 
             sizes="100%"
             alt=""
             class="tn">
    </a>
{% endif %}

<article>
    <h2><a href="#">Heading Goes in Here</a></h2>

    {% if meta is defined %}

        <footer class="meta">
            {% if date is defined %}
                {% include "_modules/relative-date" %} 
            {% endif %}
            {% if author is defined %}
                by <a href="#" rel="author">John Doe</a>
            {% endif %}
        </footer>

    {% endif %}

</article></div>

This all works as expected but I can't help feel it could be neater. There are a lot of conditions at work here for example, and this will all be part of an entries loop too. Anyone have any suggestions for streamlining this a bit? Much appreciated.

3

I'm not sure if this is better or not but one thing I'd do here is to set some defaults in my include. This also means you don't need to worry about checking whether it's defined or not. You're just checking whether it's true.

{% set options = {
    author: true,
    badge: true,
    date: true,
    magnify: true,
    meta: true,
    thumbnail: true,
} | merge(cardOptions | default({})) %}

<div class="card">

    {% if options.badge %}
        <div class="badge">
            <img src="/assets/images/icon-new.svg" height="14px" width="14px" alt="New!">
        </div>
    {% endif %}

    {% if options.thumbnail %}
        <a href="#">
            <img src="http://placehold.it/320x210" 
                srcset="http://placehold.it/640x420 750w, 
                     http://placehold.it/320x210 280w" 
                sizes="100%"
                alt=""
                class="tn">
        </a>
    {% endif %}

    <article>
        <h2><a href="#">Heading Goes in Here</a></h2>

        {% if options.meta %}

            <footer class="meta">
                {% if options.date %}
                    {% include "_modules/relative-date" %} 
                {% endif %}
                {% if options.author %}
                    by <a href="#" rel="author">John Doe</a>
                {% endif %}
            </footer>

        {% endif %}

    </article>
</div>

Then when I use this include I can pass in an object of options that could overide those defaults. But I only need to pass in values that need to change the defaults. For example here I can set meta to false but can leave all the others to be handled by the default:

{% set cardOptions = {
    meta: false
} %}

{% include "_modules/card" with cardOptions %}

You said this would be included within an entries loop. You could do this in that case:

{% set options = {
    entry: false,
    author: true,
    badge: true,
    date: true,
    magnify: true,
    meta: true,
    thumbnail: true,
} | merge(cardOptions | default({})) %}

{# Wrap the whole card in a conditional #}
{% if options.entry %}
   // Card goes here
{% endif %}

And then in your entries loop you'd do:

{% for entry in entries %}

    {% set cardOptions = {
        entry: entry
    } %}

    {% include "_modules/card" with cardOptions only %}
{% endfor %}

I'd probably use the only option on the include tag to avoid stuff leaking in.

I've also played with options such as:

{% set cardOptions = {
    styleGuide: true
} %}

In my include this option will have a false default I'll have a conditional that checks for it being true and if so it'll ignore any dynamic content and instead I'll output an example of the module with fake content. It's good for testing and for keeping a library of modules.

You could also consider a macro that wraps your include tag. I gave a similar example in a blog post not long ago: Using macros to improve consistency in your Craft CMS templates

{% macro module(type, options = {}) %}
    {% include '_modules/' ~ type with options only %}
{% endmacro %}

And then in your template you'd do something like:

{% set cardOptions = {
    entry: entry,
    meta: false
} %}

{{ module('card', cardOptions) }}

Dunno if any of that helps!

  • Thanks Jamie, that's really helpful! I'll check out your blog post too – I've dabbled with macros before but I know I need to get properly stuck into them. – Ian Ebden Apr 7 '16 at 8:17

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.