Nested multifield in AEM 6.3 + sightly | Community
Skip to main content
anoopo70540109
September 20, 2017

Nested multifield in AEM 6.3 + sightly

  • September 20, 2017
  • 4 replies
  • 10891 views

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

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.

4 replies

smacdonald2008
September 20, 2017

Thanks for posting this.

October 2, 2017

thank you for this!! <3

October 2, 2017

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

anoopo70540109
October 11, 2017

Are you using ACS common.?

anoopo70540109
January 15, 2018

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

})();

namitkochar
August 23, 2019

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#DownloadInstructionsforCFPviaPackageShare)
  2. AEM 6.3.3.0 Service Pack (https://helpx.adobe.com/experience-manager/6-3/release-notes/sp3-release-notes.html#Install6330)