Article summary

Summary

Discusses how to create a custom AEM component that uses the JSP Standard Tag Library (JSTL). In addition, discusses how to use a multifield data type that uses a custom xtype and how to read back the values using JSTL.

Also, talks about using the following AEM widget data types:

  • CQ.Ext.form.TextField
  • CQ.form.PathField
  • CQ.Ext.form.ComboBox
  • CQ.Ext.form.TextArea

A special thank you to Praveen Dubey a member of the AEM community for contributing AEM code that is used in this article. 

Digital Marketing Solution(s) Adobe Experience Manager (Adobe CQ)
Audience
Developer (intermediate)
Required Skills
Java, Maven, HTML
Tested On Adobe Experience Manager 5.6
 

Note:

You can download an AEM package that contains code and the OSGi bundle that are 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 an AEM component that uses the JSP Standard Tag Library. This community code is for teaching purposes only and not meant to go into production as is.

You can view the application by using the following URL: http://localhost:4502/cf#/content/DisplayJSPTag.html (assuming you deploy on author). 

Download

Introduction

You can create Adobe Experience Manager (AEM) components that use the JSP Standard Tag Library (JSTL). This library lets you integrate  core functionality common to applications. For example, using JSTL, you can perform tasks such as iterating through collections, parsing XML documents, performing SQL operations, and so on. For more information, see JSP - Standard Tag Library.  

This development article walks you through how to build an AEM component that uses JSTL to display information, including an image from the AEM DAM. The following illustration shows the output of an AEM component that uses JSTL.  

AEMComponent

Note:

If you deploy the package that is shown at the start of this artilce, you can skip these steps and read the article to understand the concepts. 

Create an Experience Manager application folder structure 

Create an Experience Manager application folder structure that contains templates, components, and pages by using CRXDE Lite. 

CQAppSetup

The following describes each application folder:

  • application name: contains all of the resources that an application uses. The resources can be templates, pages, components, and so on. 
  • components: contains components that your application uses. 
  • page: contains page components. A page component is a script such as a JSP file.
    global: contains global components that your application uses.
  • template: contains templates on which you base page components. 
  • src: contains source code that comprises an OSGi component (this development article does not create an OSGi bundle using this folder). 
  • install: contains a compiled OSGi bundles container.

To create an application folder structure:

  1. To view the CQ welcome page, enter the URL http://[host name]:[port] into a web browser. For example, http://localhost:4502.
  2. Select CRXDE Lite.
  3. Right-click the apps folder (or the parent folder), select Create, Create Folder.
  4. Enter the folder name into the Create Folder dialog box. Enter customJSPtag
  5. Repeat steps 1-4 for each folder specified in the previous illustration. 
  6. Click the Save All button.

 

Note:

You have to click the Save All button when working in CRXDELite for the changes to be made.

Create a template 

You can create a template by using CRXDE Lite. A CQ template enables you to define a consistent style for the pages in your application. A template comprises of nodes that specify the page structure. For more information about templates, see Templates.

To create a template, perform these tasks:

1. To view the CQ welcome page, enter the URL http://[host name]:[port] into a web browser. For example, http://localhost:4502.

2. Select CRXDE Lite.

3. Right-click the template folder (within your application), select Create, Create Template.

4. Enter the following information into the Create Template dialog box:

  • Label: The name of the template to create. Enter templateJSPDev
  • Title: The title that is assigned to the template.
  • Description: The description that is assigned to the template.
  • Resource Type: The component's path that is assigned to the template and copied to implementing pages. Enter /apps/customJSPtag/components/page/templateJSPDev.
  • Ranking: The order (ascending) in which this template will appear in relation to other templates. Setting this value to 1 ensures that the template appears first in the list.

5. Add a path to Allowed Paths. Click on the plus sign and enter the following value: /content(/.*)?.

6. Click Next for Allowed Parents.

 

Create a render component that uses the template

Components are re-usable modules that implement specific application logic to render the content of your web site. You can think of a component as a collection of scripts (for example, JSPs, Java servlets, and so on) that completely realize a specific function. In order to realize this functionality, it is your responsibility as a CQ developer to create scripts that perform specific functionality. For more information about components, see Components.

By default, a component has at least one default script, identical to the name of the component. To create a render component, perform these tasks:

1. To view the CQ welcome page, enter the URL http://[host name]:[port] into a web browser. For example, http://localhost:4502.

2. Select CRXDE Lite.

3. Right-click /apps/customJSPtag/components/page, then select
Create, Create Component.

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

  • Label: The name of the component to create. Enter templateJSPDev
  • Title: The title that is assigned to the component.
  • Description: The description that is assigned to the template.
  • Super Type:foundation/components/page. 

