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 .

After completing this tutorial, you will have a clear understanding about:-

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 :-

touchui-multified-aem-6.3

  • 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:-

multifield-component-aem6.3

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 retrive 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;

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:-

multifiled-json-array-data-aem

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 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.

 

Spread the love
  •  
  •  
  •  
  •  
  •  
  •  

Leave a Reply

Your email address will not be published. Required fields are marked *