Nested multifield in AEM 6.3 + sightly

Avatar

Avatar

anoopo70540109

Avatar

anoopo70540109

anoopo70540109

20-09-2017

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

Replies

Avatar

Avatar

smacdonald2008

Total Posts

12.7K

Likes

1.4K

Correct Reply

2.3K

Avatar

smacdonald2008

Total Posts

12.7K

Likes

1.4K

Correct Reply

2.3K
smacdonald2008

20-09-2017

Thanks for posting this.

Avatar

Avatar

neilbertm664043

Avatar

neilbertm664043

neilbertm664043

01-10-2017

thank you for this!! ❤️

Avatar

Avatar

neilbertm664043

Avatar

neilbertm664043

neilbertm664043

01-10-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

Avatar

Avatar

anoopo70540109

Avatar

anoopo70540109

anoopo70540109

11-10-2017

Are you using ACS common.?

Avatar

Avatar

anoopo70540109

Avatar

anoopo70540109

anoopo70540109

14-01-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);

})();

Avatar

Avatar

arpitas

Avatar

arpitas

arpitas

13-09-2018

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

Avatar

Avatar

Arun_Patidar

MVP

Total Posts

3.0K

Likes

1.1K

Correct Reply

857

Avatar

Arun_Patidar

MVP

Total Posts

3.0K

Likes

1.1K

Correct Reply

857
Arun_Patidar
MVP

14-09-2018

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.

Avatar

Avatar

namitkochar

Avatar

namitkochar

namitkochar

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