5. Select Next for Advanced Component Settings and Allowed Parents.

6. Select OK on Allowed Children.

7. Open the slingRTemplate.jsp located at: /apps/customJSPtag/components/page/templateJSPDev/templateJSPDev.jsp.

8. Enter the following JSP code.

<html>
<%@include file="/libs/foundation/global.jsp" %>
<cq:include script="/libs/wcm/core/components/init/init.jsp"/>
<body>
<h1>Here is where your JSP Taq component will go</h1>
<cq:include path="par" resourceType="foundation/components/parsys" />
</body>
</html>

Create the component that uses JSTL

After you setup the AEM folder structure, create the AEM component that uses JSTL. Perform these tasks using CRXDE Lite:

1. Right click on /apps/customJSPtag/components 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 developers.
  • Title: The title that is assigned to the component. Enter developers.
  • Description: The description that is assigned to the template. Enter developers.
  • Super Resource Type: Enter foundation/components/parbase.
  • Group: The group in the side rail or side kick where the component appears. Enter Adobe. (The developers component is located under the Adobe heading in the Touch UI side rail. Also appears in Adobe in the classic view sidekick.)
  • Allowed parents: Enter */*parsys.

3. Click Ok.

Add a dialog to the AEM component  

A dialog lets an author click on the component in the Touch UI (or Classic UI) view during design time and enter values that are used by the component. The component created in this development article lets the AEM author enter text values, which are then displayed in the AEM web page. (See the illustration shown at the beginning of this development article.)

The component created in this article uses a multifield widget, as shown in the following illustration. 

Dialog

This dialog also contains a custom xtype that defines the widgets in each multifield pane. A custom xtype lets you programmatically define AEM widgets and how to define its behavior. You can define custom application logic in event handlers. For more information about creating custom xtypes, see Creating your first Adobe Experience Manager custom xtype

The following illustration shows the JCR nodes that represent the dialog created in this section.

dialogNodes

To create the dialog, perform these tasks:

1.  Select /apps/customJSPtag/components and select Create, Create Dialog.

2.  In the Title field, enter Developers.

3. Click Ok.

4.  Delete all nodes under /apps/customJSPtag/components/developers/dialog.

Create the Project tab

Create the first tab in the dialog titled for the developer component. This dialog contains a text field control and a text area control.

ProjectTab

1. Click on the following node: /apps/customJSPtag/components/developers/dialog.
2. Right click and select Create, Create Node
3. Enter the following values:
  • Name: items
  • Type: cq:Widget
4. Add the following property:
  • xtype (String) - tabpanel

5. Select the /apps/customJSPtag/components/developers/dialog/items node.

6. Right click and select Create, Create Node.

7. Enter the following values:

  • Name: items
  • Type: cq:WidgetCollection

8. Select the Select the /apps/customJSPtag/components/developers/dialog/items/items node.

9. Right click and select Create, Create Node.
10. Enter the following values:
  • Name: project
  • Type: cq:Panel
11. Add the following property:
  • title (String) - Project (defines the value shown in the tab)
12. Select the Select the /apps/customJSPtag/components/developers/dialog/items/items/project node.
 
13. Right click and select Create, Create Node.
14. Enter the following values:
  • Name: items
  • Type: cq:WidgetCollection

15. Select the /apps/customJSPtag/components/developers/dialog/items/items/project/items/ node.

16. Right click and select Create, Create Node.

17. Enter the following values:

  • Name: name
  • Type: cq:Widget
18. Add the following properties:
  • fieldLabel (String) - Project Name
  • name (String) - ./projectName
  • xtype (String) - textfield

19 . Select the /apps/customJSPtag/components/developers/dialog/items/items/project/items/ node.

20. Right click and select Create, Create Node.

21. Enter the following values:
  • Name: description
  • Type: cq:Widget

22. Add the following properties:

  • fieldDescription (String) - Provide the short description about the project
  • fieldLabel (String) - Description
  • name (String) - ./summary
  • xtype (String) - textarea

Create the Developer List tab

The following illustration shows the Developer List tab. 

Dialog

Create the Developer List tab by performing these tasks:
 
1. Click on the following node: /apps/customJSPtag/components/developers/dialog/items/items.
2. Right click and select Create, Create Node
3. Enter the following values:
  • Name: devlist
  • Type: cq:Widget
4. Add the following properties:
  • title (String) - Developers List (defines the value shown in the tab)
  • xtype (String) - panel
5. Select the Select the /apps/customJSPtag/components/developers/dialog/items/items/devlist node.
 
6. Right click and select Create, Create Node.
 
7. Enter the following values:
  • Name: items
  • Type: cq:WidgetCollection

8. Select the /apps/customJSPtag/components/developers/dialog/items/items/devlist/items node.

9. Right click and select Create, Create Node.

10. Enter the following values:

  • Name: devlist
  • Type: cq:Widget
11. Add the following properties:
  • devlist (String) - Click the '+' to add a new developer
  • fieldLabel (String) - Developers
  • name (String) - ./devlist
  • xtype (String) - multifield

12 . Select the /apps/customJSPtag/components/developers/dialog/items/items/devlist/items/devlist node.

13. Right click and select Create, Create Node.

14. Enter the following values:
  • Name: fieldConfig
  • Type: cq:Widget

15. Add the following property:

  • xtype (String) - developerlist

Setup a CQ:ClientLibraryFolder node

Create a cq:ClientLibraryFolder node to store JS files that define the custom xtype. The following illustration shows the cq:ClientLibraryFolder node.

clientlibs

After you create the clientlibs node, add the following property to it.

  • categories (String[]) - demo.widget.developers
The dependencies property informs CQ to include the JS files in the JSP that comprises the component. The categories property informs CQ which clientlibs must be included.
You have to a file to the clientlibs folder. The text file maps the JS files and is named js.txt. The following lists the content of the js.txt file.
 
#base=.
jquery.js
developers.js

 
To add the files to the ClientLibs folder, perform these tasks:
  1. Right-click /apps/xtype/components then select New, Node.
  2. Make sure that the node type is cq:ClientLibraryFolder and name the node clientlibs.
  3. Right click on clientlibs and select Properties. Add the property specified in this section.
  4. Add a JS file named developers.js. (You add code to this JS file in the next step.)
  5. Add JQuery.js to this folder. 
  6. Add a TXT file to the clientlibs folder named js.txt. Add the content specified in this section.

Add JavaScript Logic to ClientLibs

Add JavaScript logic that uses the AEM Widget API to the JS file that you created in the clientlibs node:

  • developers.js: defines the custom xtype that is used in the Developers List tab. The name of the custom xtype defined in this file is named developerlist.

To develop a custom xtype, you can use a CQ.form.CompositeField object, which is the base class for panel based, complex form fields which include one form field or a group of form fields. For information, see class CQ.form.CompositeField.

The component’s dialog references the custom xtype. The following file represents the developers.js file.

try {
    if (typeof DEV == 'undefined') {
        DEV = {}; // creating namespace
    }
    DEV.CustomImageListWidget = CQ.Ext.extend(CQ.form.CompositeField, {

        /**
         * @private
         * @type CQ.Ext.form.TextField
         */
        hiddenField: null,

       /**
        * @private
        * @type CQ.Ext.form.TextField
        */
        nameField: null,
    
        /**
        * @private
        * @type CQ.form.PathField
        */
        devImage: null,
    
         /**
         * @private
         * @type CQ.form.TextField
         */
        codeName: null,

		/**
         * @private
         * @type CQ.Ext.form.ComboBox
         */
        devWeapon: null,

        /**
         * @private
         * @type CQ.Ext.form.TextArea
         */
        devDesc: null,
    
        constructor: function(config) {
            config = config || { };
            var defaults = {
                "border": true,
                "layout": "form",
                "padding": 10,
                style:"width:700px"
            };
            config = CQ.Util.applyDefaults(config, defaults);
            DEV.CustomImageListWidget.superclass.constructor.call(this, config);
        },
    
        // overriding CQ.Ext.Component#initComponent
        initComponent: function() {
            DEV.CustomImageListWidget.superclass.initComponent.call(this);
            this.hiddenField = new CQ.Ext.form.Hidden({
                name: this.name
            });
    
            this.add(this.hiddenField);

            //Name - START

            this.nameField = new CQ.Ext.form.TextField({
                fieldLabel: "Developer Name ",
                fieldDescription: "Provide you real name",
                width:400,
                listeners: {
                    change: {
                        scope:this,
                        fn:this.updateHidden
                    }
                }
            });
            this.add(this.nameField);
    
            //Name - END

            //Image - START 
    
            this.devImage = new CQ.form.PathField ({
                fieldLabel: "Image",
                rootPath:"/content/dam",
                regex :/\.(png|jpg|jpeg|gif|tiff|bmp|pnm|pgm|pbm|ppm|psd|eps|dng)$/,
                regexText : "Please choose an image file",
                editable : false,
                allowBlank: false,
                width: 225,
                listeners: {
                    change: {
                        scope: this,
                        fn: this.updateHidden
                    },
                    dialogclose: {
                        scope: this,
                        fn: this.updateHidden
                    }
                }
            });
            this.add(this.devImage);
    
            //Image - END
    

            //Code Name - START
    
            this.codeName = new CQ.Ext.form.TextField({
                fieldLabel: "Code Name",
                fieldDescription: "Provide your community code name ",
                width:400,
                listeners: {
                    change: {
                        scope: this,
                        fn: this.updateHidden
                    },
                    dialogclose: {
                        scope: this,
                        fn: this.updateHidden
                    }
                }
            });
            this.add(this.codeName);
    
            //Code Name - END


            //Weapon - START

            this.devWeapon = new CQ.Ext.form.ComboBox({
                triggerAction : 'all',
                lazyRender : true,
                mode : 'local',
                width : 225,
                fieldLabel : 'Cool Weapon',
                fieldDescription : 'Select the weapon in which you can beat the best',
                editable : false,
                store : new CQ.Ext.data.ArrayStore({
                    id : 0,
                    fields : [ 'myId', 'displayText' ],
                    data : [ [ 'Java', 'Java' ],
                            [ 'C++', 'C++' ],[ 'PHP', 'PHP' ],[ 'JS', 'JS' ],[ 'Python', 'Python' ],[ 'C#', 'C#' ]]
                }),
                valueField : 'myId',
                displayField : 'displayText',
                value : 'Java'
            });
            this.add(this.devWeapon);

            //Weapon - END

            //Description - START

            this.devDesc = new CQ.Ext.form.TextArea({
                fieldLabel: "I am ...",
                fieldDescription: "Provide your short description",
                width: 400,
                listeners: {
                    change: {
                        scope:this,
                        fn:this.updateHidden
                    }
                }
            });
            this.add(this.devDesc);

            //Description - END
    
        },
     
        // overriding CQ.form.CompositeField#processPath
        processPath: function(path) {
            console.log("CustomWidget#processPath", path);
            this.devImage.processPath(path);
            //this.linkType.processPath(path);
        },
     
        // overriding CQ.form.CompositeField#processRecord
        processRecord: function(record, path) {
            console.log("CustomWidget#processRecord", path, record);
            this.devImage.processRecord(record, path);
            //this.linkType.processRecord(record, path);
        },
     
        // overriding CQ.form.CompositeField#setValue
        setValue: function(value) {
            if (!this.devWeapon){
                return null;
            }
            if (value){
            	var parts = value.split("|");
                if (parts.length > 0){
            		this.nameField.setValue(parts[0]);
            		this.devImage.setValue(parts[1]);
            		this.codeName.setValue(parts[2]);
            		this.devWeapon.setValue(parts[3]);
            		this.devDesc.setValue(parts[4]);
            		//this.hiddenField.setValue(value);
                }
            }
        },

        // overriding CQ.form.CompositeField#getValue
        getValue: function() {
            return this.getRawValue();
        },

        // overriding CQ.form.CompositeField#getRawValue
        getRawValue: function() {
            if (!this.devWeapon){
                return null;
            }
			var title = this.nameField.getValue();
            var image = this.devImage.getValue();
			var link = this.codeName.getValue();
            var target = this.devWeapon.getValue();
            var altText = this.devDesc.getValue();

            if (title == '')
                title = " ";
            if (link == '')
                link = " ";
            if (target == '')
                target = " ";
            if (altText == '')
                altText = " ";
           	var value = title + "|" + image + "|" + link + "|" + target + "|" + altText;
            this.hiddenField.setValue(value);
            return value;
        },

        // private
        updateHidden: function() {
            //alert('customwidget updatehidden');
            this.hiddenField.setValue(this.getValue());
        }
    });
    // register xtype
    CQ.Ext.reg("developerlist", DEV.CustomImageListWidget);
    
} catch (e) {
}

