Quantcast
Channel: Experiencing Adobe Experience Manager (AEM, CQ)
Viewing all articles
Browse latest Browse all 525

AEM 6 SP2 - Touch UI Multi Field Component

$
0
0

Goal


Create a component with Touch UI (Coral UI) Dialog and Multifield (granite/ui/components/foundation/form/multifield) widget. Here we extend product multifield and create a composite multifield  /apps/touch-ui-multi-field-panel/multifield , configure it with two form fields, a Textfield (granite/ui/components/foundation/form/textfield), Pathbrowser (granite/ui/components/foundation/form/pathbrowser). End result is a simple Dashboard Component

A Classic UI Multifield configuration is available here

Demo | Package Install


Touch UI Dialog





Dialog Structure in CRX





Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de) and create a folder (nt:folder) /apps/touch-ui-multi-field-panel

2) Create a component (cq:Component) /apps/touch-ui-multi-field-panel/sample-multi-field. Check this post on how to create a CQ component

3) Add the following xml for /apps/touch-ui-multi-field-panel/sample-multi-field/dialog created. Ideally a cq:Dialog should be configured with necessary widgets for displaying dialog in Classic UI. This post is on Touch UI dialogs so keep it simple (node required for opening the touch ui dialog created in next step)

<?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"
title="Multi Field"
xtype="dialog"/>


4) Create a nt:unstructured node /apps/touch-ui-multi-field-panel/sample-multi-field/cq:dialog with following xml (the dialog UI as 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="Multifield TouchUI Component"
sling:resourceType="cq/gui/components/authoring/dialog"
helpPath="en/cq/current/wcm/default_components.html#Text">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<fieldset
jcr:primaryType="nt:unstructured"
jcr:title="Sample Dashboard"
sling:resourceType="granite/ui/components/foundation/form/fieldset">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<dashboard
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldDescription="Enter Dashboard name"
fieldLabel="Dashboard name"
name="./dashboard"/>
<pages
jcr:primaryType="nt:unstructured"
sling:resourceType="/apps/touch-ui-multi-field-panel/multifield"
class="full-width"
fieldDescription="Click '+' to add a new page"
fieldLabel="URLs">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/fieldset"
name="./items">
<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">
<page
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldDescription="Enter Page Name"
fieldLabel="Page Name"
name="./page"/>
<path
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/pathbrowser"
fieldDescription="Select Path"
fieldLabel="Path"
name="./path"
rootPath="/content"/>
</items>
</column>
</items>
</field>
</pages>
</items>
</column>
</items>
</fieldset>
</items>
</column>
</items>
</content>
</jcr:root>


5) The composite multifield node /apps/touch-ui-multi-field-panel/sample-multi-field/cq:dialog/content/items/column/items/fieldset/items/column/items/pages is of extended multifield type /apps/touch-ui-multi-field-panel/multifield (created in next steps)

6) Different layouts can be used to structure the multifield, here we use granite/ui/components/foundation/layouts/fixedcolumns layout

7) Create folder (sling:Folder) /apps/touch-ui-multi-field-panel/multifield

8) Create file (nt:file) /apps/touch-ui-multi-field-panel/multifield/multifield.jsp (for rendering the widget) and add following code. Inline JS was added for simplicity, its always recommended to create a clientlib (cq:ClientLibraryFolder) for javascript code

<%@ page import="com.adobe.granite.ui.components.Config" %>
<%@ page import="org.slf4j.Logger" %>
<%@ page import="org.slf4j.LoggerFactory" %>
<%@ page import="com.adobe.granite.ui.components.Value" %>
<%@ page import="org.apache.commons.lang3.StringUtils" %>
<%@include file="/libs/granite/ui/global.jsp" %>

<%--include ootb multifield--%>
<sling:include resourceType="/libs/granite/ui/components/foundation/form/multifield"/>

<%!
private final Logger mLog = LoggerFactory.getLogger(this.getClass());
%>

<%
Config mCfg = cmp.getConfig();

Resource mField = mCfg.getChild("field");

if (mField == null) {
mLog.warn("Field node doesn't exist");
return;
}

ValueMap mVM = mField.adaptTo(ValueMap.class);

