Article summary

Summary

Discusses how to create a custom HTL Tab component. This article uses Bootstrap and also describes how to use the JS API with HTL components. In this example, the JS logic parses a Multi-field component.   

A special thank you to Prince Shivhare for contributing code used in this community article. 

Also, thank you to Ratna Kumar Kotla for testing this article to ensure it works. 

Digital Marketing Solution(s) Adobe Experience Manager 6.4
Audience Developer
Required Skills HTML, JavaScript
Version 6.4

Note:

You can download an AEM package that contains the code used in this article. Download the package and deploy using package manager. The purpose of this code is to show the community these concepts in action. That is, it's to illustrate how to write a HTL Tab component that lets an author set various options. This community code is for teaching purposes only and not meant to go into production as is.

You can view the sample community application by using the following URL: http://localhost:4502/editor.html/content/aemtab64/en.html (assuming you deploy on author).

Download

Note:

To ensure that the Multifield works in the component dialog, also install the ACS-Commons for AEM located here: https://adobe-consulting-services.github.io/acs-aem-commons/. Otherwise, the Multi-field dialog will not store values. 

Introduction

You can create an Adobe Experience Manager 6.4 HTL component that displays data within a tab layout style. By developing a tab layout component, you can organize your data under tabs. That is, a web site visitor can click on a tab on the web page to see different data set. Tab components are located in web-sites, such as the following one that shows sports data. 

tab
An example of a tab component displaying sports data

The following illustration shows the component that is developmented in this article. Notice the following illustration displays an AEM component with four tab headings: Home Page, Career Section, Contact Us, and About US.

 

client
An Experience Manager Tab Layout component

An author can set different values such as Tab headings and Tab content by using the component dialog, as shown in this illustration. 

dialog
The Tab Component dialog

The following list describes the dialog fields:

  • Tab Id - specifies the unique identifier of the tab 
  • Tab Heading - specifies the text value located in the Tab heading 
  • Tab Description - specifies the text that appears when the user clicks the tab

The Banner component built in this article uses the Bootstrap library. For information, see Bootstrap.

Create an AEM Maven 13 archetype project

You can create an Experience Manager archetype project by using the Maven archetype plugin. In this example, assume that the working directory is C:\AdobeCQ.

maven
Files generated by Maven 13 Archetype

To create an Experience Manager archetype project, perform these steps:

1. Open the command prompt and go to your working directory (for example, C:\AdobeCQ).

2. Run the following Maven command:

mvn org.apache.maven.plugins:maven-archetype-plugin:2.4:generate -DarchetypeGroupId=com.adobe.granite.archetypes -DarchetypeArtifactId=aem-project-archetype -DarchetypeVersion=13 -DarchetypeCatalog=https://repo.adobe.com/nexus/content/groups/public/

3. When prompted, specify the following information:

  • groupId - aemtab64
  • artifactId - aemtab64
  • version - 1.0-SNAPSHOT
  • package - com.aem.community
  • appsFolderName - aemtab64
  • artifactName - aemtab64
  • componentGroupName - aemtab64
  • confFolderName - aemtab64
  • contentFolderName - aemtab64
  • cssId - aemtab64
  • packageGroup - aemtab64
  • siteName - aemtab64

4. WHen prompted, specify Y.

5. Once done, you will see a message like:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:42 min
[INFO] Finished at: 2016-04-25T14:34:19-04:00
[INFO] Final Memory: 16M/463M
[INFO] ------------------------------------------------------------------------

6. Change the working directory to aemtab64 using cd aemtab64, and then enter the following command.

mvn eclipse:eclipse

After you run this command, you can import the project into Eclipse as discussed in the next section.

Add Java files to the Maven project using Eclipse

To make it easier to work with the Maven generated project, import it into the Eclipse development environment, as shown in the following illustration.

project
Eclipse Import Project Dialog

Note:

Because this article uses the JS USE API, there is no need to create a Java class. The only file to modify is the core POM.xml file as discussed in the next section.

Modify the Maven POM file

Add the following POM dependency to the POM file located at C:\AdobeCQ\aemtab64.

<dependency>
    <groupId>com.adobe.aem</groupId>
    <artifactId>uber-jar</artifactId>
    <version>6.4.0</version>
    <classifier>apis</classifier>
    <scope>provided</scope>
</dependency>
               
  <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-atinject_1.0_spec</artifactId>
       <version>1.0</version>
       <scope>provided</scope>
   </dependency>

When you add new Java classes under core, you need to modify a POM file to successfully build the OSGi bundle. You modify the POM file located at C:\AdobeCQ\aemtab64\core.

The following code represents this POM file.