Developer JSP

The developers.jsp is the main JSP file for the component and is located at: 

/apps/customJSPtag/components/developers/developers.jsp

This application logic located in the developers.jsp contains JSTL tags and reads the values entered into the controls fields located in each multifield. The following JSP code iterates through each multifield and prints out the values that an author enters into them.

<c:if test="${not empty fn:trim(properties.devlist)}">
    <div
    style="border: 1px solid grey; background-color: #F5FBFF; width: 600px; height: auto">
    <c:forEach var="items" items="${properties.devlist}">
    <c:set var="listItem" value="${fn:split(items,'|')}" />
    <c:set var="nameField" value="${fn:trim(listItem[0])}" />
    <c:set var="devImage" value="${fn:trim(listItem[1])}" />
    <c:set var="codeName" value="${fn:trim(listItem[2])}" />
    <c:set var="devWeapon" value="${fn:trim(listItem[3])}" />
    <c:set var="devDesc" value="${fn:trim(listItem[4])}" />
    <div id="developer" style="border: 1px solid grey; padding: 7px;margin-top:10px">
    <div id="image" >
    <img src="${devImage}" style="margin-left: 250px;border: 4px solid #FFCCCC;" align="centre" height="100" width="100"
title="Developers Image" alt="dev image" />
    </div>
    <div style="padding-left:7px;margin-top:5px">
    <div id="name">
    <b> Name: </b>${nameField}
    </div>
    <div id="codename">
    <b> Code Name: </b>${codeName}
    </div>
    <div id="weapon">
    <b> Dev. Skill: </b>${devWeapon}
    </div>
    <div id="image">
    <b> About our Ninga: </b>${devDesc}
    </div>
    </div>
    </div>
