1

I am attempting to make use of the method listed here https://nystudio107.com/blog/creating-a-content-builder-in-craft-cms

twig template:

{% extends '_layouts/base.twig' %}

{% block sectionHeader %}
    {% include '_partials/entry-header.twig' %}
{% endblock %}

{% block content %}
    {% for block in entry.level4Content %}
        {% include '_level4/' ~ block.type.handle %}
    {% endfor %}
{% endblock %}

Cards.twig

<div class="relative bg-gray-50 pt-16 pb-20 px-4 sm:px-6 lg:pt-24 lg:pb-28 lg:px-8">
    <div class="absolute inset-0">
    </div>
    <div class="relative max-w-7xl mx-auto px-8">
        <div class="mt-12 max-w-lg mx-auto grid gap-5 lg:grid-cols-3 lg:max-w-none">

        
            <div class="flex flex-col rounded-lg shadow-lg overflow-hidden">
                {% if block.image|length %}
                    <div class="flex-shrink-0">
                        {% for image in block.image.limit(1).all() %}
                            <img class="h-48 w-full object-cover"
                                src="{{ image.url }}"
                                alt="{{ image.title }}" />
                        {% endfor %}
                    </div>
                {% endif %}
                <div class="flex-1 bg-white p-6 flex flex-col justify-between">
                    <div class="flex-1">
                        <a href="{{ block.resourcelinks }}" class="block">
                            <h3 class="mt-2 text-xl leading-7 font-semibold text-gray-900 hover:underline">
                                {{ block.heading }}
                            </h3>
                            <p class="mt-3 text-base leading-6 text-gray-500">
                                {{ block.cardContent }}
                            </p>
                        </a>
                    </div>
                    <div class="mt-6 flex items-center">
                        <div class="ml-0">
                            <div class="flex text-sm leading-5 text-gray-500">
                                <time datetime="{{ entry.postDate|date('M d, Y') }}">{{ entry.postDate|date('M d, Y') }}</time>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

As expected the loop is functioning as expected, however is there some way in the cards.twig where I can specify the loop? So it uses part of the code and not all of it? Alternatively, if the loop is already in effect at this stage can I specify a block to identify the loop? For example:

{% extends '_layouts/base.twig' %}

{% block sectionHeader %}
    {% include '_partials/entry-header.twig' %}
{% endblock %}

{% block content %}
    {% for block in entry.level4Content %}
        {% ('include '_level4/' ~ block.type.handle') with {
        block: matrix
    } %}
    {% endfor %}
{% endblock %}

cards.twig

<div class="relative bg-gray-50 pt-16 pb-20 px-4 sm:px-6 lg:pt-24 lg:pb-28 lg:px-8">
    <div class="absolute inset-0">
    </div>
    <div class="relative max-w-7xl mx-auto px-8">
        <div class="mt-12 max-w-lg mx-auto grid gap-5 lg:grid-cols-3 lg:max-w-none">
{% block Matrix %}
            <div class="flex flex-col rounded-lg shadow-lg overflow-hidden">
                {% if block.image|length %}
                    <div class="flex-shrink-0">
                        {% for image in block.image.limit(1).all() %}
                            <img class="h-48 w-full object-cover"
                                src="{{ image.url }}"
                                alt="{{ image.title }}" />
                        {% endfor %}
                    </div>
                {% endif %}
                <div class="flex-1 bg-white p-6 flex flex-col justify-between">
                    <div class="flex-1">
                        <a href="{{ block.resourcelinks }}" class="block">
                            <h3 class="mt-2 text-xl leading-7 font-semibold text-gray-900 hover:underline">
                                {{ block.heading }}
                            </h3>
                            <p class="mt-3 text-base leading-6 text-gray-500">
                                {{ block.cardContent }}
                            </p>
                        </a>
                    </div>
                    <div class="mt-6 flex items-center">
                        <div class="ml-0">
                            <div class="flex text-sm leading-5 text-gray-500">
                                <time datetime="{{ entry.postDate|date('M d, Y') }}">{{ entry.postDate|date('M d, Y') }}</time>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
{% endblock %}
        </div>
    </div>
