This article covers a Bloomreach Experience Manager version 11. There's an updated version available that covers our most recent release.

Develop the Products Feature Part 2: Products Overview Page

Previous Step

Develop the Products Feature Part 1: Products Document Type

Now that you have created the Product document type and content bean you can implement the Products Overview page. You will configure the page and URL, use an out-of-the-box Java component to implement the business logic, and write a Freemarker template to render the products.

Products Overview Page

Open the Hippo Console in your browser: http://localhost:8080/cms/console/. Log in as user 'admin' with password 'admin'.

Browse to the node /hst:hst/hst:configurations/gogreen/hst:pages. This is where the page configurations are stored.

Add a new node called productslist of type hst:component.

Add a property hst:referencecomponent to the productslist node and enter the value hst:abstractpages/twocolumns.

/hst:hst/hst:configurations/gogreen/hst:pages
    + productslist [hst:component]
        - hst:referencecomponent = hst:abstractpages/twocolumns

You have now defined a new page configuration productslist that extends the abstract twocolumns page configuration you defined earlier. You will define what goes in the content area later.

Browse to the node /hst:hst/hst:configurations/gogreen/hst:sitemap. This is where URLs are mapped to content paths and page configurations.

Add a new node called  products of type  hst:sitemapitem.

Add a property   hst:componentconfigurationid to the   products node and enter the value   hst:pages/productslist.

Add a property   hst:relativecontentpath to the   products node and enter the value   products.

/hst:hst/hst:configurations/gogreen/hst:sitemap
    + products [hst:sitemapitem]
        - hst:componentconfigurationid = hst:pages/productslist
        - hst:relativecontentpath = products

You have now defined a (relative) URL  products that uses the  productslist page configuration to display the content at the path  products (relative to the content root folder).

Point your browser to the new URL you defined:   http://localhost:8080/site/products. At this point, it displays the common page elements and a still empty content area.

Products Overview Business Logic

The business logic for a Hippo component is implemented by a Java class. Although it's possible to write the Java code from scratch, Hippo Developer Essentials include out-of-the-box components for many use cases. The List Component retrieves a collection of documents from the content repository and makes them available in a paginated list. This is exactly what is needed for the Products Overview.

The documentation of the List Component tells you exactly what you need to know to use it:

  • The fully qualified Java class name:  org.onehippo.cms7.essentials.components.EssentialsListComponent.
  • The name of the render attribute holding the paginated list of product documents: pageable.

Products Overview Template

The List Component does not come with a standard template. You need to create it yourself.

Open the file products.html found in the web design.

Locate the element <div class="col-md-9 col-sm-9">. This contains the HTML markup for the Products Overview.

Create a file bootstrap-webfiles/src/main/resources/site/freemarker/gogreen/productslist.ftl in your project. This will be the Freemarker template that renders the Products Overview.

Add the following line to productslist.ftl:

<#include "../include/imports.ftl">

You will implement the actual template in a minute.

Open the Hippo Console in your browser and browse to the node /hst:hst/hst:configurations/gogreen/hst:templates. This is where the templates are configured.

Add a new node called productslist of type hst:template.

Add a property hst:renderpath to the productslist node and enter the value webfile:/freemarker/gogreen/productslist.ftl.

/hst:hst/hst:configurations/gogreen/hst:templates
    + productslist [hst:template]
        - hst:renderpath = webfile:/freemarker/gogreen/productslist.ftl

Browse to the node /hst:hst/hst:configurations/gogreen/hst:pages/productslist.

Add a new node called main of type hst:component.

Add a child node called left of type hst:component to  main.

Add a property hst:componentclassname to the  left node and enter the fully qualified name of the List Component's Java class: org.onehippo.cms7.essentials.components.EssentialsListComponent.

Add a property hst:template to the left node and enter the value productslist.

/hst:hst/hst:configurations/gogreen/hst:pages/productslist
    + main [hst:component]
        + left [hst:component]
            - hst:componentclassname = org.onehippo.cms7.essentials.components.EssentialsListComponent
            - hst:template = productslist

You have now configured the  left column of the two columns content area of the  productslist page to use the  List component for its business logic and the  productslist template to render the results.

Open the template productslist.ftl again.

