Goal
Adobe Experience Manager 2023.4.12142.20230526T152858Z-230200
Create a Content Fragment Model with Multiple Composite Multifields loaded in Content Fragments Editor. Authors can edit the configuration of a multifield or add additional composite multifields...
Demo | Package Install | CF Model | Github
Configure Composite Multifield - productsListCMFTemplate
Configure Composite Multifield - countriesListCMFTemplate
Composite Multifield in Editor - productsListCMF
Composite Multifield in Editor - countriesListCMF
Data Saved in CRX
Solution
1) Create the project eaem-cf-composite-mf
mvn -B org.apache.maven.plugins:maven-archetype-plugin:3.2.1:generate -D archetypeGroupId=com.adobe.aem
-D archetypeArtifactId=aem-project-archetype -D archetypeVersion=36 -D aemVersion=cloud
-D appTitle="Experience AEM CS CF Composite Multifield" -D appId="eaem-cf-composite-mf" -D groupId="apps.experienceaem.assets"
-D frontendModule=none -D includeExamples=n -D includeDispatcherConfig=n
2) Add a helper thirdparty loadash client library /apps/eaem-cf-composite-mf/lodash with property categories="[eaem.lodash]"
3) For the multiple composite multifields configuration and runtime create a clientlib /apps/eaem-cf-composite-mf/clientlib-mf with categories="[dam.cfm.authoring.contenteditor.v2]" and dependencies="[eaem.lodash]". Add the following code (note the conventions, composite multifield names must end with word CMF and template name with Multifield name and word Template)
(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']",
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);
});
});
}
function addCMFMultiFieldListener(){
const $cmfMultis = $(CMF_SELECTOR);
createMultiFieldTemplates();
_.each($cmfMultis, (cmfMulti) => {
Coral.commons.ready(cmfMulti, splitKeyValueJSONIntoFields);
})
}
function splitKeyValueJSONIntoFields(cmfMFField){
const $cmfMFField = $(cmfMFField),
cmfMFName = $cmfMFField.attr("data-granite-coral-multifield-name");
_.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);
$content.html(getParkedMFHtml($cmfMFField));
fillMultiFieldItem(item, jsonData);
});
}
function fillMultiFieldItem(mfItem, jsonData){
_.each(jsonData, function(fValue, fKey){
const field = mfItem.querySelector("[name='" + fKey + "']");
if(field == null){
return;
}
if(field.tagName === 'CORAL-DATEPICKER'){
field.valueAsDate = new Date(fValue);
}else{
field.value = fValue;
}
});
}
function createMultiFieldTemplates(){
const $cmfMultis = $(CMF_SELECTOR);
_.each($cmfMultis, (cmfMulti) => {
let $cmfMulti = $(cmfMulti);
$cmfMulti.find("template").remove();
let template = '<template coral-multifield-template=""><div>' + getParkedMFHtml($cmfMulti) + '</div></template>';
hideTemplateTab($cmfMulti);
$cmfMulti.append(template);
})
}
function getParkedMFHtml($cmfMulti){
let $tabView = $cmfMulti.closest("coral-tabview");
return $($tabView.find("coral-panel").get(getTemplateIndex($cmfMulti))).find("coral-panel-content").html();
}
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;
}
function hideTemplateTab($cmfMulti){
let $tabView = $cmfMulti.closest("coral-tabview");
$($tabView.find("coral-tab").get(getTemplateIndex($cmfMulti))).hide();
}
function getCompositeFieldsData(){
const $cmfMultis = $(CMF_SELECTOR), allData = {};
_.each($cmfMultis, (cmfMulti) => {
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)){
return;
}
cmfData[field.getAttribute("name")] = field.value;
});
kevValueData.push(JSON.stringify(cmfData));
});
allData[cmfName] = kevValueData;
})
return allData ;
}
function canBeSkipped(field){
return (($(field).attr("type") == "hidden") || !field.value);
}
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));
Hi Sreekanth,
ReplyDeleteThank you so much for this blog post, it's working just fine except for the rte and checkbox (Multi line text and Boolean from the CFM Editor). Do you have a fix for that?
Thanks!
I am having issues when i make changes to composite multifield and then refresh the content fragment multiple times. After 3-4 refresh I notice that data gets updated to a different value which is not a JSON. Any reason why?
ReplyDeletethis solution is not working in AEMaaCS Content Fragment New editor.
ReplyDelete