« Back

Liferay DXP Tutorial: Carousel Template

Technical Blogs April 9, 2017 By Neil Jin

Liferay DXP ships with a default carousel Application Display Template in global site. But this one is an old fashion AUI one that has been used often before DXP. Since Liferay DXP support Lexicon, I am going to show you how to utilize Lexicon and Bootstrap to create a true mobile responsive carousel in web content. It's remarkably easy with Lexicon and Bootstrap to create responsive UI component in Liferay DXP.
 
Basic requirement:
A carousel with images.
When the screen resolution changes the images are not out of shape.
Change images without work of modifying the code.
No plugin development.
Reusable.
 
 
Analysis:
We can utilize Bootstrap 3(the default bootstrap in Liferay DXP) carousel component to support the basic carousel feature.(https://www.w3schools.com/bootstrap/bootstrap_carousel.asp)
We can utilize Lexicon Image Aspect Ratios to keep the image in shape when screen resolution changes.(http://liferay.github.io/lexicon/content/images-and-thumbnails/#image-aspect-ratios)
 
We can utilize Web Content Structure to save image information with a repeatable field and a Web Content Template to put HTML and CSS as a framework.
No OSGi module plugin development at all. The JSON format structure and FTL format template are easy to migrate.
 
Step 1: Building our web content structure.
 
We can simply create a web content structure with a, aspect ratio field and repeatable image field.
 
Step 2: Create template for the carousel structure
 
We can simply create a template for the newly created carousel structure.
 
 
Step 3: define your image aspect ratio.
As lexicon documented, you can define a custom aspect ratio for your carousel through CSS. For mobile, I just simply hard-code the ratio.
<style>
 #carousel-<@portlet.namespace /> .aspect-ratio-custom {
    padding-bottom: ${aspectRatio.getData()};
 }
@media (max-width: 799px) {
    #carousel-<@portlet.namespace /> .aspect-ratio-custom {
        padding-bottom: 67%;
    }
 }
</style>
 
Step 4: implement by Bootstrap carousel and Lexicon aspect ratio
You can simply apply boot strap HTML and CSS and loop out the image indexed and images. And apply the aspect-ratio and aspect-ratio-custom to the div contains the image.
<#if image.getSiblings()?has_content>

    <section class="carousel-container">

        <div class="carousel slide" data-ride="carousel" id='carousel-<@portlet.namespace />'>

            <ol class="carousel-indicators hidden-sm hidden-xs">

                <#list image.getSiblings() as cur_img>

                   <li class="${(cur_img?counter == 1)?then('active', '')}" data-slide-to="${(cur_img?counter == 1)?then(0, (cur_img?counter - 1))}" data-target='carousel-<@portlet.namespace />'></li>

                </#list>

            </ol>


            <div class="carousel-inner" role="listbox">

                <#list image.getSiblings() as cur_innerImage>

                    <div class="${(cur_innerImage?counter == 1)?then('active', '')} item">

                        <div class="aspect-ratio aspect-ratio-custom aspect-ratio-middle">

                            <img alt="${cur_innerImage.getAttribute("alt")}" src="${cur_innerImage.getData()}">

                        </div>

                    </div>

                </#list>

            </div>


            <a class="left carousel-control" href='#carousel-<@portlet.namespace />' role="button" data-slide="prev">

                <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>


                <span class="sr-only">Previous</span>

            </a>


            <a class="right carousel-control" href='#carousel-<@portlet.namespace />' role="button" data-slide="next">

                <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>


                <span class="sr-only">Next</span>

            </a>

        </div>

    </section>

</#if>
Step 5: add you content
You can upload some images to your Document and Media Folder.
Then add a web content article based on Carousel structure and template.
For the Aspect Ratio, I put 26%(width/height)
 
You can also utilize Liferay DXP's embedded image editor to edit your image and apply filters.
 
Step 6 Publish and Preview
Publish the web content and display it on the page using Web Content Display portlet.
 
You can change your screen size check if the aspect ratio works.
You can use the Simulation tool to preview if the responsive selector works. Right now the ratio is 67% as I set it in the template.
 

