7 Replies Latest reply on Sep 14, 2018 2:16 AM by Arun Patidar

    Nested multifield in AEM 6.3 + sightly

    anoopo70540109 Level 1

      Multifield dialog-

       

      <?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="List Collection"
          sling:resourceType="cq/gui/components/authoring/dialog">
          <content
              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">
                          <Basic
                              jcr:primaryType="nt:unstructured"
                              jcr:title="Basic"
                              sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
                              margin="{Boolean}false">
                              <items jcr:primaryType="nt:unstructured">
                                  <column
                                      jcr:primaryType="nt:unstructured"
                                      sling:resourceType="granite/ui/components/coral/foundation/container">
                                      <items jcr:primaryType="nt:unstructured">
                                          <enterheadline
                                              jcr:primaryType="nt:unstructured"
                                              sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                              fieldDescription="Please enter headline"
                                              fieldLabel="Enter Headline"
                                              name="./headlineText"/>
                                          <headingurl
                                              jcr:primaryType="nt:unstructured"
                                              sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
                                              fieldDescription="Select Link Path"
                                              fieldLabel="Link Url"
                                              name="./headingUrl"/>
                                          <menu
                                              jcr:primaryType="nt:unstructured"
                                              sling:resourceType="granite/ui/components/foundation/form/multifield"
                                              composite="{Boolean}true"
                                              fieldDescription="Click + to add a new page"
                                              fieldLabel="Multifield collection"
                                              name="./menu">
                                              <field
                                                  jcr:primaryType="nt:unstructured"
                                                  sling:resourceType="granite/ui/components/foundation/form/fieldset"
                                                  multifield-nested=""
                                                  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">
                                                              <linkurl
                                                                  jcr:primaryType="nt:unstructured"
                                                                  sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
                                                                  fieldDescription="Select Link Path"
                                                                  fieldLabel="Link Url"
                                                                  name="./linkUrl"/>
                                                              <enterlinktext
                                                                  jcr:primaryType="nt:unstructured"
                                                                  sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                                                  fieldDescription="Enter Link Text"
                                                                  fieldLabel="Enter link text"
                                                                  name="./linkText"/>
                                                              <submenu
                                                                  jcr:primaryType="nt:unstructured"
                                                                  sling:resourceType="granite/ui/components/foundation/form/multifield"
                                                                  fieldDescription="Add upto 14 links"
                                                                  fieldLabel="Submenus"
                                                                  name="./submenussss">
                                                                  <field
                                                                      jcr:primaryType="nt:unstructured"
                                                                      sling:resourceType="granite/ui/components/foundation/form/fieldset"
                                                                      name="./submenu">
                                                                      <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">
                                                                                  <url
                                                                                      jcr:primaryType="nt:unstructured"
                                                                                      sling:resourceType="granite/ui/components/foundation/form/pathbrowser"
                                                                                      allowBlank="false"
                                                                                      fieldLabel="Provide navigation link for text"
                                                                                      key="url"
                                                                                      name="./subUrl"/>
                                                                                  <title
                                                                                      jcr:primaryType="nt:unstructured"
                                                                                      sling:resourceType="granite/ui/components/foundation/form/textfield"
                                                                                      allowBlank="false"
                                                                                      fieldLabel="Provide navigation text"
                                                                                      key="title"
                                                                                      maxLength="16"
                                                                                      maxLengthText="A maximum of 16 characters is allowed for navigation text"
                                                                                      name="./subTitle"/>
                                                                              </items>
                                                                          </column>
                                                                      </items>
                                                                  </field>
                                                              </submenu>
                                                          </items>
                                                      </column>
                                                  </items>
                                              </field>
                                          </menu>
                                      </items>
                                  </column>
                              </items>
                          </Basic>
                      </items>
                  </tabs>
              </items>
          </content>
      </jcr:root>
      
      
      
      

       

       

      navigation.js

       

      "use strict";
      
      
      use(function () {
          var readJson = null;
          var listArr = [];
          if (this.fieldValue != '') {
              readJson = this.fieldValue;
              var count = 0;
              if (readJson != null && readJson != "") {
                  readJson.forEach(function (entry) {
                      var itemJson = entry;
                      listArr[count++] = itemJson;
                  });
              }
          } else if (this.fieldName != '') {
              readJson = granite.resource.properties[this.fieldName];
              if (typeof readJson != "undefined" && readJson != null && readJson != "") {
                  if (readJson.length > 0) {
                      var count = 0;
                      readJson.forEach(function (entry) {
                          var itemJson = JSON.parse(entry);
                          listArr[count++] = itemJson;
                      });
                  } else {
                      listArr[0] = JSON.parse(readJson);
                  }
              }
          }
          return {
              listJson: listArr
          }
      });
      

       

      Sightly: somecomponent.html

       

       

       

      <sly data-sly-use.menuInfo="${'navigation.js' @ fieldName = 'items',fieldValue = ''}">
      
                              <sly data-sly-list.menuDetails="${menuInfo.listJson}">
      
      
      <li><a href="${menuDetails.linkUrl}">${menuDetails.linkText}</a>
      <ul>
                                      <sly data-sly-use.submenuInfo="${'navigation.js' @ fieldName = '',fieldValue = menuDetails.submenu}">
                                      <sly data-sly-list.innerlistJson="${submenuInfo.listJson}">
      ${innerlistJson.subTitle}</span></a></li>
      </sly>
                                      </sly>
                                      </ul>
      </li>
      
      
       </sly>
      
                              </sly>
      

       

      I have created this without using ACS-common package. hope it will helpful for the community

        • 1. Re: Nested multifield in AEM 6.3 + sightly
          smacdonald2008 Adobe Employee

          Thanks for posting this.

          • 2. Re: Nested multifield in AEM 6.3 + sightly
            neilbertm66404342 Level 1

            thank you for this!! <3

            • 3. Re: Nested multifield in AEM 6.3 + sightly
              neilbertm66404342 Level 1

              the add button for multifield node which is menu

              slingResourceType: granite/ui/components/foundation/form/multifield - add button not working

              sling:resourceType="granite/ui/components/coral/foundation/form/multifield" - add button working but the Ok/Submit button not working

              • 4. Re: Nested multifield in AEM 6.3 + sightly
                anoopo70540109 Level 1

                Are you using ACS common.?

                • 5. Re: Nested multifield in AEM 6.3 + sightly
                  anoopo70540109 Level 1
                  (function () {
                    var DATA_NESTED = "data-multifield-nested";
                    var CFFW = ".coral-Form-fieldwrapper";
                    //reads multifield data from server, creates the nested composite multifields and fills them
                    function addDataInFields() {
                      $(document).on("dialog-ready", function () {
                  
                  
                        var $fieldSets = $("[" + DATA_NESTED + "][class='coral-Form-fieldset']");
                        if (_.isEmpty($fieldSets)) {
                          return;
                        }
                        var mNames = [];
                        $fieldSets.each(function (i, fieldSet) {
                          mNames.push($(fieldSet).data("name"));
                        });
                        mNames = _.uniq(mNames);
                        var actionUrl = $fieldSets.closest("form.foundation-form").attr("action") + ".json";
                        $.ajax(actionUrl).done(postProcess);
                        function postProcess(data) {
                          _.each(mNames, function (mName) {
                            buildMultiField(data, mName);
                          });
                        }
                        //creates & fills the nested multifield with data
                        function fillNestedFields($multifield, valueArr) {
                          _.each(valueArr, function (record, index) {
                            $multifield.find(".js-coral-Multifield-add").click();
                  
                  
                            //a setTimeout may be needed
                            _.each(record, function (value, key) {
                              var $field = $($multifield.find("[name='./" + key + "']")[index]);
                              $field.val(value);
                            });
                          });
                        }
                        function buildMultiField(data, mName) {
                          if (_.isEmpty(mName)) {
                            return;
                          }
                          $fieldSets = $("[data-name='" + mName + "']");
                          mName = mName.substring(2);
                          var mValues = data[mName], $field, name;
                          if (_.isString(mValues)) {
                            mValues = [JSON.parse(mValues)];
                          }
                          _.each(mValues, function (record, i) {
                            if (!record) {
                              return;
                            }
                            if (_.isString(record)) {
                              record = JSON.parse(record);
                            }
                            _.each(record, function (rValue, rKey) {
                              $field = $($fieldSets[i]).find("[name='./" + rKey + "']");
                  
                  
                              if (_.isArray(rValue) && !_.isEmpty(rValue)) {
                                fillNestedFields($($fieldSets[i]).find("[data-init='multifield']"), rValue);
                              } else {
                                $field.val(rValue);
                              }
                            });
                          });
                        }
                      });
                    }
                    function fillValue($field, record) {
                      var 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();
                    }
                    //for getting the nested multifield data as js objects
                    function getRecordFromMultiField($multifield) {
                      var $fieldSets = $multifield.find("[class='coral-Form-fieldset']");
                      var records = [], record, $fields, name;
                      $fieldSets.each(function (i, fieldSet) {
                        $fields = $(fieldSet).find("[name]");
                        record = {};
                        $fields.each(function (j, field) {
                          fillValue($(field), record);
                        });
                        if (!$.isEmptyObject(record)) {
                          records.push(record);
                        }
                      });
                      return records;
                    }
                    //collect data from widgets in multifield and POST them to CRX as JSON
                    function collectDataFromFields() {
                      $(document).on("click", ".cq-dialog-submit", function () {
                        var fieldd = $(".coral-Multifield");
                        var sizee = fieldd.attr("data-minlinksallowed");
                        if (sizee) {
                          var uii = $(window).adaptTo("foundation-ui");
                          var totalLinkCount = $(".coral-Multifield-list li").length;
                          if (totalLinkCount < sizee) {
                            uii.alert("Minimum " + sizee + " fields are required");
                            return false;
                          }
                        }
                        var $form = $(this).closest("form.foundation-form");
                        $form.find('input.input-mf-data').each(function(){
                          $(this).remove();
                        });
                        var $fieldSets = $("[" + DATA_NESTED + "][class='coral-Form-fieldset']");
                        var record, $fields, $field, name, $nestedMultiField;
                        $fieldSets.each(function (i, fieldSet) {
                          $fields = $(fieldSet).children().children(CFFW);
                          record = {};
                          $fields.each(function (j, field) {
                            $field = $(field);
                            //may be a nested multifield
                            $nestedMultiField = $field.find("[data-init='multifield']");
                            if ($nestedMultiField.length === 0) {
                              fillValue($field.find("[name]"), record);
                            } else {
                              name = $nestedMultiField.find("[class='coral-Form-fieldset']").data("name");
                              if (!name) {
                                return;
                              }
                              //strip ./
                              name = name.substring(2);
                              record[name] = getRecordFromMultiField($nestedMultiField);
                            }
                          });
                          if ($.isEmptyObject(record)) {
                            return;
                          }
                          //add the record JSON in a hidden field as string
                            $('<input />').attr('type', 'hidden')
                              .attr('class', 'input-mf-data')
                              .attr('name', $(fieldSet).data("name"))
                              .attr('value', JSON.stringify(record))
                              .appendTo($form);
                        });
                        return true;
                      });
                    }
                    $(document).ready(function () {
                      addDataInFields();
                      collectDataFromFields();
                    });
                    $(document).on("dialog-ready", function () {
                      $(".js-coral-Multifield-add").click(function () {
                        var field = $(this).parent();
                        var size = field.attr("data-limit");
                        if (size) {
                          var ui = $(window).adaptTo("foundation-ui");
                          var totalLinkCount = $(this).prev('ol').children('li').length;
                          if (totalLinkCount >= size) {
                            ui.alert("Warning", "Only " + totalLinkCount + " fields are allowed!", "notice");
                            return false;
                          }
                        }
                      });
                    });
                  
                  
                    //extend otb multifield for adjusting event propagation when there are nested multifields
                    //for working around the nested multifield add and reorder
                    CUI.MultifieldProject = new Class({
                      toString: "Multifield",
                      extend: CUI.Multifield,
                      construct: function (options) {
                        this.script = this.$element.find(".js-coral-Multifield-input-template:last");
                      },
                      _addListeners: function () {
                        this.superClass._addListeners.call(this);
                        //otb coral event handler is added on selector .js-coral-Multifield-add
                        //any nested multifield add click events are propagated to the parent multifield
                        //to prevent adding a new composite field in both nested multifield and parent multifield
                        //when user clicks on add of nested multifield, stop the event propagation to parent multifield
                        this.$element.on("click", ".js-coral-Multifield-add", function (e) {
                          e.stopPropagation();
                        });
                        this.$element.on("drop", function (e) {
                          e.stopPropagation();
                        });
                      }
                    });
                    CUI.Widget.registry.register("multifield", CUI.MultifieldProject);
                  })();
                  (function () {
                    var DATA_NESTED = "data-multifield-nested";
                    var CFFW = ".coral-Form-fieldwrapper";
                    //reads multifield data from server, creates the nested composite multifields and fills them
                    function addDataInFields() {
                      $(document).on("dialog-ready", function () {
                        var $fieldSets = $("[" + DATA_NESTED + "][class='coral-Form-fieldset']");
                        if (_.isEmpty($fieldSets)) {
                          return;
                        }
                        var mNames = [];
                        $fieldSets.each(function (i, fieldSet) {
                          mNames.push($(fieldSet).data("name"));
                        });
                        mNames = _.uniq(mNames);
                        var actionUrl = $fieldSets.closest("form.foundation-form").attr("action") + ".json";
                        $.ajax(actionUrl).done(postProcess);
                        function postProcess(data) {
                          _.each(mNames, function (mName) {
                            buildMultiField(data, mName);
                          });
                        }
                        //creates & fills the nested multifield with data
                        function fillNestedFields($multifield, valueArr) {
                          _.each(valueArr, function (record, index) {
                            $multifield.find(".js-coral-Multifield-add").click();
                  
                  
                            //a setTimeout may be needed
                            _.each(record, function (value, key) {
                              var $field = $($multifield.find("[name='./" + key + "']")[index]);
                              $field.val(value);
                            });
                          });
                        }
                        function buildMultiField(data, mName) {
                          if (_.isEmpty(mName)) {
                            return;
                          }
                          $fieldSets = $("[data-name='" + mName + "']");
                          mName = mName.substring(2);
                          var mValues = data[mName], $field, name;
                          if (_.isString(mValues)) {
                            mValues = [JSON.parse(mValues)];
                          }
                          _.each(mValues, function (record, i) {
                            if (!record) {
                              return;
                            }
                            if (_.isString(record)) {
                              record = JSON.parse(record);
                            }
                            _.each(record, function (rValue, rKey) {
                              $field = $($fieldSets[i]).find("[name='./" + rKey + "']");
                  
                  
                              if (_.isArray(rValue) && !_.isEmpty(rValue)) {
                                fillNestedFields($($fieldSets[i]).find("[data-init='multifield']"), rValue);
                              } else {
                                $field.val(rValue);
                              }
                            });
                          });
                        }
                      });
                    }
                    function fillValue($field, record) {
                      var 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();
                    }
                    //for getting the nested multifield data as js objects
                    function getRecordFromMultiField($multifield) {
                      var $fieldSets = $multifield.find("[class='coral-Form-fieldset']");
                      var records = [], record, $fields, name;
                      $fieldSets.each(function (i, fieldSet) {
                        $fields = $(fieldSet).find("[name]");
                        record = {};
                        $fields.each(function (j, field) {
                          fillValue($(field), record);
                        });
                        if (!$.isEmptyObject(record)) {
                          records.push(record);
                        }
                      });
                      return records;
                    }
                    //collect data from widgets in multifield and POST them to CRX as JSON
                    function collectDataFromFields() {
                      $(document).on("click", ".cq-dialog-submit", function () {
                        var fieldd = $(".coral-Multifield");
                        var sizee = fieldd.attr("data-minlinksallowed");
                        if (sizee) {
                          var uii = $(window).adaptTo("foundation-ui");
                          var totalLinkCount = $(".coral-Multifield-list li").length;
                          if (totalLinkCount < sizee) {
                            uii.alert("Minimum " + sizee + " fields are required");
                            return false;
                          }
                        }
                        var $form = $(this).closest("form.foundation-form");
                        $form.find('input.input-mf-data').each(function(){
                          $(this).remove();
                        });
                        var $fieldSets = $("[" + DATA_NESTED + "][class='coral-Form-fieldset']");
                        var record, $fields, $field, name, $nestedMultiField;
                        $fieldSets.each(function (i, fieldSet) {
                          $fields = $(fieldSet).children().children(CFFW);
                          record = {};
                          $fields.each(function (j, field) {
                            $field = $(field);
                            //may be a nested multifield
                            $nestedMultiField = $field.find("[data-init='multifield']");
                            if ($nestedMultiField.length === 0) {
                              fillValue($field.find("[name]"), record);
                            } else {
                              name = $nestedMultiField.find("[class='coral-Form-fieldset']").data("name");
                              if (!name) {
                                return;
                              }
                              //strip ./
                              name = name.substring(2);
                              record[name] = getRecordFromMultiField($nestedMultiField);
                            }
                          });
                          if ($.isEmptyObject(record)) {
                            return;
                          }
                          //add the record JSON in a hidden field as string
                            $('<input />').attr('type', 'hidden')
                              .attr('class', 'input-mf-data')
                              .attr('name', $(fieldSet).data("name"))
                              .attr('value', JSON.stringify(record))
                              .appendTo($form);
                        });
                        return true;
                      });
                    }
                    $(document).ready(function () {
                      addDataInFields();
                      collectDataFromFields();
                    });
                    $(document).on("dialog-ready", function () {
                      $(".js-coral-Multifield-add").click(function () {
                        var field = $(this).parent();
                        var size = field.attr("data-limit");
                        if (size) {
                          var ui = $(window).adaptTo("foundation-ui");
                          var totalLinkCount = $(this).prev('ol').children('li').length;
                          if (totalLinkCount >= size) {
                            ui.alert("Warning", "Only " + totalLinkCount + " fields are allowed!", "notice");
                            return false;
                          }
                        }
                      });
                    });
                  
                  
                    //extend otb multifield for adjusting event propagation when there are nested multifields
                    //for working around the nested multifield add and reorder
                    CUI.MultifieldProject = new Class({
                      toString: "Multifield",
                      extend: CUI.Multifield,
                      construct: function (options) {
                        this.script = this.$element.find(".js-coral-Multifield-input-template:last");
                      },
                      _addListeners: function () {
                        this.superClass._addListeners.call(this);
                        //otb coral event handler is added on selector .js-coral-Multifield-add
                        //any nested multifield add click events are propagated to the parent multifield
                        //to prevent adding a new composite field in both nested multifield and parent multifield
                        //when user clicks on add of nested multifield, stop the event propagation to parent multifield
                        this.$element.on("click", ".js-coral-Multifield-add", function (e) {
                          e.stopPropagation();
                        });
                        this.$element.on("drop", function (e) {
                          e.stopPropagation();
                        });
                      }
                    });
                    CUI.Widget.registry.register("multifield", CUI.MultifieldProject);
                  })();
                  
                  
                  • 6. Re: Nested multifield in AEM 6.3 + sightly
                    arpitas Level 1

                    This solution is not working in  coral3 for aem6.3 migration. In coral 3 multifield tenplate is different so this js doesnt work.

                    • 7. Re: Nested multifield in AEM 6.3 + sightly
                      Arun Patidar Experience Cloud MVP

                      Hi,

                      In Coral3, multifields items are store as a node not as JSON, To make it work this with Js or Java, you can iterate through nodes and return as JSON array or java Arraylist.