Using Wagtail, I need to display cards gallery where one of the feature of the card is a carousel images. I made it with images only, but when I tried to add a caption text to each carousel I had a problem to display the data. While I can display the first image on my carousel, I cannot navigate with the Next/Previous carousel icons, also, the caption text fields are blank. It seems like I don't use the right syntax to pass the variable names to the carousel.
As always, many thanks for any input on how to solve the syntax and/or a better block structure to my carousel model.
Data flow goes from carouselblock to blogpage (a child of) bloglistingpage
blocks.py:
class CarouselBlock(blocks.StreamBlock):
carousel_image = blocks.StructBlock([
('image', ImageChooserBlock()),
('caption', blocks.TextBlock()),
])
class Meta:
icon = "cogs"
template = "streams/custom_carousel_block.html"
models.py:
class BlogListingPage(Page):
content = StreamField(
[
...
("cards", blocks.CustomCardBlock()),
],
null=True,
blank=True,
)
content_panels = Page.content_panels + [
...
StreamFieldPanel("content"),
]
def get_context(self, request):
# Update context to include only published posts, ordered by reverse-chron
context = super().get_context(request)
blogpages = self.get_children().live().order_by('-first_published_at')
context['blogpages'] = blogpages
return context
class BlogPage(Page):
...
main_content = StreamField([
('carousel', CarouselBlock()),
...
],
null=True,
blank=False)
...
content_panels = Page.content_panels + [
...
StreamFieldPanel('main_content'),
]
streams/custom_carousel_block.html:
{% load wagtailcore_tags wagtailimages_tags %}
<div id="carousel" class="carousel slide" data-ride="carousel">
<div class="carousel-inner">
{% for sub_block in self %}
{% image carousel_image.value original as carousel_img %}
<ul class="carousel_image">
{% for img in block.value %}
<li class="carousel-item {% if forloop.first %}active{% endif %}">
<img src="{{ image.value.url }}" class="d-block w-100" alt="{{ image.value.alt }}">
{% for sub_block in all_carousel %}
{# for caption I tried both syntax SPECIFIC/VALUE, none worked for me #}
{% if sub_block.specific.caption %}
<p>{{ sub_block.specific.caption }}</p>
{% endif %}
{% endfor %}
</li>
{% endfor %}
<li>{{carousel_image.value.caption}}</li>
</ul>
{% endfor %}
</div>
<a class="carousel-control-prev" href="#carousel" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#carousel" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
Starting from the line:
{% for sub_block in self %}
Here self
is the StreamBlock value, which behaves just as the top-level StreamField value would in your page template - looping over it with {% for sub_block in self %}
will give you a sub_block
object with two properties block_type
and value
.
In this case, sub_block.block_type
will always be 'carousel_image'
because that's the only block type available within the stream. sub_block.value
will give you the block's value - and since that block type is a StructBlock consisting of 'image' and 'caption', the value will be a dictionary consisting of those two fields, which you can access as sub_block.value.image
and sub_block.value.caption
.
The code therefore becomes:
{% for sub_block in self %}
{% image sub_block.value.image original %}
<p>{{ sub_block.value.caption }}</p>
{% endfor %}
(I'll leave you the task of fitting the HTML back around that.)