</div>

However when trying to use the block method to specify the loop this gave me an error: Impossible to access an attribute ("subHeading") on a string variable ("matrix").

{{ block.subHeading }} is part of another twig file block of the same matrix field, the reason this error is coming up is because the its the first error it sees. Im not sure if its supported in this manner to use {{ block.subHeading }}inside {% block %}. Open to suggestions...

2
  • I'm not sure I understand what you're trying to achieve here. Can you explain the layout outcome that you're after - or at least what parts of the card template you want to change based on the loop? Maybe that'll make a solution become more clear. – Martin Spain May 13 at 8:43
  • Hi Martin, I am tying to implement the setup as per the linked article. (nystudio107.com/blog/creating-a-content-builder-in-craft-cms). Example: say I have an entry with with my Matrix block. One of the fields is the Cards field, for every Card field that is populated the whole card.twig is rendered instead of just the card component itself. Basically the Outer Divs only need to be rendered once as opposed to x number of card fields completed in the CP. I have this working with {% switch %} and {% case %} in another part of the site, but wanted to follow article to stay DRY. – user3536638 May 16 at 23:37
2

This isn't ideal and reviewing your markup might be a better alternative but you could use the loop variable provided by Twig's for (docs).

For you example, something like this should work

{# Inject this on the first iteration only #}
{% if loop.first %}
<div class="relative bg-gray-50 pt-16 pb-20 px-4 sm:px-6 lg:pt-24 lg:pb-28 lg:px-8">
    <div class="absolute inset-0">
    </div>
    <div class="relative max-w-7xl mx-auto px-8">
        <div class="mt-12 max-w-lg mx-auto grid gap-5 lg:grid-cols-3 lg:max-w-none">
{% endif %}
            <div class="flex flex-col rounded-lg shadow-lg overflow-hidden">
                {% if block.image|length %}
                    <div class="flex-shrink-0">
                        {% for image in block.image.limit(1).all() %}
                            <img class="h-48 w-full object-cover"
                                src="{{ image.url }}"
                                alt="{{ image.title }}" />
                        {% endfor %}
                    </div>
                {% endif %}
                <div class="flex-1 bg-white p-6 flex flex-col justify-between">
                    <div class="flex-1">
                        <a href="{{ block.resourcelinks }}" class="block">
                            <h3 class="mt-2 text-xl leading-7 font-semibold text-gray-900 hover:underline">
                                {{ block.heading }}
                            </h3>
                            <p class="mt-3 text-base leading-6 text-gray-500">
                                {{ block.cardContent }}
                            </p>
                        </a>
                    </div>
                    <div class="mt-6 flex items-center">
                        <div class="ml-0">
                            <div class="flex text-sm leading-5 text-gray-500">
                                <time datetime="{{ entry.postDate|date('M d, Y') }}">{{ entry.postDate|date('M d, Y') }}</time>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
{# Inject this on the last iteration only #}
{% if loop.last %}
        </div>
    </div>
</div>
{% endif %}

This will inject the opening part of your wrapper on the first iteration and close it on the last one.

3
  • Thanks @oli for some reason, it does not seem to think that start of the card.twig is the first loop. When I use {% if loop.first %} its does not render anything in between that and the {% endif %} – user3536638 May 16 at 23:30
  • Is your card template getting called directly by {% include '_level4/' ~ block.type.handle %} or do you have something in between? – Oli May 17 at 13:25
  • it's getting called directly. So in the Matrix Field, all the different blocks have their own block.twig and my understanding is that {% include '_level4/' ~ block.type.handle %} iterates through all of them. I get this as a method of staying DRY, but I don't understand how it works when you need multiple entries in the same div wrapper.... – user3536638 May 17 at 15:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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