<?xml version="1.0" encoding="UTF-8"?>
<!--
 |  Copyright 2017 Adobe Systems Incorporated
 |
 |  Licensed under the Apache License, Version 2.0 (the "License");
 |  you may not use this file except in compliance with the License.
 |  You may obtain a copy of the License at
 |
 |      http://www.apache.org/licenses/LICENSE-2.0
 |
 |  Unless required by applicable law or agreed to in writing, software
 |  distributed under the License is distributed on an "AS IS" BASIS,
 |  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 |  See the License for the specific language governing permissions and
 |  limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>aemtab64</groupId>
        <artifactId>aemtab64</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <artifactId>aemtab64.core</artifactId>
    <packaging>bundle</packaging>
    <name>aemtab64 - Core</name>
    <description>Core bundle for aemtab64</description>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.sling</groupId>
                <artifactId>maven-sling-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <!-- Import any version of javax.inject, to allow running on multiple versions of AEM -->
                        <Import-Package>javax.inject;version=0.0.0,*</Import-Package>
                        <Sling-Model-Packages>
                            com.aem.tab.core
                        </Sling-Model-Packages>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <!-- OSGi Dependencies -->
        <dependency>
            <groupId>com.adobe.aem</groupId>
            <artifactId>uber-jar</artifactId>
            <classifier>apis</classifier>
        </dependency>
     
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-atinject_1.0_spec</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>osgi.core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>osgi.cmpn</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>osgi.annotation</artifactId>
        </dependency>
        <!-- Other Dependencies -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.jcr</groupId>
            <artifactId>jcr</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.adobe.aem</groupId>
            <artifactId>uber-jar</artifactId>
            <classifier>apis</classifier>
        </dependency>
        <dependency>
            <groupId>org.apache.sling</groupId>
            <artifactId>org.apache.sling.models.api</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
        </dependency>
        <dependency>
            <groupId>junit-addons</groupId>
            <artifactId>junit-addons</artifactId>
        </dependency>
    </dependencies>
</project>

Deploy the Project to Experience Manager

To build the OSGi bundle by using Maven, perform these steps:

  1. Open the command prompt and go to the C:\AdobeCQ\aemtab64.
  2. Run the following maven command: mvn -PautoInstallPackage install.
  3. The OSGi component can be found in the following folder: C:\AdobeCQ\aemtab64\core\target
  4. The file name of the OSGi component is aemtab64.core-1.0-SNAPSHOT.jar.

The command -PautoInstallPackage automatically deploys the OSGi bundle to AEM.

Create the Experience Manager Tab Component

To create the Tab component, perform these tasks using CRXDE Lite:

1. Right click on After you setup the AEM folder structure, create the AEM Touch UI component. Perform these tasks using CRXDE Lite:

1. Right click on /apps//aemtab64/components/content and then select New, Component.

2. Enter the following information into the Create Component dialog box:

  • Label: The name of the component to create. Enter TabComponent
  • Title: The title that is assigned to the component. Enter TabComponent.
  • Description: The description that is assigned to the component.
  • Super Resource Type: Enter foundation/components/parbase.
  • Group: The group in the side rail or side kick where the component appears. Enter aemtab64

3. Click Ok.

Change the file extention of  /apps/aemtab64/components/content/TabComponent/TabComponent.jsp to .HTML. Next, add the following code to TabComponent.html.

<div class="cq-placeholder section" data-emptytext="Tab Component"></div>

	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
	<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>


<div class="container">
	<h2>${properties.heading}</h2>
		
		<sly data-sly-use.convertToJSON="${'script.js'}">
			<ul class="nav nav-tabs">
				<sly data-sly-list.multival="${convertToJSON.proposerObjs}" data-sly-unwrap>
						<li><a data-toggle="tab" href="#${multival.tabId}">${multival.tabHeading}</a></li>
				</sly>
			</ul>
			<div class="tab-content">
				<sly data-sly-list.multival1="${convertToJSON.proposerObjs}" data-sly-unwrap>
					<div id="${multival1.tabId}" class="tab-pane fade">
						<h3>${multival1.tabname}</h3>
						${multival1.first}
						<p><div data-sly-resource="${'item{0}' @ format=multival1List.count, resourceType='wcm/foundation/components/parsys'}" data-sly-unwrap></div></p>
					</div>
				</sly>
			</div>
		</sly>
</div>

Note:

Notice that the bootstrap library is included using this line of code: 

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

In this example, notice:

<sly data-sly-use.convertToJSON="${'script.js'}">

data-sly-use.convertToJSON references the JavaScript located in script.js that is located at /apps/aemtab64/components/content/TabComponent/script.js. If you did not install the package, make sure you create this file and add the following code.

In this script file, notice this function.  

 

