Expand my Community achievements bar.

Dive into Adobe Summit 2024! Explore curated list of AEM sessions & labs, register, connect with experts, ask questions, engage, and share insights. Don't miss the excitement.

Nested multifield in AEM 6.3 + sightly

Avatar

Level 2

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

8 Replies

Avatar

Level 4

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

Avatar

Level 2

(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);

})();

Avatar

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.

Avatar

Community Advisor

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.



Arun Patidar

Avatar

Level 2

Using aem 6.3 service pack and cummulative fix pack, multiield of 'n' number of levels can be added in AEM 6.3.

  1. AEM 6.3 Cumulative Fix Pack (https://helpx.adobe.com/experience-manager/release-notes--aem-6-3-cumulative-fix-pack.html#DownloadI...)
  2. AEM 6.3.3.0 Service Pack (https://helpx.adobe.com/experience-manager/6-3/release-notes/sp3-release-notes.html#Install6330)