Nested Multifield in Content Fragment - AEM 6.5 ? | Community
Skip to main content
kchaurasiya
Level 5
September 7, 2021
Solved

Nested Multifield in Content Fragment - AEM 6.5 ?

  • September 7, 2021
  • 7 replies
  • 16783 views

I have implemented  multi-field  in content fragment and it working and there is no any issue.

Also, Implementing nested multi-field but somehow it is working but not working fully. Values are getting save at backend but when i open the content fragment there are no auto value rendering in CF.

Has anyone implemented it or written JS code for the same . Please let me know. Thank You..!!

Best answer by prashantonkar

I have tried this once and had to do a lot of customization to get it working.

In your content fragment model, you need to make the change similar to this: ( i am just showing a part of CFM which I had made composite multifield.

<multifieldtest
	jcr:primaryType="nt:unstructured"
	sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
	composite="{Boolean}true"
	fieldLabel="Test Service"
	listOrder="1"
	maxlength="255"
	metaType="text-single"
	name="testSection"
	renderReadOnly="false"
	showEmptyInReadOnly="true"
	valueType="string[]">
	<field
		jcr:primaryType="nt:unstructured"
		sling:resourceType="granite/ui/components/coral/foundation/container"
		name="./testSection">
		<items jcr:primaryType="nt:unstructured">
			<column
				jcr:primaryType="nt:unstructured"
				sling:resourceType="granite/ui/components/coral/foundation/container">
				<items jcr:primaryType="nt:unstructured">
					<!-- your fields go heare -->
				</items>
			</column>
		</items>
	</field>
</multifieldtest>

After that, used the following code to populate the data into the fields:

(function ($) {
    var CFM = window.Dam.CFM,
        MASTER = "master",
        CFM_EDITOR_SEL = ".content-fragment-editor",
        CORAL_MF_ITEM = "coral-multifield-item",
        EAEM_COMPOSITE_ITEM_VALUE = "data-eaem-composite-item-value",
        MF_NAME_ATTR = "data-granite-coral-multifield-name",
        COMPOSITE_MF_SEL = "[data-granite-coral-multifield-composite]";

    CFM.Core.registerReadyHandler(getMultifieldsContent);

    extendRequestSave();

    function getMultifieldsContent(){
        if(!compositeMutifieldsExist()){
            return;
        }

        var url = CFM.EditSession.fragment.urlBase + "/jcr:content/data.2.json";

        $.ajax(url).done(loadContentIntoMultiFields);
    }

    function loadContentIntoMultiFields(data){
        var $composites = $(COMPOSITE_MF_SEL), mfValArr, mfAddEle,
            vData = data[getVariation()], $lastItem;

        if(_.isEmpty(vData)){
            return;
        }

        _.each($composites, function(mField){
            mfValArr = vData[getNameDotSlashRemoved(($(mField)).attr(MF_NAME_ATTR))];

            if(_.isEmpty(mfValArr)){
                return;
            }

            mfAddEle = mField.querySelector("[coral-multifield-add]");

            _.each(mfValArr, function(mfMap){
                mfAddEle.click();

                $lastItem = $(mField).find(CORAL_MF_ITEM).last();
                $lastItem.attr(EAEM_COMPOSITE_ITEM_VALUE, mfMap);
                Coral.commons.ready($lastItem[0], function (lastItem) {
                    fillMultifieldItems(lastItem);
                });

            });
        });
    }

    function fillMultifieldItems(mfItem){
        if(mfItem == null){
            return;
        }

        var mfMap = mfItem.getAttribute(EAEM_COMPOSITE_ITEM_VALUE);

        if(_.isEmpty(mfMap)){
            return;
        }

        mfMap = JSON.parse(mfMap);

        _.each(mfMap, function(fValue, fKey){
            const field = mfItem.querySelector("[name$='" + fKey + "']");

            if(field == null){
                return;
            }

            setFieldValue(field, fValue);
        });
    }

    function setFieldValue(field, value){
        if( field.tagName == "CORAL-CHECKBOX"){
            field.checked = (field.getAttribute("value") == value);
        }else{
            field.value = value;
        }
    }

    function getVariation(){
        var variation = $(CFM_EDITOR_SEL).data('variation');

        variation = variation || "master";

        return variation;
    }

    function compositeMutifieldsExist(){
        return !_.isEmpty($(COMPOSITE_MF_SEL));
    }

    function extendRequestSave(){
        var orignFn = CFM.editor.Page.requestSave;

        CFM.editor.Page.requestSave = requestSave;

        function requestSave(callback, options) {
            orignFn.call(this, callback, options);

            if(!compositeMutifieldsExist()){
                return;
            }

            var mfsData = getMultifieldData();

            if(_.isEmpty(mfsData)){
                return;
            }

            var url = CFM.EditSession.fragment.urlBase + ".cfm.content.json",
                variation = getVariation(),
                createNewVersion = (options && !!options.newVersion) || false;

            var data = {
                ":type": "multiple",
                ":newVersion": createNewVersion,
                "_charset_": "utf-8"
            };

            if(variation !== MASTER){
                data[":variation"] = variation;
            }

            var request = {
                url: url,
                method: "post",
                dataType: "json",
                data: _.merge(data, mfsData),
                cache: false
            };

            CFM.RequestManager.schedule({
                request: request,
                type: CFM.RequestManager.REQ_BLOCKING,
                condition: CFM.RequestManager.COND_EDITSESSION,
                ui: (options && options.ui)
            })
        }
    }

    function getMultifieldData(){
        var $composites = $(COMPOSITE_MF_SEL), value,
            mfData = {}, values, $fields;

        _.each($composites, function(mField){
            values = [];

            _.each(mField.items.getAll(), function(item) {
                $fields = $(item.content).find("[name]");

                value = {};

                _.each($fields, function(field){
                    if(canbeSkipped(field)){
                        return;
                    }

                    value[getNameDotSlashRemoved(field.getAttribute("name"))] =  getFieldValue(field);
                });

                values.push(JSON.stringify(value));
            });

            mfData[ getNameDotSlashRemoved(($(mField)).attr(MF_NAME_ATTR))] = values;
        });

        return mfData;
    }

    function getFieldValue(field){
        var value;

        if( field.tagName == "CORAL-CHECKBOX"){
            value = field.checked ? field.getAttribute("value") : "";
        }else{
            value = field.value;
        }

        return value;
    }

    function canbeSkipped(field){
        return (($(field).attr("type") == "hidden") || (field.type == "checkbox"));
    }

    function getNameDotSlashRemoved(name){
        if(_.isEmpty(name)){
            return name;
        }

        const indexOfLastSlash = name.lastIndexOf("/");
		return (indexOfLastSlash === -1) ? name : name.substr(indexOfLastSlash + 1);
    }
}(jQuery));

7 replies

Asutosh_Jena_
Community Advisor
Community Advisor
September 7, 2021

Hi @kchaurasiya 

 

Here is a good article for Content Fragment with Multifield:

https://jimfrenette.com/aem/content-fragments/composite-multifield/

 

Thanks!

kchaurasiya
Level 5
September 7, 2021

@asutosh_jena_ Thanks Ashutosh for reply. Multifield i have already implemented in CF. I am looking for nested multifield in content fragment that is somehow working but not fully. For that we need to write the custom js code, I need that. Thanks

Level 2
January 17, 2023

@kchaurasiya 

Is This Solved Issue

Kishore_Kumar_
Level 9
September 7, 2021
kchaurasiya
Level 5
September 7, 2021

@kishore_kumar_ Thanks Kishor. I have already implemented multifield in component but  I need to implement in Content Fragment not in component. Thanks

Kishore_Kumar_
Level 9
September 7, 2021

Oh ok my bad sorry. I haven't tried customizing CF for nested multifield capability in 6.5. But if you can share us some sample code what you have achieved so far, me/someone may help to fix.

 

 

ibishika
Level 4
September 7, 2021

This is what you can try:

  • Add a content reference multifield in the first content fragment model.
  • Create a 2nd content fragment model and again add a multifield of required type there.
  • Reference the content fragments of second type from the first type

I think this would be the simpliest way to achieve this. May me you can try customizing as given in the links shared here in previous comments depending on your requirement. 

kchaurasiya
Level 5
September 7, 2021

@ibishika I have already implemented nested multi field in Content fragment but it is not working fully. Have u implemented it ??

prashantonkar
prashantonkarAccepted solution
Level 4
September 7, 2021

I have tried this once and had to do a lot of customization to get it working.

In your content fragment model, you need to make the change similar to this: ( i am just showing a part of CFM which I had made composite multifield.

<multifieldtest
	jcr:primaryType="nt:unstructured"
	sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
	composite="{Boolean}true"
	fieldLabel="Test Service"
	listOrder="1"
	maxlength="255"
	metaType="text-single"
	name="testSection"
	renderReadOnly="false"
	showEmptyInReadOnly="true"
	valueType="string[]">
	<field
		jcr:primaryType="nt:unstructured"
		sling:resourceType="granite/ui/components/coral/foundation/container"
		name="./testSection">
		<items jcr:primaryType="nt:unstructured">
			<column
				jcr:primaryType="nt:unstructured"
				sling:resourceType="granite/ui/components/coral/foundation/container">
				<items jcr:primaryType="nt:unstructured">
					<!-- your fields go heare -->
				</items>
			</column>
		</items>
	</field>
</multifieldtest>

After that, used the following code to populate the data into the fields:

(function ($) {
    var CFM = window.Dam.CFM,
        MASTER = "master",
        CFM_EDITOR_SEL = ".content-fragment-editor",
        CORAL_MF_ITEM = "coral-multifield-item",
        EAEM_COMPOSITE_ITEM_VALUE = "data-eaem-composite-item-value",
        MF_NAME_ATTR = "data-granite-coral-multifield-name",
        COMPOSITE_MF_SEL = "[data-granite-coral-multifield-composite]";

    CFM.Core.registerReadyHandler(getMultifieldsContent);

    extendRequestSave();

    function getMultifieldsContent(){
        if(!compositeMutifieldsExist()){
            return;
        }

        var url = CFM.EditSession.fragment.urlBase + "/jcr:content/data.2.json";

        $.ajax(url).done(loadContentIntoMultiFields);
    }

    function loadContentIntoMultiFields(data){
        var $composites = $(COMPOSITE_MF_SEL), mfValArr, mfAddEle,
            vData = data[getVariation()], $lastItem;

        if(_.isEmpty(vData)){
            return;
        }

        _.each($composites, function(mField){
            mfValArr = vData[getNameDotSlashRemoved(($(mField)).attr(MF_NAME_ATTR))];

            if(_.isEmpty(mfValArr)){
                return;
            }

            mfAddEle = mField.querySelector("[coral-multifield-add]");

            _.each(mfValArr, function(mfMap){
                mfAddEle.click();

                $lastItem = $(mField).find(CORAL_MF_ITEM).last();
                $lastItem.attr(EAEM_COMPOSITE_ITEM_VALUE, mfMap);
                Coral.commons.ready($lastItem[0], function (lastItem) {
                    fillMultifieldItems(lastItem);
                });

            });
        });
    }

    function fillMultifieldItems(mfItem){
        if(mfItem == null){
            return;
        }

        var mfMap = mfItem.getAttribute(EAEM_COMPOSITE_ITEM_VALUE);

        if(_.isEmpty(mfMap)){
            return;
        }

        mfMap = JSON.parse(mfMap);

        _.each(mfMap, function(fValue, fKey){
            const field = mfItem.querySelector("[name$='" + fKey + "']");

            if(field == null){
                return;
            }

            setFieldValue(field, fValue);
        });
    }

    function setFieldValue(field, value){
        if( field.tagName == "CORAL-CHECKBOX"){
            field.checked = (field.getAttribute("value") == value);
        }else{
            field.value = value;
        }
    }

    function getVariation(){
        var variation = $(CFM_EDITOR_SEL).data('variation');

        variation = variation || "master";

        return variation;
    }

    function compositeMutifieldsExist(){
        return !_.isEmpty($(COMPOSITE_MF_SEL));
    }

    function extendRequestSave(){
        var orignFn = CFM.editor.Page.requestSave;

        CFM.editor.Page.requestSave = requestSave;

        function requestSave(callback, options) {
            orignFn.call(this, callback, options);

            if(!compositeMutifieldsExist()){
                return;
            }

            var mfsData = getMultifieldData();

            if(_.isEmpty(mfsData)){
                return;
            }

            var url = CFM.EditSession.fragment.urlBase + ".cfm.content.json",
                variation = getVariation(),
                createNewVersion = (options && !!options.newVersion) || false;

            var data = {
                ":type": "multiple",
                ":newVersion": createNewVersion,
                "_charset_": "utf-8"
            };

            if(variation !== MASTER){
                data[":variation"] = variation;
            }

            var request = {
                url: url,
                method: "post",
                dataType: "json",
                data: _.merge(data, mfsData),
                cache: false
            };

            CFM.RequestManager.schedule({
                request: request,
                type: CFM.RequestManager.REQ_BLOCKING,
                condition: CFM.RequestManager.COND_EDITSESSION,
                ui: (options && options.ui)
            })
        }
    }

    function getMultifieldData(){
        var $composites = $(COMPOSITE_MF_SEL), value,
            mfData = {}, values, $fields;

        _.each($composites, function(mField){
            values = [];

            _.each(mField.items.getAll(), function(item) {
                $fields = $(item.content).find("[name]");

                value = {};

                _.each($fields, function(field){
                    if(canbeSkipped(field)){
                        return;
                    }

                    value[getNameDotSlashRemoved(field.getAttribute("name"))] =  getFieldValue(field);
                });

                values.push(JSON.stringify(value));
            });

            mfData[ getNameDotSlashRemoved(($(mField)).attr(MF_NAME_ATTR))] = values;
        });

        return mfData;
    }

    function getFieldValue(field){
        var value;

        if( field.tagName == "CORAL-CHECKBOX"){
            value = field.checked ? field.getAttribute("value") : "";
        }else{
            value = field.value;
        }

        return value;
    }

    function canbeSkipped(field){
        return (($(field).attr("type") == "hidden") || (field.type == "checkbox"));
    }

    function getNameDotSlashRemoved(name){
        if(_.isEmpty(name)){
            return name;
        }

        const indexOfLastSlash = name.lastIndexOf("/");
		return (indexOfLastSlash === -1) ? name : name.substr(indexOfLastSlash + 1);
    }
}(jQuery));
kchaurasiya
Level 5
September 7, 2021

@prashantonkar : Thanks for reply. I have tested multi-field the same way and it is working as expected in the Content Fragment.

 

But my question is related to nested-multi field.I think the code that you shared it seems its for multifield.

JS code that you shared so is it for multifield or nested-multifield???

 

Thank You.

Level 2
June 15, 2022

Hi @kchaurasiya,

 

Have you found any solution for the scenario you mentioned above, if so kindly share the solution so that we can try from our side.

 

Thank you

Level 2
December 21, 2021

Instead of using the following sling:resourceType , 

sling:resourceType
granite/ui/components/coral/foundation/form/multifield
granite/ui/components/foundation/form/multifield


use the resourceType : dam/cfm/admin/components/authoring/contenteditor/cfmmultifield

Adobe Employee
July 12, 2024

Hi @kchaurasiya  : were you able to achieve your requirement of nested multifield in CF via this solution . Kindly reply 

May 28, 2025

Hello old thread,

I managed to get this to work based on the blog spot article linked here for a composite multfield. I had to resort to some naming stipulations on the nested multifield, the field name and the included fields name must start with "nested_".

 

Here is my content fragment model

<?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" jcr:primaryType="cq:Template" allowedPaths="[/content/entities(/.*)?]" ranking="{Long}100"> <jcr:content cq:lastModified="{Date}2025-05-15T17:19:07.988-04:00" cq:lastModifiedBy="admin" cq:scaffolding="/conf/global/settings/dam/cfm/models/multifield-test/jcr:content/model" cq:templateType="/libs/settings/dam/cfm/model-types/fragment" jcr:primaryType="cq:PageContent" jcr:title="Multifield Testing" sling:resourceSuperType="dam/cfm/models/console/components/data/entity" sling:resourceType="dam/cfm/models/console/components/data/entity/default" status="enabled"> <metadata jcr:primaryType="nt:unstructured"/> <model cq:targetPath="/content/entities" jcr:primaryType="cq:PageContent" sling:resourceType="wcm/scaffolding/components/scaffolding" dataTypesConfig="/mnt/overlay/settings/dam/cfm/models/formbuilderconfig/datatypes" maxGeneratedOrder="20"> <cq:dialog jcr:primaryType="nt:unstructured" sling:resourceType="cq/gui/components/authoring/dialog"> <content jcr:lastModified="{Date}2025-05-15T17:19:07.988-04:00" jcr:lastModifiedBy="admin" jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"> <items jcr:primaryType="nt:unstructured" maxGeneratedOrder="23"> <_x0031_746608949250 jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textfield" fieldLabel="TItle" listOrder="1" maxlength="255" metaType="text-single" name="title" renderReadOnly="false" showEmptyInReadOnly="true" valueType="string"> <granite:data jcr:primaryType="nt:unstructured"/> </_x0031_746608949250> <_x0031_746807669152 jcr:primaryType="nt:unstructured" sling:resourceType="dam/cfm/models/editor/components/contentreference" fieldLabel="Title URL" filter="hierarchy" listOrder="10" metaType="reference" name="titleUrl" nameSuffix="contentReference" renderReadOnly="false" required="on" showEmptyInReadOnly="true" showThumbnail="false" valueType="string/reference"> <granite:data jcr:primaryType="nt:unstructured" thumbnail-validation="cfm.validation.thumbnail.show"/> </_x0031_746807669152> <!-- multifield, all fields that follow the tab placeholder will be placed in the multifield --> <_x0031_746615758359 jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/multifield" fieldLabel="Nav Links" listOrder="1" maxlength="255" metaType="text-single" name="menuLinksCMF" renderReadOnly="false" showEmptyInReadOnly="true" valueType="string[]"> <field jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textfield" maxlength="255" name="menuLinksCMF" renderReadOnly="false"> <granite:data jcr:primaryType="nt:unstructured"/> </field> <granite:data jcr:primaryType="nt:unstructured"/> </_x0031_746615758359> <_x0031_746615617490 jcr:primaryType="nt:unstructured" sling:resourceType="dam/cfm/models/editor/components/tabplaceholder" fieldLabel="menuLinksCMFTemplate" listOrder="14" metaType="tab-placeholder" name="placeholder" renderReadOnly="false" showEmptyInReadOnly="true" valueType="string/tab"/> <_x0031_746615406220 jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textfield" fieldLabel="Nav Link Title" listOrder="1" maxlength="255" metaType="text-single" name="navTitle" renderReadOnly="false" showEmptyInReadOnly="true" valueType="string"> <granite:data jcr:primaryType="nt:unstructured"/> </_x0031_746615406220> <_x0031_746615438589 jcr:primaryType="nt:unstructured" sling:resourceType="dam/cfm/models/editor/components/contentreference" fieldLabel="Nav Link Target" filter="hierarchy" listOrder="10" metaType="reference" name="navLink" nameSuffix="contentReference" renderReadOnly="false" showEmptyInReadOnly="true" showThumbnail="false" valueType="string/reference"> <granite:data jcr:primaryType="nt:unstructured" thumbnail-validation="cfm.validation.thumbnail.show"/> </_x0031_746615438589> <!-- Nested multifield, field name and item names must start with nested_ --> <_x0031_74661575833412341 jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/multifield" fieldLabel="Sub Nav Links" listOrder="1" maxlength="255" metaType="text-single" name="nested_menuLinks2CMF" renderReadOnly="false" showEmptyInReadOnly="true" valueType="string[]"> <field jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textfield" maxlength="255" name="nested_menuLinks2CMF" renderReadOnly="false"> <granite:data jcr:primaryType="nt:unstructured"/> </field> <granite:data jcr:primaryType="nt:unstructured"/> </_x0031_74661575833412341> <_x0031_747343850478 jcr:primaryType="nt:unstructured" sling:resourceType="dam/cfm/models/editor/components/tabplaceholder" fieldLabel="nested_menuLinks2CMFTemplate" listOrder="21" metaType="tab-placeholder" name="placeholder" renderReadOnly="false" showEmptyInReadOnly="true" valueType="string/tab"/> <_x0031_747343909062 jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textfield" fieldLabel="Sub Nav Link Title" listOrder="22" maxlength="255" metaType="text-single" name="nested_NavTitle" renderReadOnly="false" showEmptyInReadOnly="true" valueType="string"> <granite:data jcr:primaryType="nt:unstructured"/> </_x0031_747343909062> <_x0031_747343918597 jcr:primaryType="nt:unstructured" sling:resourceType="dam/cfm/models/editor/components/contentreference" fieldLabel="Sub Nav Link Target" filter="hierarchy" listOrder="23" metaType="reference" name="nested_NavLink" nameSuffix="contentReference" renderReadOnly="false" showEmptyInReadOnly="true" showThumbnail="false" valueType="string/reference"> <granite:data jcr:primaryType="nt:unstructured" thumbnail-validation="cfm.validation.thumbnail.show"/> </_x0031_747343918597> </items> </content> </cq:dialog> </model> </jcr:content> </jcr:root>

 

and here is my JavaScript

(function ($) { const URL = document.location.pathname, CFFW = ".coral-Form-fieldwrapper", MASTER = "master", CFM_EDITOR_SEL = ".content-fragment-editor", CMF_SELECTOR = "[data-granite-coral-multifield-name$='CMF']", NESTED_CMF_SELECTOR = "[data-granite-coral-multifield-name^='nested_']", CMF_TEMPLATE = "Template"; let initialized = false; if (!isCFEditor()) { return; } init(); function init() { if (initialized) { return; } initialized = true; window.Dam.CFM.Core.registerReadyHandler(() => { extendRequestSave(); addCMFMultiFieldListener(); Dam.CFM.editor.UI.addBeforeApplyHandler(() => { Dam.CFM.EditSession.notifyActiveSession(); Dam.CFM.EditSession.setDirty(true); }); }); } //Populates multfields with data on page load function addCMFMultiFieldListener() { const $cmfMultis = $(CMF_SELECTOR); //Add Multifields to Fragment form createMultiFieldTemplates(); //Populate first level multifields //Iterate through every multifield _.each($cmfMultis, (cmfMulti) => { //If this is not a nested multifield then attach to Coral Ready event if ( !$(cmfMulti) .attr("data-granite-coral-multifield-name") .startsWith("nested_") ) { Coral.commons.ready(cmfMulti, splitKeyValueJSONIntoFields); } }); //Populate nested multifields document.onreadystatechange = function () { if (document.readyState == "complete") { const $nestedCmfMultis = $(NESTED_CMF_SELECTOR); //Iterate through every nested multifield _.each($nestedCmfMultis, (cmfMulti) => { //Parse saved json data from first level multifield const $cmfMFField = $(cmfMulti), cmfMFName = $cmfMFField.attr( "data-granite-coral-multifield-name" ); let jsonData = $cmfMFField.attr("nested-mf-data"); if (!jsonData) { return; } jsonData = JSON.parse(jsonData); //Iterate through each multifield entry and set data let i = 0; _.each(cmfMulti.items.getAll(), function (item) { //Iterate throgh this multifield entry's fields and set their data _.each(jsonData[i], function (fValue, fKey) { const field = item.querySelector( "[name='" + fKey + "']" ); if (field == null) { return; } field.value = fValue; }); ++i; }); }); } }; } //Creates first level Multifield entries on form, parses multifield json and populates entries with data function splitKeyValueJSONIntoFields(cmfMFField) { const $cmfMFField = $(cmfMFField), cmfMFName = $cmfMFField.attr("data-granite-coral-multifield-name"); //Iterate through each multifield entry and set data _.each(cmfMFField.items.getAll(), function (item) { const $content = $(item).find("coral-multifield-item-content"); let jsonData = $content.find("[name=" + cmfMFName + "]").val(); if (!jsonData) { return; } jsonData = JSON.parse(jsonData); //Create multifield entry HTML on form $content.html(getParkedMFHtml($cmfMFField)); fillMultiFieldItem(item, jsonData); }); } //Populates first level multifield entry with json data function fillMultiFieldItem(mfItem, jsonData) { _.each(jsonData, function (fValue, fKey) { const field = mfItem.querySelector("[name='" + fKey + "']"); //If entry contains a nested multifield populate the necessary amount of entries and save json data as attribute, fields are populated with data after page load for nested multifield entries if (fKey.startsWith("nested_") && typeof fValue == "object") { const nestedMfItem = $(mfItem).find( "[data-granite-coral-multifield-name='" + fKey + "']" ); const addButton = $(nestedMfItem).find( "[coral-multifield-add]" )[0]; nestedMfItem.attr("nested-mf-data", JSON.stringify(fValue)); _.each(fValue, (nestedJsonData) => { addButton.click(); }); return; } if (field == null) { return; } if (field.tagName === "CORAL-DATEPICKER") { field.valueAsDate = new Date(fValue); } else { field.value = fValue; } }); } //Populate multifield templates on form html, templates are used when a user clicks the "add" button on a multifield, template fields come from the template tab in the fragment model. Hide template tab from fragment model. function createMultiFieldTemplates() { const $cmfMultis = $(CMF_SELECTOR); const $nestedCmfMultis = $(NESTED_CMF_SELECTOR); _.each($nestedCmfMultis, (nestedCmfMulti) => { let $nestedCmfMulti = $(nestedCmfMulti); $nestedCmfMulti.find("template").remove(); let nestedTemplate = '<template coral-multifield-template=""><div>' + getParkedMFHtml($nestedCmfMulti) + "</div></template>"; hideTemplateTab($nestedCmfMulti); $nestedCmfMulti.append(nestedTemplate); }); _.each($cmfMultis, (cmfMulti) => { if (!$(cmfMulti).attr("data-granite-coral-multifield-name").startsWith("nested_")) { let $cmfMulti = $(cmfMulti); $cmfMulti.find("template").remove(); let template = '<template coral-multifield-template=""><div>' + getParkedMFHtml($cmfMulti) + "</div></template>"; hideTemplateTab($cmfMulti); $cmfMulti.append(template); } }); } //Returns multifield fields html function getParkedMFHtml($cmfMulti) { let $tabView = $cmfMulti.closest("coral-tabview"); return $($tabView.find("coral-panel").get(getTemplateIndex($cmfMulti))) .find("coral-panel-content") .html(); } //Returns tab index for template tab corresponding with provided multifield function getTemplateIndex($cmfMulti) { let cmfMultiName = $cmfMulti.attr("data-granite-coral-multifield-name"), cmfMultiTemplateName = cmfMultiName + CMF_TEMPLATE, $tabView = $cmfMulti.closest("coral-tabview"), $tabLabels = $tabView.find("coral-tab-label"), templateIndex; _.each($tabLabels, (tabLabel, index) => { if ($(tabLabel).html().trim() == cmfMultiTemplateName) { templateIndex = index; } }); return templateIndex; } //Hide template tab from fragment model. function hideTemplateTab($cmfMulti) { let $tabView = $cmfMulti.closest("coral-tabview"); $($tabView.find("coral-tab").get(getTemplateIndex($cmfMulti))).hide(); } //Returns JSON data of current multifields value function getCompositeFieldsData() { const $cmfMultis = $(CMF_SELECTOR), allData = {}; _.each($cmfMultis, (cmfMulti) => { if ( !$(cmfMulti) .attr("data-granite-coral-multifield-name") .startsWith("nested_") ) { let $cmfMulti = $(cmfMulti), kevValueData = [], cmfName = $cmfMulti.attr( "data-granite-coral-multifield-name" ); _.each(cmfMulti.items.getAll(), function (item) { const $fields = $(item.content).find("[name]"), cmfData = {}; _.each($fields, function (field) { if ( canBeSkipped(field) || field.getAttribute("name").startsWith("nested_") ) { return; } cmfData[field.getAttribute("name")] = field.value; }); const $nestedCMFMultis = $(item.content).find(CMF_SELECTOR); _.each($nestedCMFMultis, function (nestedCMFMulti) { if ( $(nestedCMFMulti) .attr("data-granite-coral-multifield-name") .startsWith("nested_") ) { let $nestedCMFMulti = $(nestedCMFMulti), nestedKeyValueData = [], nestedCmfName = $nestedCMFMulti.attr( "data-granite-coral-multifield-name" ); _.each( nestedCMFMulti.items.getAll(), function (nestedItem) { const $nestedFields = $( nestedItem.content ).find("[name]"), nestedCmfData = {}; _.each( $nestedFields, function (nestedField) { if ( canBeSkipped(nestedField) || !nestedField .getAttribute("name") .startsWith("nested_") ) { return; } nestedCmfData[ nestedField.getAttribute("name") ] = nestedField.value; } ); nestedKeyValueData.push(nestedCmfData); } ); cmfData[nestedCmfName] = nestedKeyValueData; } }); kevValueData.push(JSON.stringify(cmfData)); }); allData[cmfName] = kevValueData; } }); return allData; } function canBeSkipped(field) { return $(field).attr("type") == "hidden" || !field.value; } //Save Multifield data to JCR node function extendRequestSave() { const CFM = window.Dam.CFM, orignFn = CFM.editor.Page.requestSave; CFM.editor.Page.requestSave = requestSave; function requestSave(callback, options) { orignFn.call(this, callback, options); const kvData = getCompositeFieldsData(); if (_.isEmpty(kvData)) { return; } const url = CFM.EditSession.fragment.urlBase + ".cfm.content.json", variation = getVariation(), createNewVersion = (options && !!options.newVersion) || false; let data = { ":type": "multiple", ":newVersion": createNewVersion, _charset_: "utf-8", }; if (variation !== MASTER) { data[":variation"] = variation; } const request = { url: url, method: "post", dataType: "json", data: _.merge(data, kvData), cache: false, }; CFM.RequestManager.schedule({ request: request, type: CFM.RequestManager.REQ_BLOCKING, condition: CFM.RequestManager.COND_EDITSESSION, ui: options && options.ui, }); } } function getVariation() { var variation = $(CFM_EDITOR_SEL).data("variation"); variation = variation || "master"; return variation; } function isCFEditor() { return ( URL.indexOf("/editor.html") == 0 || URL.indexOf( "/mnt/overlay/dam/cfm/admin/content/v2/fragment-editor.html" ) == 0 ); } })(jQuery);