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
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
(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)));
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
No comments:
Post a Comment
If you have any doubts or questions, please let us know.