You must define the storefront assets for a layout template before your custom
template can be used on the storefront. The storefront components for the template define the
container for the widget and the configurable slots within the container. The storefront components
consist of the definition of the template container and placement of the template slots, the
environment setup file.
For more information about the overall creation of a layout
template, see Creating Commerce Composer layout templates.
Procedure
Create the storefront assets for your custom layout
template.
-
In the Enterprise Explorer view within HCL Commerce Developer, create a directory to
contain the code assets for your custom layout templates.
-
Go to the
WCDE_installdir\workspace\crs-web\WebContent\Aurora
directory.
-
If the directory does not exist within the Aurora directory, create the
Container-vendor directory. Where vendor is the name
of your company.
For example, the name for your directory can be
Container-MyCompany
-
Create the container JSP file for your custom layout template. The container JSP file defines
the template, which is an empty widget that is divided into configurable slots. The container
definition is similar to the definition of a widget, but does not have any functionality or content
that is defined for use on the storefront. The container identifies the configurable slots where a
widget can be added and marks these slots with the
wcpgl:widgetImport
tag. Each
slot is defined by an internal slot ID, which must be unique within the container. The container JSP
file also identifies the environment setup file.
-
Go to the
WCDE_installdir\workspace\crs-web\WebContent\Aurora\Container
directory.
-
Copy the StaticContentPageDisplayContainer.jsp file. This file is the
container JSP file for the Any page, single slot layout template that is
provided by default with HCL Commerce.
-
Go to your
WCDE_installdir\workspace\crs-web\WebContent\Aurora\Container-vendor
directory and add the copied JSP file into this directory.
-
Rename the JSP file and open the file for editing.
For example, you can rename the file
MyCustomLayoutTemplate.jsp.
By
default, the contents of the container JSP file resembles the following
code.
1<%@include file="../Common/EnvironmentSetup.jspf" %>
<%@taglib uri="http://commerce.ibm.com/pagelayout" prefix="wcpgl"%>
2<div class="rowContainer" id="container_${pageDesign.layoutID}">
3<div class="row">
4<div class="col12" data-slot-id="1"><wcpgl:widgetImport slotId="1"/></div>
</div>
</div>
Where
- 1 The environment setup file. This configuration file is used at run time. This file
retrieves and prepares the JSP file path and resource bundle for the container. The configuration
file is used to identifies the values of the environment configurations for the Commerce Composer
framework. The file identifies the values for any prefix variables, common context file paths, page
encoding setting, and the page to display as an error page. This file is called by many JSP files in
a store and is shared by multiple containers. By default, the
WCDE_installdir\workspace\crs-web\WebContent/Aurora/Common/EnvironmentSetup.jspf
file is specified.
- 2 Container division element. This element is the beginning of the
code that defines the overall layout template container. Include the definitions for all rows and
slots within this
rowContainer
element. For the id
attribute, the
value for all layout templates is "container_${pageDesign.layoutID}"
. The value for
the ID is generated when the layout template is used to create a layout. The value of the new layout
ID is retrieved as the ${pageDesign.layoutID}
portion of the value for this
id
attribute. For the class
attribute, include
"rowContainer"
as part of the attribute value to help you identify the division
within the corresponding CSS file. For example, you can rename the class value to be
"MyCustomTemplate_tab_content rowContainer"
. The value for this attribute must map
to an entry within a corresponding CSS file to define the rendering of the template on the
storefront.
- 3 The row division element. Include more
<div
class="row">
elements to design your layout template to include multiple rows of slots. The
value for the class
attribute must match an entry within a corresponding CSS
file.
- 4 The column division element. This element is used to define a slot within a row. By
default the Any page, single slot layout template includes a single slot
within one row. To define the slot, you must define the
class
and
data-slot-id
attributes.The class
attribute identifies the CSS
entry that defines display information for the template slot. The values for the
class
attribute in the following steps specify CSS entries that are defined by
default for template columns. The CSS file that includes the CSS definitions for layout templates is
defined in the
WCDE_installdir\workspace\crs-web\WebContent\Aurora\css\base.css
file. By default, the CSS for a layout template uses a 12-column fluid grid system that divides a
page into rows and columns. The class value "col12"
entry, defines a slot to have a
width that equals 100% of the page width, or as wide as all 12 columns.
The
data-slot-id
attribute defines the
slotId
for the slot within your
layout template. This ID displays in
Management Center for the template slot wireframe to help
users identify and select the slots within a template to include widgets within a layout. The value
for the
data-slot-id
must use the following
format
data-slot-id="1"><wcpgl:widgetImport slotId="1"/>
Where the specified
numerical value is the ID of the slot that the column division element is defining. For each
additional slot that you include within a template, it is recommended that you increment the slot ID
value in sequential order.
For example, the following code snippet defines the first three slots
within a template. All of the slots are included within a single
row.<div class="row">
<div class="col4 acol12" data-slot-id="1"><wcpgl:widgetImport slotId="1"/></div>
<div class="col4 acol12" data-slot-id="2"><wcpgl:widgetImport slotId="2"/></div>
<div class="col4 acol12" data-slot-id="3"><wcpgl:widgetImport slotId="3"/></div>
</div>
The
class value "col4 acol12"
entry, maps to the col4
and
acol12
entries in the base.css file. The col4
entry defines a slot width to be equal to a third of the total page width when the page is viewed in
a browser with a page range greater than 600 pixels. The acol12
entry defines the
slot width to be equal to 100% of the page width when the page is viewed at a page range fewer than
600 pixels. When this fewer 600 pixel breakpoint is encountered, the slots within the layout
template rearrange on the storefront to stack vertically so that the slots can show at the defined
width. This behavior ensures that the layout template is responsive. For more information about the
CSS framework for the fluid grid system that makes up a layout template, see Responsive Web Design (RWD) framework.
-
Update the container JSP file for your layout to divide you layout into more rows and
columns.
To divide your template into more rows, include more row division elements. The following code
snippet divides the
MyCustomLayoutTemplate.jsp container JSP into three rows.
The first row is divided into two columns and, the second row into four columns. The third row
includes a single
column.
<%@include file="../Common/EnvironmentSetup.jspf" %>
<%@taglib uri="http://commerce.ibm.com/pagelayout" prefix="wcpgl"%>
<div class="rowContainer" id="container_${pageDesign.layoutID}">
<div class="row">
<div class="col6 acol12" data-slot-id="1"><wcpgl:widgetImport slotId="1"/></div>
<div class="col6 acol12" data-slot-id="2"><wcpgl:widgetImport slotId="2"/></div>
</div>
<div class="row">
<div class="col3 acol12" data-slot-id="3"><wcpgl:widgetImport slotId="3"/></div>
<div class="col3 acol12" data-slot-id="4"><wcpgl:widgetImport slotId="4"/></div>
<div class="col3 acol12" data-slot-id="5"><wcpgl:widgetImport slotId="5"/></div>
<div class="col3 acol12" data-slot-id="6"><wcpgl:widgetImport slotId="6"/></div>
</div>
<div class="row">
<div class="col12" data-slot-id="7"><wcpgl:widgetImport slotId="7"/></div>
</div>
</div>
-
Update the container JSP to include any tabbed slots that you want within your layout
template.
-
To include tabbed slots within your template, you must include the
ProductTab.js JavaScript file within your container JSP file.
This file defines the interactions that a shopper can perform to switch between tabs and
defines how the tabbed slots respond. To include this file, add the following code after the include
statements for the environment setup file and the tag
library
<script type="text/javascript" src="${jsAssetsDir}javascript/Widgets/ProductTab/ProductTab.js"></script>
-
Update the definition for the row that you want to include tabbed slots to include the logic to
identify the number of tabbed slots and to retrieve the slot ID values for the slots.
Within the row definition, replace the division elements for any columns that you want to be
tabbed slots with the following code. The following code defines the number of tabbed slots and the
logic that is used to retrieve the ID values for the tabbed slots.
<wcf:useBean var="tabSlotIds" classname="java.util.ArrayList"/>
<%-- Double loop to get the slots into the array list in proper order. The service does not return the child widgets in any predictable order. --%>
<c:set var="tabSlotCount" value="0"/>
1 <c:forEach var="slotNumber" begin="3" end="6">
<c:set var="foundCurrentSlot" value="false"/>
<c:forEach var="childWidget" items="${pageDesign.widget.childWidget}">
<c:if test="${childWidget.slot.internalSlotId == slotNumber && !foundCurrentSlot}">
<wcf:set target="${tabSlotIds}" value="${slotNumber}" />
<c:set var="foundCurrentSlot" value="true"/>
<c:set var="tabSlotCount" value="${tabSlotCount+1}"/>
</c:if>
</c:forEach>
</c:forEach>
Where
- 1 Defines the beginning and end of the tabbed slots to set the
number of tabbed slots. The preceding code snippet defines the row of tabbed slots to include the
slots with the slot ID values
3
, 4
, 5
, and
6
.
Add the code to define the titles for the tabbed slots. Append the following code after the
code for defining the number of tabbed
slots.
<div class="tabButtonContainer" role="tablist">
<div class="tab_header tab_header_double">
<c:forEach var="tabSlotId" items="${tabSlotIds}" varStatus="status">
1<c:set var="tabSlotName" value="Title${tabSlotId}"/>
2<c:forEach var="childWidget" items="${pageDesign.widget.childWidget}">
<c:if test="${childWidget.slot.internalSlotId == tabSlotName}">
<c:set var="tabWidgetDefIdentifier" value="${childWidget.widgetDefinitionIdentifier.uniqueID}"/>
<c:set var="tabWidgetIdentifier" value="${childWidget.widgetIdentifier.uniqueID}"/>
</c:if>
</c:forEach>
3<c:choose>
<c:when test="${status.first}">
<c:set var="tabClass" value="tab_container active_tab" />
<c:set var="tabIndex" value="0" />
</c:when>
<c:otherwise>
<c:set var="tabClass" value="tab_container inactive_tab" />
<c:set var="tabIndex" value="-1" />
</c:otherwise>
</c:choose>
<c:set var="tabNumber" value="${status.index+1}" scope="request"/>
4<div id="tab${status.count}" tabindex="${tabIndex}" class="tab_container ${tabClass}"
aria-labelledby="contentRecommendationWidget_${tabSlotName}_${tabWidgetDefIdentifier}_${tabWidgetIdentifier}" aria-controls="tab${status.count}Widget"
onfocus="ProductTabJS.focusTab('tab${status.count}');" onblur="ProductTabJS.blurTab('tab${status.count}');"
role="tab" aria-setsize="${tabSlotCount}" aria-posinset="${status.count}" aria-selected="${status.first == true ? 'true' : 'false'}"
onclick="ProductTabJS.selectTab('tab${status.count}');"
onkeydown="ProductTabJS.selectTabWithKeyboard('${status.count}','${tabSlotCount}', event);">
5<wcpgl:widgetImport slotId="${tabSlotName}"/>
</div>
<c:remove var="tabNumber"/>
</c:forEach>
</div>
</div>
Where
- 1 Sets the title for the tab slot as the
tabSlotName
- 2 The code also defines the logic for returning the widgets within
a tabbed slot in the correct order.
- 3 Sets which tab to display when a shopper initially views a page
that uses this layout template
- 4 Calls the JavaScript functions defined within the
ProductTab.js file. These functions define how the page layout changes between
tabs.
- 5 The code to import the Text Editor widget to use as the value for
the
tabSlotName
that displays in the storefront as the title for the tabbed
slot.
Add the code to define the retrieval of content for display in the tabbed slots. Append
the following code after the code to define the tabbed slot
titles.
<c:forEach var="tabSlotId" items="${tabSlotIds}" varStatus="status">
<c:set var="tabStyle" value=""/>
<c:if test="${!status.first}">
<c:set var="tabStyle" value="style='display:none'" />
</c:if>
<div role="tabpanel" class="tab left" data-slot-id="${tabSlotId}" id="tab${status.count}Widget" aria-labelledby="tab${status.count}" ${tabStyle}>
<div class="content">
<wcpgl:widgetImport slotId="${tabSlotId}"/>
</div>
</div>
<c:remove var="tabStyle"/>
</c:forEach>
-
Save your container JSP file.
Your updated JSP file can resemble the following code, which defines the
Any page,
seven slots, tabs layout template.
<%@include file="../Common/EnvironmentSetup.jspf" %>
<%@taglib uri="http://commerce.ibm.com/pagelayout" prefix="wcpgl"%>
<script type="text/javascript" src="${jsAssetsDir}javascript/Widgets/ProductTab/ProductTab.js"></script>
<div class="rowContainer" id="container_${pageDesign.layoutID}">
<div class="row">
<div class="col6 acol12" data-slot-id="1"><wcpgl:widgetImport slotId="1"/></div>
<div class="col6 acol12" data-slot-id="2"><wcpgl:widgetImport slotId="2"/></div>
</div>
<div class="row margin-true">
<div class="col12 acol12 ccol12 right">
<wcf:useBean var="tabSlotIds" classname="java.util.ArrayList"/>
<%-- Double loop to get the slots into the array list in proper order. The service does not return the child widgets in any predictable order. --%>
<c:set var="tabSlotCount" value="0"/>
<c:forEach var="slotNumber" begin="3" end="6">
<c:set var="foundCurrentSlot" value="false"/>
<c:forEach var="childWidget" items="${pageDesign.widget.childWidget}">
<c:if test="${childWidget.slot.internalSlotId == slotNumber && !foundCurrentSlot}">
<wcf:set target="${tabSlotIds}" value="${slotNumber}" />
<c:set var="foundCurrentSlot" value="true"/>
<c:set var="tabSlotCount" value="${tabSlotCount+1}"/>
</c:if>
</c:forEach>
</c:forEach>
<div class="tabButtonContainer" role="tablist">
<div class="tab_header tab_header_double">
<c:forEach var="tabSlotId" items="${tabSlotIds}" varStatus="status">
<c:set var="tabSlotName" value="Title${tabSlotId}"/>
<c:forEach var="childWidget" items="${pageDesign.widget.childWidget}">
<c:if test="${childWidget.slot.internalSlotId == tabSlotName}">
<c:set var="tabWidgetDefIdentifier" value="${childWidget.widgetDefinitionIdentifier.uniqueID}"/>
<c:set var="tabWidgetIdentifier" value="${childWidget.widgetIdentifier.uniqueID}"/>
</c:if>
</c:forEach>
<c:choose>
<c:when test="${status.first}">
<c:set var="tabClass" value="tab_container active_tab" />
<c:set var="tabIndex" value="0" />
</c:when>
<c:otherwise>
<c:set var="tabClass" value="tab_container inactive_tab" />
<c:set var="tabIndex" value="-1" />
</c:otherwise>
</c:choose>
<c:set var="tabNumber" value="${status.index+1}" scope="request"/>
<div id="tab${status.count}" tabindex="${tabIndex}" class="tab_container ${tabClass}"
aria-labelledby="contentRecommendationWidget_${tabSlotName}_${tabWidgetDefIdentifier}_${tabWidgetIdentifier}" aria-controls="tab${status.count}Widget"
onfocus="ProductTabJS.focusTab('tab${status.count}');" onblur="ProductTabJS.blurTab('tab${status.count}');"
role="tab" aria-setsize="${tabSlotCount}" aria-posinset="${status.count}" aria-selected="${status.first == true ? 'true' : 'false'}"
onclick="ProductTabJS.selectTab('tab${status.count}');"
onkeydown="ProductTabJS.selectTabWithKeyboard('${status.count}','${tabSlotCount}', event);">
<wcpgl:widgetImport slotId="${tabSlotName}"/>
</div>
<c:remove var="tabNumber"/>
</c:forEach>
</div>
</div>
<c:forEach var="tabSlotId" items="${tabSlotIds}" varStatus="status">
<c:set var="tabStyle" value=""/>
<c:if test="${!status.first}">
<c:set var="tabStyle" value="style='display:none'" />
</c:if>
<div role="tabpanel" class="tab left" data-slot-id="${tabSlotId}" id="tab${status.count}Widget" aria-labelledby="tab${status.count}" ${tabStyle}>
<div class="content">
<wcpgl:widgetImport slotId="${tabSlotId}"/>
</div>
</div>
<c:remove var="tabStyle"/>
</c:forEach>
</div>
</div>
<div class="row">
<div class="col12" data-slot-id="7"><wcpgl:widgetImport slotId="7"/></div>
</div>
</div>
- Optional:
Define the CSS to render your custom layout template in the storefront.
-
Go to the
WCDE_installdir\workspace\crs-web\WebContent\Aurora\css
directory.
-
Open the base.css file for review. This file defines the CSS for all
layout templates that are provided by HCL Commerce.
-
Search for the comment
/*** Grid system ***/
within the file. The CSS in the
section defines the display for the layout template fluid grid system. By default, this CSS file
defines various display options. If the base.css does not include the
definition that you require, create a base-vendor.css file
to include your custom CSS definition. Create this file within the existing css
directory. Ensure that the values for the attributes areclass
unique and map to
your new CSS file.
What to do next
With the definition of the storefront assets for your custom layout template complete, you
must register your layout template by using the Data Load utility.