In Part-1 of Custom Multifield, we have gone through some of the issues of "Custom Multifield", but there are two more issues still persist. In this blog we will see how to handle them.
Issue 1: When we use checkbox widget in Composite Multi-Field, It doesn’t work properly.
- In below dialog "Open in new tab" checkbox will be unchecked on re-opening the dialog box in-spite of the fact that it was checked or unchecked earlier.
Fig- Not showing the saved value in node |
- In multifield, it doesn’t matter whether you select or deselect the values, the value of the checkbox “_target” will always be saved in the repository.
Fig- Always saving the value in node even checkbox is not selected |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function ($, $document) { | |
var DATA_EAEM_NESTED = "data-eaem-nested"; | |
var CFFW = ".coral-Form-fieldwrapper"; | |
function isSelectOne($field){ | |
return !_.isEmpty($field) && ($field.prop("type") === "select-one"); | |
} | |
function setSelectOne($field, value){ | |
var select = $field.closest(".coral-Select").data("select"); | |
if(select){ | |
select.setValue(value); | |
} | |
} | |
function isCheckbox($field){ | |
return !_.isEmpty($field) && ($field.prop("type") === "checkbox"); | |
} | |
function setCheckBox($field, value){ | |
$field.prop( "checked", $field.attr("value") == value); | |
} | |
function setWidgetValue($field, value){ | |
if(_.isEmpty($field)){ | |
return; | |
} | |
if(isSelectOne($field)){ | |
setSelectOne($field, value); | |
}else if(isCheckbox($field)){ | |
setCheckBox($field, value); | |
}else{ | |
$field.val(value); | |
} | |
} | |
//reads multifield data from server, creates the nested composite multifields and fills them | |
function addDataInFields() { | |
$(document).on("dialog-ready", dlgReadyHandler); | |
function dlgReadyHandler() { | |
var $fieldSets = $("[" + DATA_EAEM_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){ | |
setWidgetValue($($multifield.find("[name='./" + key + "']")[index]), value); | |
}) | |
}) | |
} | |
function buildMultiField(data, mName){ | |
if(_.isEmpty(mName)){ | |
return; | |
} | |
$fieldSets = $("[data-name='" + mName + "']"); | |
//strip ./ | |
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{ | |
var select = $field.closest(".coral-Select").data("select"); | |
if(select){ | |
select.setValue(rValue); | |
} | |
else{ | |
setWidgetValue($field, rValue); | |
} | |
} | |
}); | |
}); | |
} | |
} | |
} | |
function fillValue($field, record){ | |
var name = $field.attr("name"), value; | |
if (!name) { | |
return; | |
} | |
//strip ./ | |
if (name.indexOf("./") == 0) { | |
name = name.substring(2); | |
} | |
value = $field.val(); | |
if( isCheckbox($field) ){ | |
value = $field.prop("checked") ? $field.val() : ""; | |
} | |
record[name] = value; | |
//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 $form = $(this).closest("form.foundation-form"); | |
var $fieldSets = $("[" + DATA_EAEM_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('name', $(fieldSet).data("name")) | |
.attr('value', JSON.stringify(record)) | |
.appendTo($form); | |
}); | |
}); | |
} | |
$document.ready(function () { | |
addDataInFields(); | |
collectDataFromFields(); | |
}); | |
//extend otb multifield for adjusting event propagation when there are nested multifields | |
//for working around the nested multifield add and reorder | |
CUI.CustomMultifield = 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.CustomMultifield); | |
}(jQuery, jQuery(document))); |
Fig - Showing the correct checkbox status |
Fig - Correct value saved in node |
Issue 2: None of the property is available out of the box to restrict the size of multifield.
To solve this issue:
- Add the limit property in node which is having sling:resourceType as "granite/ui/components/foundation/form/multified" .The value corresponds to limit attribute will denote the number of multifield items allowed. Let's say limit is set to 3 then only 3 multifield values will be allowed and for 4th value it will show an alert with the respective error message.
Fig- Add limit property to multifield node |
- Create file (nt:file) "/apps/aem-learning/components/content/multifield/clientlibs/js/limit.js" and add the following JS fileThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
$(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", "Maximum " + size + " links are allowed!", "notice"); return false; } } }); });
Fig - Multifield size limit cross |
Refer below package and try it yourself.
Demo Package Install
Hope it will help you guys !!👍
Thanks and Happy Learning 😊
I have to agree with everything in this post. Thanks for the useful information.
ReplyDeleteStruts Training in Chennai
Struts Training
Wordpress Training in Chennai
Wordpress Training
Wordpress Training in Velachery
Wordpress Training in Tambaram
Struts Training
Struts Training in Chennai
I have updated my custom multifield.js with the one that you shared above but it's still not working for me.
ReplyDeleteWhich AEM Version you are using?
Delete6.3
Delete6.3
ReplyDeleteI tried it in AEM 6.3 previously and it used to work... try to use exact package of mine...
ReplyDeleteI using aem 6.3.3. I have installed your package , still it was not working and thae values are not saving inside dilog also.
ReplyDelete