use(function () {

	var proposerMap = [];

	var tempProposerMap = properties.subscriptionmulti;

	try{
		if (typeof tempProposerMap === 'string' || tempProposerMap instanceof String){
			var item = tempProposerMap;
			 item = JSON.parse(item);
			if(item.hasOwnProperty("multivalues")){
                for(var cnt in item.multivalues) {
                    item.multivalues[cnt] = JSON.parse(item.multivalues[cnt]);
                }
            }
			proposerMap.push(item)
		}
	}catch(e){
		for(var i in tempProposerMap) {
			var item = tempProposerMap[i];
			 item = JSON.parse(item);
			if(item.hasOwnProperty("multivalues")){
                for(var cnt in item.multivalues) {
                    item.multivalues[cnt] = JSON.parse(item.multivalues[cnt]);
                }
            }
			proposerMap.push(item)
		}
	}

		return {
			proposerObjs : proposerMap

		};
	});

This is how you handle a multifield located in the component dialog by using JavaScript. Notice this line of code: 

var tempProposerMap = properties.subscriptionmulti;

This is referencing the node located here: 

/apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2/items/category/field

The rest of the code parses that values from the multifield and places them into an Array named proposerMap. 

Note:

For more information about the JS USE API, see the HTL Specification

Build the Component dialog

Use CRXDE Lite to develop the Tab component dialog so it resembles the following illustration. 

dialog
JCR Nodes that create a Touch UI dialog.

Note:

If you install the package at the start of this article, then you do not need to create the dialog by following this procedure. 

Build the Touch UI dialog by performing these tasks:

1. Select /apps/aemtab64/components/content/TabComponent.

2. Right click and select Create, Create Node.

3. Enter the following values:

  • Name: cq:dialog
  • Type: nt:unstructured

4. Add the following property to the cq:dialog node.

  • sling:resourceType (String) - cq/gui/components/authoring/dialog

5. Select /apps/aemtab64/components/content/TabComponent/cq:dialog.

6. Right click and select Create, Create Node. Enter the following values:

  • Name: content
  • Type: nt:unstructured

7. Add the following properties to the content node.

  • sling:resourceType (String) - granite/ui/components/foundation/container

8. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content.

9. Right click and select Create, Create Node. Enter the following values:

  • Name: layout
  • Type: nt:unstructured

10. Add the following properties to the layout node.

  • sling:resourceType (String) - granite/ui/components/foundation/layouts/fixedcolumns

11. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content.

12. Right click and select Create, Create Node. Enter the following values:

  • Name: items
  • Type: nt:unstructured

13. Click on the following node:/apps/aemtab64/components/content/TabComponent/cq:dialog/content/items.

14. Right click and select Create, Create Node.

15. Enter the following values:

  • Name: column
  • Type: nt:unstructured

16. Add the following property to the currentissue node.

  • sling:resourceType (String) - granite/ui/components/foundation/container

17. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column.

18. Right click and select Create, Create Node.

19. Enter the following values:

  • Name: items
  • Type: nt:unstructured

20. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items.

21. Right click and select Create, Create Node.

22. Enter the following values:

  • Name: fieldset
  • Type: nt:unstructured

23. Add the following propery to this node: 

  • sling:resourceType (String) - granite/ui/components/foundation/form/fieldset

24. Click on the following node: /apps/aemtab64/components/content/Tabomponent/cq:dialog/content/items/column/items/fieldset.

25. Right click and select Create, Create Node.

  • Name: layout
  • Type: nt:unstructured

26. Add the following property to this node.

sling:resourceType (String) - granite/ui/components/foundation/layouts/fixedcolumns


27. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset.

28. Right click and select Create, Create Node.

29. Enter the following values:

  • Name: items
    Type: nt:unstructured

30. Click on the following node:/apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items.

31. Right click and select Create, Create Node.

32. Enter the following values:

  • Name: column
  • Type: nt:unstructured

33. Add the following property to this node.

  • sling:resourceType (String) - granite/ui/components/foundation/container


34. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column.

35. Right click and select Create, Create Node.

36. Enter the following values:

  • Name: items
  • Type: nt:unstructured

37. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items.

38. Right click and select Create, Create Node.

39. Enter the following values:

  • Name: heading
  • Type: nt:unstructured

40. Add the following properties to this node.

  • fieldDescription(String) - Tab Heading
  • fieldLabel (String) - Heading
  • sling:resourceType (String) - granite/ui/components/foundation/form/textfield
  • name (String) - ./heading

41. Click on the following node: /apps/aemtab64/components/content/bannerComponent/cq:dialog/content/items/column/items/fieldset/items/column/items.

42. Right click and select Create, Create Node.