String mName = mVM.get("name", "");

if ("".equals(mName)) {
mLog.warn("name property doesn't exist on field node");
return;
}

Value mValue = ((ComponentHelper) cmp).getValue();

//get the values added in multifield
String[] mItems = mValue.get(mName, String[].class);
%>

<script>
(function () {
//function to add values into multifield widgets. The values are stored in CRX by collectDataFromFields() as json
//eg. {"page":"English","path":"/content/geometrixx/en"}
var addDataInFields = function () {
var mValues = [ <%= StringUtils.join(mValue.get(mName, String[].class), ",") %> ],
mName = '<%=mName%>',
$fieldSets = $("[class='coral-Form-fieldset'][data-name='" + mName + "']");

var record, $fields, $field, name;

$fieldSets.each(function (i, fieldSet) {
$fields = $(fieldSet).find("[name]");

record = mValues[i];

if (!record) {
return;
}

$fields.each(function (j, field) {
$field = $(field);

name = $field.attr("name");

if (!name) {
return;
}

//strip ./
if (name.indexOf("./") == 0) {
name = name.substring(2);
}

$field.val(record[name]);
});
});
};

//collect data from widgets in multifield and POST them to CRX as JSON
var collectDataFromFields = function(){
$(document).on("click", ".cq-dialog-submit", function () {
var $form = $(this).closest("form.foundation-form"), mName = '<%=mName%>';

//get all the input fields of multifield
var $fieldSets = $("[class='coral-Form-fieldset'][data-name='" + mName + "']");

var record, $fields, $field, name;

$fieldSets.each(function (i, fieldSet) {
$fields = $(fieldSet).find("[name]");

record = {};

$fields.each(function (j, field) {
$field = $(field);

name = $field.attr("name");

if (!name) {
return;
}

//strip ./
if (name.indexOf("./") == 0) {
name = name.substring(2);
}

record[name] = $field.val();

//remove the field, so that individual values are not POSTed
$field.remove();
});

if ($.isEmptyObject(record)) {
return;
}

//add the record JSON in a hidden field as string
$('<input />').attr('type', 'hidden')
.attr('name', mName)
.attr('value', JSON.stringify(record))
.appendTo($form);
});
});
};

$(document).ready(function () {
addDataInFields();
collectDataFromFields();
});
})();
</script>


9) sling:include at #9 includes the product multifield  /libs/granite/ui/components/foundation/form/multifield after which the code necessary for creating a composite multifield is added

10) The function collectDataFromFields() at #80 registers a click listener on dialog submit, to collect the multifield form data, create a json to group it and add in a hidden field before data is POSTed to CRX. The function addDataInFields() at #44 reads json data added previously and fills the multifield fields, when dialog is reopened. The extension uses simple jquery calls to get and set data; based on the widget type more code may be necessary for supporting complex Granite UI widgets (granite/ui/components/foundation/form)

11) Create the component jsp /apps/touch-ui-multi-field-panel/sample-multi-field/sample-multi-field.jsp for rendering data entered in dialog

<%@ page import="org.apache.sling.commons.json.JSONObject" %>
<%@ page import="java.io.PrintWriter" %>
<%@include file="/libs/foundation/global.jsp" %>
<%@page session="false" %>

<div style="display: block; border-style: solid; border-width: 1px; margin: 10px; padding: 10px">
<b>Multi Field Sample Dashboard</b>

<%
try {
Property property = null;

if (currentNode.hasProperty("items")) {
property = currentNode.getProperty("items");
}

if (property != null) {
JSONObject obj = null;
Value[] values = null;

if (property.isMultiple()) {
values = property.getValues();
} else {
values = new Value[1];
values[0] = property.getValue();
}

for (Value val : values) {
obj = new JSONObject(val.getString());
%>
Page : <b><%= obj.get("page") %></b>,
URL : <b><a href="<%= obj.get("path") %>.html" target="_blank"><%= obj.get("path") %></a></b>

<%
}
} else {
%>
Add values in dialog
<%
}
} catch (Exception e) {
e.printStackTrace(new PrintWriter(out));
}
%>

</div>



Viewing all articles
Browse latest Browse all 525

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>