March 29, 2020
Estimated Post Reading Time ~

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

In this blog, we will see how to handle the issues of "Custom Multifield".

Issue 1: When we use the checkbox widget in Composite Multi-Field, It doesn’t work properly.
In the 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
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 the 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);
                     //maybe 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 a 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 a 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)));

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).





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 the 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 the limit attribute that will denote the number of multifield items allowed. Let's say the 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 the 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; } } }); });

You can see the alert if you will add more items than the limit :

Fig - Multifield size limit cross 


And this is how we can customize the multifield accordingly.
Refer below the package and try it yourself.
Demo Package Install


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.