Create TouchUI Multifield Component using HTL
The aim of this tutorial is to learn how to create Touch UI Multifield component using HTL formerly known as sightly. As out of the box multifield component has many limitations thanks to ACS common (Adobe consulting services) an open source community for enhancing the existing granite multifield component (granite/ui/components/foundation/form/multifield) that allows developers to create a multifield of a fieldset (group of different fields) . That’s why we are going to use a ACS common multifield component in this tutorial and show you how to read JSON value saved to the JCR .
Post Updated with creating TouchUI Mutlifield Component using HTL in (Adobe Experience Manager) AEM 6.5
After completing this tutorial, you will have a clear understanding about:-
- How to Create Multi Field TouchUI Component in AEM 6.3.
- How to retrieve values from Multi field dialog.
- How to retrieve checkbox values from Multi field dialog.
- Trouble Shooting Multi Field Component in AEM 6.3.
- How to create Multi Field Touch UI Component in AEM 6.5
Create TouchUI MultiField Component AEM 6.3:-
Lets take a simple use case of creating a user menu for your website and retrieving its value stored in the form of JSON Array from crxde/JCR repository . Follow below steps to create acs commons multi field component :-
- Login to crxde.
- Go to /apps/<project-name>/<path-to-component> [For Ex:- /apps/my-aem-project/components/content/ ]
- Create a new component under content node [For Ex:- In our example usermenu]
- Paste below content.xml of our multi field component.
myusermenu content.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:description="User menu component"
jcr:primaryType="cq:Component"
jcr:title="User Menu Custom"
componentGroup="aemcq5tutorials"/>
cq:dialog content.xml:-
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="User Menu"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/tabs"
type="nav"/>
<items jcr:primaryType="nt:unstructured">
<tabs
jcr:primaryType="nt:unstructured"
jcr:title="General"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<usersubmenu
jcr:primaryType="nt:unstructured"
jcr:title="User Submenu"
sling:resourceType="granite/ui/components/foundation/section">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
<items jcr:primaryType="nt:unstructured">
<tab
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<usersubmenudetails
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/multifield"
class="full-width"
fieldDescription="Click 'Add field' to add a new User Submenu title and links"
fieldLabel="User Submenu Items">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/fieldset"
acs-commons-nested=""
name="./myUserSubmenu">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"
method="absolute"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<title
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldDescription="Enter User Submenu title"
fieldLabel="User Submenu Title"
name="./title"/>
<link
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/pathbrowser"
fieldDescription="Enter User Submenu Link"
fieldLabel="User Submenu Link"
name="./link"
rootPath="/content"/>
<flag
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/checkbox"
checked="{Boolean}false"
fieldDescription="Testing Flag for checkbox"
name="./flag"
text="Flag"
title="Checkbox Tooltip"/>
</items>
</column>
</items>
</field>
</usersubmenudetails>
</items>
</tab>
</items>
</usersubmenu>
</items>
</tabs>
</items>
</content>
</jcr:root>
Note:- To use acs commons multifield it is mandatory to add acs-commons-nested=”” property to a fieldset within a multifield node like at myUserSubmenu node in our example. acs-commons-nested=”” acs-commons-nested=”JSON_STORE” or 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”.
cq:editConfig content.xml:-
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="cq:EditConfig">
<cq:listeners
jcr:primaryType="cq:EditListenersConfig"
afteredit="REFRESH_SELF"/>
</jcr:root>
dialog.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="cq:Dialog"
xtype="dialog"/>
Note:- dialog.xml or editConfig.xml is required because if we don’t have dialog in our component then it wont get displayed in side rail of Touch UI.
- After adding these nodes content.xml. Open component rending script or html file [For EX:- myusermenu.html] and add below code to it.
Click here to configure Multi Field User Menu
<div data-sly-use.multiItems="aemlearning.pojo.TouchMultiFieldComponentUse">
<div data-sly-list.head="${multiItems.multiFieldItems}">
<p><b>Page Name:</b> ${head.title}</p>
<p><b>Page Path:</b> ${head.link}</p>
<p><b>Flag:</b> ${head.flag}</p>
</div>
</div>
Now our component structure should look like below screenshot:-
Retrieve values from Multi field dialog:-
Once you have created the touchui multi field component using HTL, lets see how to retrieve its value.
Create two java file TouchMultiFieldBean and TouchMultiFieldComponentUse to retrieve values from crxde.
TouchMultiFieldBean.java
package aemlearning.bean;
/**
* POJO for Multi Field items
* @author aahlawat
*
*/
public class TouchMultiFieldBean {
private String title;
private String link;
private String flag;
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
}
TouchMultiFieldComponentUse.java
package aemlearning.pojo;
import java.util.ArrayList;
import java.util.List;
import org.apache.sling.commons.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.cq.sightly.WCMUsePojo;
import aemlearning.bean.TouchMultiFieldBean;
public class TouchMultiFieldComponentUse extends WCMUsePojo {
private static final Logger LOGGER = LoggerFactory.getLogger(TouchMultiFieldComponentUse.class);
private List<TouchMultiFieldBean> submenuItems = new ArrayList<>();
@Override
public void activate() throws Exception {
setMultiFieldItems();
}
/**
* Method to get Multi field data
* @return submenuItems
*/
private List<TouchMultiFieldBean> setMultiFieldItems() {
@SuppressWarnings("deprecation")
JSONObject jObj;
try{
String[] itemsProps = getProperties().get("myUserSubmenu", String[].class);
if (itemsProps != null) {
for (int i = 0; i < itemsProps.length; i++) {
jObj = new JSONObject(itemsProps[i]);
TouchMultiFieldBean menuItem = new TouchMultiFieldBean();
String title = jObj.getString("title");
String path = jObj.getString("link");
String flag = jObj.getString("flag");
menuItem.setTitle(title);
menuItem.setLink(path);
menuItem.setFlag(flag);
submenuItems.add(menuItem);
}
}
}catch(Exception e){
LOGGER.error("Exception while Multifield data {}", e.getMessage(), e);
}
return submenuItems;
}
public List<TouchMultiFieldBean> getMultiFieldItems() {
return submenuItems;
}
}
That’s it you are done
- Just build the component from eclipse.
- Drag and drop usermenu component on any parsys.
- Enter the values and save it.
Value stored in CRX/JCR in form of JSON Array:-
Note:- But our Multi field is still having one issue ACS commons Multifield has issues with retrieving values from checkbox and radio. That’s why adobe suggest to use dropdown instead of checkbox. But as nothing is impossible there is a slight change in acs commons js using which we can make multi field checkbox working.
Retrieve checkbox values from Multi field dialog:-
Follow below steps to retrive checkbox values from multi field component in aem, If you are using AEM 6.3 with service pack 1 then below fix is not required. Below fix is available as part of AEM 6.3 SP1 :-
- Got to /apps/acs-commons/touchui-widgets/composite-multifield/source/touchui-widgets-init.js.
- Navigate to Line no. 69
- Replace code $field.prop(“checked”, $field.attr(“value”) === value); inside setCheckbox method with below code
setCheckBox: function ($field, value) {
if(value != ""){
$field.prop("checked", true);
}else{
$field.prop("checked", $field.attr("value") === value);
}
}
Note:- It is not advised to change the code in acs commons , as when new version of acs commons comes, your changes will be overwritten.
You can download the above code for AEM 6.3 my git hub repository https://bitbucket.org/aahlawa/aem-learning/branch/multifield
Trouble Shooting Multi Field Component in AEM:-
- Getting Uncaught RangeError: Maximum call stack size exceeded on click of Add Field button
If you are getting Uncaught RangeError: Maximum call stack size exceeded on click of Add Field button, make sure you have not added any customized Multifield.js files.
Create Touch UI Multi Field Component in AEM 6.5:-
This section contains updated code as per AEM 6.5 instance. Link of git repo will be available at end of the tutorial
Updated cq:dialog content.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Nested Multifield Component"
sling:resourceType="cq/gui/components/authoring/dialog"
extraClientlibs="[core.wcm.components.navigation.v1.editor]"
helpPath="https://www.adobe.com/go/aem_cmp_navigation_v1">
<content
granite:class="cmp-navigation__editor"
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<tabs
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/tabs"
maximized="{Boolean}true">
<items jcr:primaryType="nt:unstructured">
<usersubmenu
jcr:primaryType="nt:unstructured"
jcr:title="User Submenu"
sling:resourceType="granite/ui/components/coral/foundation/container"
margin="{Boolean}true">
<items jcr:primaryType="nt:unstructured">
<columns
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<listitems
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
class="full-width"
composite="{Boolean}true"
fieldDescription="Click 'Add field' to add a new User Submenu title and links"
fieldLabel="">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/fieldset"
acs-commons-nested=""
name="./myUserSubmenu">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"
method="absolute"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<title
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldDescription="Enter User Submenu title"
fieldLabel="User Submenu Title"
name="./title"/>
<link
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/pathbrowser"
fieldDescription="Enter User Submenu Link"
fieldLabel="User Submenu Link"
name="./link"
rootPath="/content"/>
<flag
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/checkbox"
checked="{Boolean}false"
fieldDescription="Testing Flag for checkbox"
name="./flag"
text="Flag"
title="Checkbox Tooltip"/>
</items>
</column>
</items>
</field>
</listitems>
</items>
</column>
</items>
</columns>
</items>
</usersubmenu>
</items>
</tabs>
</items>
</content>
</jcr:root>
Updated myusermenu.html
Click here to configure Multi Field User Menu
<div data-sly-use.multiItems="aemcq5tutorials.core.models.TouchMultiFieldComponentModel">
<div data-sly-list.head="${multiItems.multiFieldItems}">
<p><b>Page Name:</b> ${head.title}</p>
<p><b>Page Path:</b> ${head.link}</p>
<p><b>Flag:</b> ${head.flag}</p>
</div>
</div>
Updated TouchMultiFieldComponentModel.java:-
From AEM 6.4 it is advised to use Model instead of Wcmuse class, updated the code accordingly below:-
package aemcq5tutorials.core.models;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Optional;
import org.apache.sling.models.annotations.Via;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import aemcq5tutorials.core.bean.TouchMultiFieldBean;
@Model(adaptables = { SlingHttpServletRequest.class, Resource.class })
public class TouchMultiFieldComponentModel {
private static final Logger LOGGER = LoggerFactory.getLogger(TouchMultiFieldComponentModel.class);
/**
* Tabs title multifield value.
*/
@Optional
@Inject
@Via("resource")
private List<Resource> myUserSubmenu;
private List<TouchMultiFieldBean> submenuItems;
/**
* Init Method of Model.
*/
@PostConstruct
public final void init() {
populateMultiFieldItems(myUserSubmenu);
}
/**
* Method to get Multi field data
* @return submenuItems
*/
private void populateMultiFieldItems(List<Resource> resourceList) {
if (null != resourceList && !resourceList.isEmpty()) {
submenuItems = new ArrayList<TouchMultiFieldBean>();
for (Resource item : resourceList) {
if (item != null) {
TouchMultiFieldBean menuItem = new TouchMultiFieldBean();
ValueMap vm = item.getValueMap();
String title = getPropertyValue(vm, "title");
String link = getPropertyValue(vm, "link");
String flag = getPropertyValue(vm, "flag");
menuItem.setTitle(title);
menuItem.setLink(link);
menuItem.setFlag(flag);
submenuItems.add(menuItem);
} else {
LOGGER.info("ValueMap not found for resource : {}", item);
}
}
}
}
private String getPropertyValue(final ValueMap properties, final String propertyName) {
return properties.containsKey(propertyName) ? properties.get(propertyName, String.class) : StringUtils.EMPTY;
}
public List<TouchMultiFieldBean> getMultiFieldItems() {
return this.submenuItems;
}
}
You can download the above code for AEM 6.5 my git hub repository https://bitbucket.org/aahlawa/aemcq5tutorials6.5/src/develop/
Leave a Reply