Use Freemarker syntax to dynamically render the product documents as HTML that conforms to the web design. It will be very similar to the News Overview template you worked on in the previous sprint. Some hints:

  • The image variant used in the product overview is 'smallsquare'.
  • Products ratings are out of scope in this sprint, you can comment out the related markup ( <#-- -->).
  • You can include the same pagination template as you have used before.

You should end up with something like this:

<#include "../include/imports.ftl">
<#if pageable??>
  <div class="isotope" id="masonry-elements">
    <#list pageable.items as item>
      <@hst.link var="link" hippobean=item/>
      <div class="feature blog-masonry isotope-item">
        <#if item.images?? && item.images[0].smallsquare??>
          <@hst.link var="img" hippobean=item.images[0].smallsquare />
          <div class="feature-image img-overlay">
            <img src="${img}" alt="" />
            <div class="item-img-overlay">
              <div class="item_img_overlay_link">
                <a href="${link}" title="${item.title?html}"> </a>
              </div>
              <div class="item_img_overlay_content">
                <h3 class="thumb-label-item-title">
                  <a href="${link}">Add to cart</a>
                </h3>
              </div>
            </div>
          </div>
        </#if>
        <div class="feature-content">
          <h3 class="h3-body-title blog-title">
            <a href="${link}">${item.title?html}</a>
          </h3>
          <p>${item.introduction?html}</p>
        </div>
        <div class="feature-details">
          <img src="<@hst.webfile path="/images/icon-banknote.png"/>" class="icon">
          <span><@fmt.formatNumber value="${item.price}" type="currency" /></span> 
          <#--<div class="feature-share"><span data-score="3.5" class="product-rating"/></div>-->
        </div>
      </div>
    </#list>
  </div>
  <#if cparam.showPagination>
    <#include "../include/pagination.ftl">
  </#if>
</#if>

Head Contributions

The Products Overview page in the web design makes use of a number of scripts and style sheets. You will need to include them in the page.

Open the file  products.html again and locate the CSS and Javascript references that are specific to this page. They are marked with the following comments:

<!-- start page specific css -->

<!-- end page specific css -->

and:

<!-- start page specific js -->

<!-- end page specific js -->

Each of those references needs to be included in the  productpage-main.ftl template as a  head contribution. A head contribution allows any template to add HTML markup to the HTML  <head> element which is typically found in the top level component's template ( bootstrap-webfiles/src/main/resources/site/freemarker/gogreen/base-layout.ftl in your project).

It doesn't really matter where in the template you add the head contributions, but it makes sense to group them together at the top or the bottom.

Some hints:

  • Each  <@hst.headContribution> tag can only contain one child element, typically  <script> or  <link>. The child element can have any number of child elements.
  • You can specify different categories of head contributions through the  category attribute. Look at the top level component's template ( bootstrap-webfiles/src/main/resources/site/freemarker/gogreen/base-layout.ftl) to see which categories are expected.
  • You can look at  bootstrap-webfiles/src/main/resources/site/freemarker/hstdefault/essentials-carousel.ftl for inspiration.

You will end up with something like this:

<@hst.headContribution category="htmlHead">
  <link rel="stylesheet" href="<@hst.webfile path="/css/jquery.raty.css"/>" />
</@hst.headContribution>
<@hst.headContribution category="htmlBodyEnd">
  <script type="text/javascript" src="<@hst.webfile path="/js/jquery.raty.js"/>"></script>
</@hst.headContribution>
<@hst.headContribution category="htmlBodyEnd">
  <script type="text/javascript" src="<@hst.webfile path="/js/jquery.isotope.js"/>"></script>
</@hst.headContribution>
<@hst.headContribution category="htmlBodyEnd">
  <script type="text/javascript">
    jQuery(document).ready(function($) {
   
      $(window).resize(function() {
          $('#masonry-elements,.portfolio-items').isotope('reLayout');
      });
   
      var $cont = $('.portfolio-items');
   
      $cont.isotope({
          itemSelector: '.portfolio-items .thumb-label-item',
          masonry: {columnWidth: $('.isotope-item:first').width(), gutterWidth: 6},
          filter: '*',
          transformsEnabled: false,
          layoutMode: 'masonry'
      });
 
    });
 
    $(window).load(function() {
      var $masonryElement = $('#masonry-elements');
      $masonryElement.isotope({
        transformsEnabled: false,
        masonry: {
          columnWidth: 235,
          gutterWidth: 15
        }
      });
      $('#masonry-elements,.portfolio-items').isotope('reLayout');
    });  
 
    $('#masonry-elements .product-rating').raty({
        score: function() {
            return $(this).attr('data-score');
        },
        readOnly: true,
        half: true,
        starType :  'i'
    });
  </script>
</@hst.headContribution>

Point you browser to  http://localhost:8080/site/products. It will display the products you entered in the CMS.

Navigation

Now that the Products Overview page is fully functional, you can add a link to it to the navigation menu.

In the Hippo Console browse to the node /hst:hst/hst:configurations/gogreen/hst:workspace/hst:sitemenus/main.

Add a new node called Products of type  hst:sitemenuitem.

Add a property hst:referencesitemapitem to the Products node and enter the value products.

Use the 'Up' button in the top menu to move the Products node up so it is between News and About

Reload the site in your browser. You will see the Products item appear in the navigation menu and when you click on it the Products Overview page will load.

Next Step

Develop the Product Feature Part 3: Product Detail Page

Full Source Code

productslist.ftl

<#include "../include/imports.ftl">
<#if pageable??>
  <div class="isotope" id="masonry-elements">
    <#list pageable.items as item>
      <@hst.link var="link" hippobean=item/>
      <div class="feature blog-masonry isotope-item">
        <#if item.images?? && item.images[0].smallsquare??>
          <@hst.link var="img" hippobean=item.images[0].smallsquare />
          <div class="feature-image img-overlay">
            <img src="${img}" alt="" />
            <div class="item-img-overlay">
              <div class="item_img_overlay_link">
                <a href="${link}" title="${item.title?html}"> </a>
              </div>
              <div class="item_img_overlay_content">
                <h3 class="thumb-label-item-title">
                  <a href="${link}">Add to cart</a>
                </h3>
              </div>
            </div>
          </div>
        </#if>
        <div class="feature-content">
          <h3 class="h3-body-title blog-title">
            <a href="${link}">${item.title?html}</a>
          </h3>
          <p>${item.introduction?html}</p>
        </div>
        <div class="feature-details">
          <img src="<@hst.webfile path="/images/icon-banknote.png"/>" class="icon">
          <span><@fmt.formatNumber value="${item.price}" type="currency" /></span> 
          <#--<div class="feature-share"><span data-score="3.5" class="product-rating"/></div>-->
        </div>
      </div>
    </#list>
  </div>
  <#if cparam.showPagination>
    <#include "../include/pagination.ftl">
  </#if>
</#if>
 
<@hst.headContribution category="htmlHead">
  <link rel="stylesheet" href="<@hst.webfile path="/css/jquery.raty.css"/>" />
</@hst.headContribution>
<@hst.headContribution category="htmlBodyEnd">
  <script type="text/javascript" src="<@hst.webfile path="/js/jquery.raty.js"/>"></script>
</@hst.headContribution>
<@hst.headContribution category="htmlBodyEnd">
  <script type="text/javascript" src="<@hst.webfile path="/js/jquery.isotope.js"/>"></script>
</@hst.headContribution>
<@hst.headContribution category="htmlBodyEnd">
  <script type="text/javascript">
    jQuery(document).ready(function($) {
   
      $(window).resize(function() {
          $('#masonry-elements,.portfolio-items').isotope('reLayout');
      });
   
      var $cont = $('.portfolio-items');
   
      $cont.isotope({
          itemSelector: '.portfolio-items .thumb-label-item',
          masonry: {columnWidth: $('.isotope-item:first').width(), gutterWidth: 6},
          filter: '*',
          transformsEnabled: false,
          layoutMode: 'masonry'
      });
 
    });
 
    $(window).load(function() {
      var $masonryElement = $('#masonry-elements');
      $masonryElement.isotope({
        transformsEnabled: false,
        masonry: {
          columnWidth: 235,
          gutterWidth: 15
        }
      });
      $('#masonry-elements,.portfolio-items').isotope('reLayout');
    });  
 
    $('#masonry-elements .product-rating').raty({
        score: function() {
            return $(this).attr('data-score');
        },
        readOnly: true,
        half: true,
        starType :  'i'
    });
  </script>
</@hst.headContribution>
Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?