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
For Touch UI Image Multifield check this post
Tested on AEM 61; should work ok on 60
Demo | Package Install
Bug Fixes
AEM 61 - Select granite/ui/components/foundation/form/select and Checkbox granite/ui/components/foundation/form/checkbox show incorrect values on dialog open - Demo | Package Install
AEM 60 SP2 - Select granite/ui/components/foundation/form/select and Checkbox granite/ui/components/foundation/form/checkbox show incorrect values on dialog open - 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/touch-ui-composite-multi-field-store-as-child-nodes/clientlib/multifield.js, add the following code
For storing values as json check this post
For Classic UI Composite Multifield , storing values as child nodes check this post
For Touch UI Image Multifield check this post
Tested on AEM 61; should work ok on 60
Demo | Package Install
Bug Fixes
AEM 61 - Select granite/ui/components/foundation/form/select and Checkbox granite/ui/components/foundation/form/checkbox show incorrect values on dialog open - Demo | Package Install
AEM 60 SP2 - Select granite/ui/components/foundation/form/select and Checkbox granite/ui/components/foundation/form/checkbox show incorrect values on dialog open - 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/touch-ui-composite-multi-field-store-as-child-nodes/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(); }); })();
It did not work for me on AEM 6.0, the values are saved under a String[] instead of each element in a node.
ReplyDeleteLooks like 60 doesn't POST @Delete attribute on dialog close... have added a fix in the composite multifield extension
DeleteDemo on 60: https://drive.google.com/file/d/0B4d6KmbLkAumUUZpRXAxM1g3ZmM/view?usp=sharing
Demo on 60 SP2: https://drive.google.com/file/d/0B4d6KmbLkAumTnpYWjZvejFHVHM/view?usp=sharing
Package Install: https://drive.google.com/file/d/0B4d6KmbLkAumV1AtMDRYMmF3cHc/view?usp=sharing
Great component, but it doesn't work for me with some field types. I tried granite/ui/components/foundation/form/select and checkbox. The value entered into these field types is saved, but the value is not loaded/displayed when I open the dialog again. Do you have any idea why this is happening? Thanks!
ReplyDeleteHi, thanks for reporting
Delete61 package install - https://drive.google.com/file/d/0B4d6KmbLkAumRk5OeUlQY3N1c3c/view?usp=sharing
61 demo - https://drive.google.com/file/d/0B4d6KmbLkAumLV9LUW5HeDFMVU0/view?usp=sharing
Thanks for the fix! It works.
DeleteHi Sreekanth, I tried both the original package as well as the one with bugfix for AEM6.1. After saving the data in dialog and reopeing the dialog, I am seeing the values multiple times. Stuck there. :-(
ReplyDeletehi DJ, can you upload the dialog xml and share url here?
DeleteHi Sreekanth,
DeleteI too am facing the same issue where the data is shown two times upon opening the dialog for subsequent edits. I followed the similar dialog structure mentioned here in the blog.
I had modified nested-multifield.js to have the multi-field node names as item_1, item_2, etc... instead of 1,2 and so on.
Please let me know how to fix this issue.
Let me know a way to upload the cq:dialog xml.
hi Srikanth, can you upload the component (with dialog xml and clientlib) to google drive or dropbox and post the link here....
DeleteHi Sreekanth,
DeleteHere is the google drive link for dialog xml and clientlib
https://www.dropbox.com/sh/l49jejjomr2bq81/AADC3r2l4XNSwwQiM9-irnLra?dl=0
Srikanth, tested with the uploaded dialog xml and it seems to be working fine, other than the selects are not set to correct value in multifield on dialog reopen (please check the bugfixes section above for fix) the following demo shows nodes created as item_ and select widget fix...
Deletehttps://drive.google.com/file/d/0B4d6KmbLkAumMzNGVU0yNElKZ3M/view?usp=sharing
need more detail or a video showing the steps to reproduce would be great...
This issue existed in only my local AEM instance. Works fine in my teammates' instances. Thanks for the help Sreekanth.
DeleteThis comment has been removed by the author.
DeleteHello Sreekanth, I need to have the control 'Select' inside the nested multifield, and it needs to be dynamically populated using JS.
DeleteCan you please suggest any solution.
Hi Sreekanth,
ReplyDeleteI am testing this code on AEM6.1 and it works fine except in one scenario when i have FileUpload widget in my dialog. When FileUpload dialog is present in dialog, images are displayed as many times as .js-coral-Multifield-add click event is called. I did some debugging and found that click event is trigerring cui-contentloaded event which in turns trigger CUI.FileChunkedUpload.init. This method is responsible for displaying additional images. Have you faced this issue or do you know a solution for this issue?
Thanks for all your help.
Regards,
Mayank
Mayank, are you looking for something like - http://experience-aem.blogspot.com/2015/06/aem-61-touch-ui-image-multifield.html
DeleteThis comment has been removed by the author.
DeleteSreekanth,
DeleteThanks for your reply. I am not creating image multifield rather i am creating normal multifield similar to the one in this post but in dialog i have FileUpload widget as well along with multifield i created.
Regards,
Mayank
This comment has been removed by the author.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteHi Sreekanth!
ReplyDeleteI've tried to implement your solution for nested multifield in multifield, but does not seems to work. What I'm trying to achieve is a first level multifield for countries, then a nested multifield with Language and Url for the specific language.
Any advice?
hi, can you upload the package to dropbox and share url....
DeleteHi Srikanth I am also trying for nested multi composite field. Can you please help how to make this work. https://www.dropbox.com/s/4gu0mavwbdnmg41/multimultifield.xml?dl=0
DeleteHi,
ReplyDeleteI need to implement same in the commerce screens. Can you please help
Hi Sreekanth, after added in few more granite UI dialogs, things gone haywire. Tested with two additional dialog on top of your package, text area and richtext.
ReplyDeleteWhen I created just one set of content, not multiple. It saved into jcr as node = 1. But when I open up my dialog, it displayed more than one set.
You can take a look on my package with below link (contains a lot of dialogs as test cases) and hope you will be able to help support all of them. Thanks.
https://www.dropbox.com/s/zotmxcc3imbfmdd/touch-ui-composite-multi-field-store-as-child-nodes.zip?dl=0
I'm looking for something that will allow me to have a numberfield and a checkbox inside a multifield. Whenever I do this, it saves the values in the node correctly as a String[], but it never re-displays the values when I get back to the dialog.
ReplyDeleteAny thoughts?
Hey Dude,
ReplyDeleteThis Multi field values are not working when we have a selection or drop-down .
Values are not getting retained when we open the dialog.
Thanks,
Venkatesh Seetha
Thank you so much for this! I wish Adobe would build this solution into AEM6.2. It solves a common problem with dialogs where we need to have a multifield containing groups of related input. For example, is there an OOTB way to enable a list of hyperlinks (i.e. href, title and text)?
ReplyDeleteHi sreekanth,
ReplyDeleteeverything is working fine and nodes are created but, after adding values to the dialog and reloading the page, the properties are duplicated and appearing twice in the dialog.
can you help me with this.
Thanks
Hi Karthick ,
DeleteAm also facing similar issue can u help me on this.Is this issue got resolved
Hi Sambasivaraja,
DeleteThe fields got replicated because, there was already a similar script added in my project.
Doesn't seem to work in 6.1.
ReplyDeleteJS console error:
Deleteclientlib.js:187 Uncaught RangeError: Maximum call stack size exceededCUI.Multifield.Class._addListeners @ clientlib.js:187CUI.Multifield.Class._addListeners @ clientlib.js:188CUI.Multifield.Class._addListeners @ clientlib.js:188CUI.Multifield.Class._addListeners @ clientlib.js:188CUI.Multifield.Class._addListeners @ clientlib.js:188CUI.Multifield.Class._addListeners @ clientlib.js:188CUI.Multifield.Class._addListeners @ clientlib.js:188CUI.Multifield.Class._addListeners @ clientlib.js:188CUI.Multifield.Class._addListeners @ clientlib.js:188CUI.Multifield.Class._addListeners @ clientlib.js:188
Hi, its not working for richtext , value not pre-poulating in dialog
ReplyDeleteHi All,
ReplyDeleteI need to restrict my path browser to a particular path in touch UI. On selection of a wrong path, it should throw an error.
Any help?
Thanks in advance
Hi All,
ReplyDeleteI've created a custom touch-ui multi-field dialog with respective js. Im facing a random behavior with the populating the multi-field. At times the fields get populated and at times they don't. There is no definitive pattern. Please help.
Thanks,
Mayur
Hi All,
ReplyDeleteTouch ui composite multi-field is not working in Safari browser.Any help on this
Hi All,
ReplyDeleteI tried this blog post in my local instance it works but my question is what happens if we upgrade to newer AEM instance do we have to update the nested-multifiled.js as well everytime? Do we have any newer version of this JS file ? The package I used was of 2015 from here: https://drive.google.com/file/d/0B4d6KmbLkAumRk5OeUlQY3N1c3c/view?usp=sharing
It would be good if the nested multifiled for component development becomes out of the box feature instead as its such common use case for component development. Any suggestions ?
Hi Srikanth,
ReplyDeletewhen i am trying to use chekbox or selection box inside multifileds. Value of checkbox do not retain when again i am opening th dialog . Plz help
Srikanth,
ReplyDeleteYou're a life saver, thanks for providing this plugin.
I notice you have contributed this to ACS AEM Commons as well.
Hi Srikanth,
ReplyDeleteI tried in AEM 6.2 but it is not working. I installed your package and tested. So i need to do more?
Hi,
ReplyDeleteI have developed a multifield component in touch UI.
The issue i am facing is the values in multifield is not retained in the dialog.
Can you please help me with the same.
Hi Srikanth,
ReplyDeleteCould you please share the sling model package.