For the structure and template, you can visit the following link to download: http://samples.liferayee.com/tree/master/sample-templates/web-content/carousel

Threaded Replies Author Date
I cannot find the Aspect Ratio Field. Can you... Rahul Joshi April 11, 2017 5:41 AM
Hi Rahul, You need to create this field in your... Neil Jin April 11, 2017 6:29 AM
Sure, I will check that. I guess that would... Rahul Joshi April 11, 2017 6:31 AM
Yes, that's is a workable source code for the... Neil Jin April 11, 2017 6:32 AM
Actually the Aspect Ratio Field is just a text... Neil Jin April 11, 2017 6:34 AM
Can you help me with one more thing? If I need... Rahul Joshi April 24, 2017 8:05 AM
Then you can add the link in the structure and... Neil Jin May 12, 2017 8:44 PM
Actualy there is a little bug in your template.... Christian Pätzold July 27, 2017 5:16 AM
In my case I used... Christian Pätzold July 27, 2017 5:28 AM
Sorry, this is the link:... Christian Pätzold July 27, 2017 5:34 AM
You can find the structure in the Javascript... Christian Pätzold July 27, 2017 5:35 AM
Hi Christian, Thanks for pointing that out! I... Neil Jin July 27, 2017 7:25 PM

I cannot find the Aspect Ratio Field. Can you please guide where it is located?
Posted on 4/11/17 5:41 AM.
Hi Rahul,
You need to create this field in your own structure. Please check my structure json file.
Posted on 4/11/17 6:29 AM in reply to Rahul Joshi.
Sure, I will check that. I guess that would give more details.
Posted on 4/11/17 6:31 AM in reply to Neil Jin.
Yes, that's is a workable source code for the structure.
Posted on 4/11/17 6:32 AM in reply to Rahul Joshi.
Actually the Aspect Ratio Field is just a text field.
Posted on 4/11/17 6:34 AM in reply to Rahul Joshi.
Can you help me with one more thing? If I need to link an image with a blog post or news article in Liferay. How can I do that?
Posted on 4/24/17 8:05 AM in reply to Neil Jin.
Then you can add the link in the structure and html in template accordingly.
Posted on 5/12/17 8:44 PM in reply to Rahul Joshi.
Actualy there is a little bug in your template. In data-target you forgot a # ;)

wrong:
<li class="${(cur_img?counter == 1)?then('active', '')}" data-slide-to="${(cur_img?counter == 1)?then(0, (cur_img?counter - 1))}" data-target='carousel-<@portlet.namespace />'></li>

right: <li class="${(cur_img?counter == 1)?then('active', '')}" data-slide-to="${(cur_img?counter == 1)?then(0, (cur_img?counter - 1))}" data-target='#carousel-<@portlet.namespace />'></li>
Posted on 7/27/17 5:16 AM in reply to Neil Jin.
In my case I used "${.vars['reserved-article-id'].data}" instead of "<@portlet.namespace />"

You can find the structure in the Javascript field. Template is in the css and html field.
<script async src="//jsfiddle.net/rhg7o359/embed/"></script>
Posted on 7/27/17 5:28 AM in reply to Rahul Joshi.
Sorry, this is the link: https://jsfiddle.net/rhg7o359/
Posted on 7/27/17 5:34 AM in reply to Christian Pätzold.
You can find the structure in the Javascript field. Template is in the css and html field.

https://jsfiddle.net/rhg7o359/
Posted on 7/27/17 5:35 AM in reply to Rahul Joshi.
Hi Christian, Thanks for pointing that out!
I think using ${.vars['reserved-article-id'].data} may cause duplicated id if you add 2 web-content-display portlet to the same page with the same web content. Uisng portlet namespace can gurantee the id is unique in the page.
For the name space, you can use <@portlet.namespace /> or ${request.get("portlet-namespace")} or ${request["portlet-namespace"]} depends on what version of Liferay you are using.
Posted on 7/27/17 7:25 PM in reply to Christian Pätzold.