Goal
Create a Touch UI Composite Multifield, storing field values as child nodes (useful when executing search queries for exact matches).
For storing values as json check this post
For Classic UI Composite Multifield , storing values as child nodes check this post
Tested on AEM 61; should work ok on 60
Demo | Package Install
Composite Multifield in Dialog
![]()
Value Nodes in CRX
![]()
Dialog Structure
![]()
Dialog XML
Sample dialog with 3 composite multifields added in 3 tabs. #49, #125, #202 mark these multifields as composite, by specifying the flag eaem-nested
1) Login to CRXDE Lite, create folder (nt:folder) /apps/touch-ui-composite-multi-field-store-as-child-nodes
2) Create clientlib (type cq:ClientLibraryFolder) /apps/touch-ui-composite-multi-field-store-as-child-nodes/clientlib and set a property categories of String type to cq.authoring.dialog, dependencies of type String[] with value underscore
3) Create file ( type nt:file ) /apps/touch-ui-composite-multi-field-store-as-child-nodes/clientlib/js.txt, add the following
multifield.js
4) Create file ( type nt:file ) /apps/classic-ui-multi-field-panel-node-store/clientlib/multifield.js, add the following code
5) A sample exact match query performed with query builder, on property name page, returning composite multifield nodes
![]()
For storing values as json check this post
For Classic UI Composite Multifield , storing values as child nodes check this post
Tested on AEM 61; should work ok on 60
Demo | Package Install
Composite Multifield in Dialog
Value Nodes in CRX
Dialog Structure
Dialog XML
Sample dialog with 3 composite multifields added in 3 tabs. #49, #125, #202 mark these multifields as composite, by specifying the flag eaem-nested
<?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/tabs"
type="nav"/>
<items jcr:primaryType="nt:unstructured">
<india
jcr:primaryType="nt:unstructured"
jcr:title="India"
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">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<fieldset
jcr:primaryType="nt:unstructured"
jcr:title="India 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="./iDashboard"/>
<pages
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/multifield"
class="full-width"
eaem-nested=""
fieldDescription="Click '+' to add a new page"
fieldLabel="URLs">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/fieldset"
name="./iItems">
<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>
</india>
<usa
jcr:primaryType="nt:unstructured"
jcr:title="USA"
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">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<fieldset
jcr:primaryType="nt:unstructured"
jcr:title="USA 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="./uDashboard"/>
<pages
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/multifield"
class="full-width"
eaem-nested=""
fieldDescription="Click '+' to add a new page"
fieldLabel="URLs">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/fieldset"
eaem-nested=""
name="./uItems">
<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>
</usa>
<uk
jcr:primaryType="nt:unstructured"
jcr:title="UK"
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">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<fieldset
jcr:primaryType="nt:unstructured"
jcr:title="UK 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="./ukDashboard"/>
<pages
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/multifield"
class="full-width"
eaem-nested=""
fieldDescription="Click '+' to add a new page"
fieldLabel="URLs">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/fieldset"
eaem-nested=""
name="./ukItems">
<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>
</uk>
</items>
</content>
</jcr:root>
Solution
1) Login to CRXDE Lite, create folder (nt:folder) /apps/touch-ui-composite-multi-field-store-as-child-nodes
2) Create clientlib (type cq:ClientLibraryFolder) /apps/touch-ui-composite-multi-field-store-as-child-nodes/clientlib and set a property categories of String type to cq.authoring.dialog, dependencies of type String[] with value underscore
3) Create file ( type nt:file ) /apps/touch-ui-composite-multi-field-store-as-child-nodes/clientlib/js.txt, add the following
multifield.js
4) Create file ( type nt:file ) /apps/classic-ui-multi-field-panel-node-store/clientlib/multifield.js, add the following code
(function () {
var DATA_EAEM_NESTED = "data-eaem-nested";
var CFFW = ".coral-Form-fieldwrapper";
//reads multifield data from server, creates the nested composite multifields and fills them
function addDataInFields() {
function getMultiFieldNames($multifields){
var mNames = {}, mName;
$multifields.each(function (i, multifield) {
mName = $(multifield).children("[name$='@Delete']").attr("name");
mName = mName.substring(0, mName.indexOf("@"));
mName = mName.substring(2);
mNames[mName] = $(multifield);
});
return mNames;
}
function buildMultiField(data, $multifield, mName){
if(_.isEmpty(mName) || _.isEmpty(data)){
return;
}
_.each(data, function(value, key){
if(key == "jcr:primaryType"){
return;
}
$multifield.find(".js-coral-Multifield-add").click();
_.each(value, function(fValue, fKey){
if(fKey == "jcr:primaryType"){
return;
}
var $field = $multifield.find("[name='./" + fKey + "']").last();
if(_.isEmpty($field)){
return;
}
$field.val(fValue);
});
});
}
$(document).on("dialog-ready", function() {
var $multifields = $("[" + DATA_EAEM_NESTED + "]");
if(_.isEmpty($multifields)){
return;
}
var mNames = getMultiFieldNames($multifields),
$form = $(".cq-dialog"),
actionUrl = $form.attr("action") + ".infinity.json";
$.ajax(actionUrl).done(postProcess);
function postProcess(data){
_.each(mNames, function($multifield, mName){
buildMultiField(data[mName], $multifield, mName);
});
}
});
}
//collect data from widgets in multifield and POST them to CRX
function collectDataFromFields(){
function fillValue($form, fieldSetName, $field, counter){
var name = $field.attr("name");
if (!name) {
return;
}
//strip ./
if (name.indexOf("./") == 0) {
name = name.substring(2);
}
//remove the field, so that individual values are not POSTed
$field.remove();
$('<input />').attr('type', 'hidden')
.attr('name', fieldSetName + "/" + counter + "/" + name)
.attr('value', $field.val())
.appendTo($form);
}
$(document).on("click", ".cq-dialog-submit", function () {
var $multifields = $("[" + DATA_EAEM_NESTED + "]");
if(_.isEmpty($multifields)){
return;
}
var $form = $(this).closest("form.foundation-form"),
$fieldSets, $fields;
$multifields.each(function(i, multifield){
$fieldSets = $(multifield).find("[class='coral-Form-fieldset']");
$fieldSets.each(function (counter, fieldSet) {
$fields = $(fieldSet).children().children(CFFW);
$fields.each(function (j, field) {
fillValue($form, $(fieldSet).data("name"), $(field).find("[name]"), (counter + 1));
});
});
});
});
}
$(document).ready(function () {
addDataInFields();
collectDataFromFields();
});
})();
5) A sample exact match query performed with query builder, on property name page, returning composite multifield nodes