Ad Code

Wednesday, April 5, 2017

Touch UI Composite Multi-field with CheckBox Issue in AEM 6.2


Hello Everyone,

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.
checkbox.PNG
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.
^5FAA1604936855B9E087171B954A51B6D3128746BD378448A6^pimgpsh_fullsize_distr.jpg
Fig- Always saving the value in node even checkbox is not selected

To fix this we need to update the "Multifield.js" for the same.

(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)));
view raw Multifield.js hosted with ❤ by GitHub
Once the above js is updated you can check the "Open in new tab checkbox" and the correct values will be stored in the repository(node).
^086E03CEF65B2975A334E79817DE7AB66196F7AD841B1B4F9F^pimgpsh_fullsize_distr.jpg
Fig - Showing the correct checkbox status

^4AF3775DC7230CCAD1D2B8F2189C61DC36A8B897BAAA17155A^pimgpsh_fullsize_distr.jpg
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.

Capture.PNG
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 file
    $(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;
    }
    }
    });
    });
    view raw limit.js hosted with ❤ by GitHub
You can see the alert if you will add more items than the limit :

Capture.PNG
Fig - Multifield size limit cross
And this is how we can customised the multifield accrodingly.

Refer below package and try it yourself.
Demo Package Install


If you have any query or suggestion then kindly comment or mail us at sgaem.blog02@gmail.com.

Hope it will help you guys !!👍
Thanks and Happy Learning 😊

7 comments:

  1. I have updated my custom multifield.js with the one that you shared above but it's still not working for me.

    ReplyDelete
  2. I tried it in AEM 6.3 previously and it used to work... try to use exact package of mine...

    ReplyDelete
  3. I 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