</c:forEach>

This line of code starts the iteratation through each pane in the multifield.

<c:forEach var="items" items="${properties.devlist}">

Notice that properties.devlist maps to the name property of multifield node in the JCR dialog, as shown in this illustration. 

mutlifieldPane

The following code represents the entire developer.jsp file.

<jsp:directive.include file="/libs/foundation/global.jsp" />
<cq:includeClientLib categories="demo.widget.developers" />


<div id="project" style="border: 1px solid grey; background-color: #FFF0F0; width: 600px; height: auto;padding:7px">
	<c:if test="${not empty properties.projectName }">
		<c:set var="projectName" value="${properties.projectName}" />
        <b>Project:</b> ${projectName}
    </c:if>
<br>
	<c:if test="${not empty properties.summary }">
		<c:set var="summary" value="${properties.summary}" />
     <b>Summary:</b> ${summary}
    </c:if>
</div>

<c:if test="${not empty fn:trim(properties.devlist)}">
	<div
		style="border: 1px solid grey; background-color: #F5FBFF; width: 600px; height: auto">
		<c:forEach var="items" items="${properties.devlist}">
			<c:set var="listItem" value="${fn:split(items,'|')}" />
			<c:set var="nameField" value="${fn:trim(listItem[0])}" />
			<c:set var="devImage" value="${fn:trim(listItem[1])}" />
			<c:set var="codeName" value="${fn:trim(listItem[2])}" />
			<c:set var="devWeapon" value="${fn:trim(listItem[3])}" />
			<c:set var="devDesc" value="${fn:trim(listItem[4])}" />
            <div id="developer" style="border: 1px solid grey; padding: 7px;margin-top:10px">
				<div id="image" >
                    <img src="${devImage}" style="margin-left: 250px;border: 4px solid #FFCCCC;" align="centre" height="100" width="100"
						title="Developers Image" alt="dev image" />
				</div>
                <div style="padding-left:7px;margin-top:5px">
                    <div id="name">
                       <b> Name: </b>${nameField}
                    </div>
                    <div id="codename">
                        <b> Code Name: </b>${codeName}
                    </div>
                    <div id="weapon">
                        <b> Dev. Skill: </b>${devWeapon}
                    </div>
                    <div id="image">
                        <b> About our Ninga: </b>${devDesc}
                    </div>
                </div>
			</div>
		</c:forEach>
	</div>
</c:if>

Create a web page that component

Create an AEM page that displays the component.

  1. Go to the Websites page at http://localhost:4502/siteadmin#/content.
  2. Select New Page.
  3. Specify the title of the page in the Title field. Enter DisplayJSPTag.
  4. Specify the name of the page in the Name field.
  5. Select templateJSPDev from the template list that appears. This value represents the template that is created in this development article. If you do not see it, then repeat the steps in this development article. For example, if you made a typing mistake when entering in path information, the template will not show up in the New Page dialog box.
  6. Open the page by clicking the DisplayJSPTag page.
  7. The developers component will be under the Adobe heading in the sidekick.
  8. Open the component dialog and enter values into the dialog fields.  

Populate the AEM side kick

If the CQ sidekick is empty, you can populate it by clicking the Design button located at the bottom of the sidekick. Next click the Edit button that appears and select Adobe from the list of categories. To view the sidekick with the Adobe category, click the down arrow icon that appears on the sidekick. Drag the developers component on the AEM web page. Double click on the component and you see the dialog that you created in this development article.  

The following illustration, shows the Adobe category where the component is located. 

AdobeDialog

See also

Congratulations, you have just created an AEM component using JSTL. Please refer to the AEM community page for other articles that discuss how to build AEM services/applications.

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