43. Enter the following values:

  • Name: well 2
  • Type: nt:unstructured

44. Add the following properties to this node.

  • sling:resourceType (String) - granite/ui/components/foundation/container

45. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2.

46. Right click and select Create, Create Node.

47. Enter the following values:

  • Name: layout
  • Type: nt:unstructured

48. Add the following properties to this node.

  • sling:resourceType (String) - granite/ui/components/foundation/layouts/well

49. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2.

50. Right click and select Create, Create Node.

51. Enter the following values:

  • Name: items
  • Type: nt:unstructured

53. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2/items/.

54. Right click and select Create, Create Node.

55. Enter the following values:

  • Name: category
  • Type: nt:unstructured

56. Add the following properties to this node (this node represents the multifield).

  • class (String) - full-width
  • fieldLabel (String) - Tab Information
  • sling:resourceType (String) - granite/ui/components/foundation/form/multifield

57. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2/items/category.

58. Right click and select Create, Create Node.

59. Enter the following values:

  • Name: field
  • Type:  nt:unstructured

60. Add the following properties to this node.

  • acs-commons-nested (String) - "" (empty string)
  • name (String) - ./subscriptionmulti (this value is referenced in the JS Script)
  • sling:resourceType (String) - granite/ui/components/foundation/form/fieldset

61. Click on the following node: /apps/sightlymf/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2/items/category/field.

62. Right click and select Create, Create Node.

63. Enter the following values:

  • Name: layout
  • Type: nt:unstructured

64. Add the following properties to this node.

  • method (String) - absolute
  • sling:resourceType (String) - granite/ui/components/foundation/layouts/fixedcolumns

65. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2/items/category/field.

66. Right click and select Create, Create Node.

67. Enter the following values:

  • Name: items
  • Type: nt:unstructured

68. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2/items/category/field/items.

69. Right click and select Create, Create Node.

70. Enter the following values:

  • Name: column
  • Type: nt:unstructured

71. Add the following properties to this node.

  • sling:resourceType (String) - granite/ui/components/foundation/container

72. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2/items/category/field/items/column.

73. Right click and select Create, Create Node.

74. Enter the following values:

  • Name: items
  • Type: nt:unstructured

75. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2/items/category/field/items/column/items.

76. Right click and select Create, Create Node.

77. Enter the following values:

  • Name: desc
  • Type: nt:unstructured

78. Add the following properties to this node.

  • fieldLabel (String) - Tab Id
  • name (String) - ./tabId
  • sling:resourceType (String) - granite/ui/components/foundation/form/textfield

79. Click on the following node: /apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2/items/category/field/items/column/items.

80. Right click and select Create, Create Node.

81. Enter the following values:

  • Name: tabhead
  • Type: nt:unstructured

82. Add the following properties to this node.

  • fieldLabel (String) - Tab Heading
  • name (String) - ./tabHeading
  • sling:resourceType (String) - granite/ui/components/foundation/form/textfield

83. Click on the following node:
/apps/aemtab64/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2/items/category/field/items/column/items.

84. Right click and select Create, Create Node.

85. Enter the following values:

  • Name: tabname
  • Type: nt:unstructured

86. Add the following properties to this node.

  • fieldLabel (String) - Tab Description
  • name (String) - ./tabname
  • sling:resourceType (String) - granite/ui/components/foundation/form/textfield

Note:

To use acs commons multifield, it is mandatory to add acs-commons-nested=”” property to a fieldset within a multifield node like at ./subscriptionmulti node in our example (/apps/sightlymf/components/content/TabComponent/cq:dialog/content/items/column/items/fieldset/items/column/items/well 2/items/category/field).

The value acs-commons-nested=”JSON_STORE” means we want to store multifiled data as JSON Array. To store multi filed data as seperate child node use acs-commons-nested=”NODE_STORE”.

Read more at AEMCQ5Tutorials: Create TouchUI Multifield Component using HTL.

View the Tab Component within an Experience Manager page

To access the component, enter the following URL:

http://localhost:4502/editor.html/content/sightlymf.html (assuming your installed the package)

The following illustration shows the HTL component.

client
The Tab component displayed in an AEM Web page

If you drag the Banner component onto a new page, you will see this before you set the values in the component dialog. 

dandd
The Tab component without the dialog values set

Click on the component to access the Touch UI dialog. Enter the following values:

dialog1
An AEM 6.3 Touch UI component dialog

After you fill in the dialog values and click the checkmark icon, you will see the component render on the AEM web page. 

See also

Join the AEM community at: Adobe Experience Manager Community

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License  Twitter™ and Facebook posts are not covered under the terms of Creative Commons.

Legal Notices   |   Online Privacy Policy