Steps to create Composite Multi-Field:
Step1: Create a component having touch UI dialog with the following hierarchy:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Multifield"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/tabs"
type="nav"/>
<items jcr:primaryType="nt:unstructured">
<basic
jcr:primaryType="nt:unstructured"
jcr:title="Basic"
sling:resourceType="granite/ui/components/foundation/section">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<fieldset
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/fieldset">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<items
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/multifield"
fieldDescription="Click 'Add field' button to add a new field."
fieldLabel="Product List">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/fieldset"
eaem-nested=""
name="./items">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<title
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldLabel="Social Title"
name="./socialTitle"/>
<icon
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/select"
fieldLabel="Select Social Icon"
name="./socialIcon">
<items jcr:primaryType="nt:unstructured">
<select
jcr:primaryType="nt:unstructured"
text="Select Icon"/>
jcr:primaryType="nt:unstructured"
text="Facebook"
value="facebook"/>
jcr:primaryType="nt:unstructured"
text="Linkedin"
value="linkedin"/>
jcr:primaryType="nt:unstructured"
text="Twitter"
value="twitter"/>
<googlePlus
jcr:primaryType="nt:unstructured"
text="Google +"
value="google-plus"/>
</items>
</icon>
</items>
</column>
</items>
</field>
</items>
</items>
</column>
</items>
</fieldset>
</items>
</column>
</items>
</basic>
</items>
</content>
</jcr:root>
Step2: Create node "/apps/aem-learning/components/content/multifield/clientlibs" of type cq:ClientLibraryFolder and add a String property categories with value cq.authoring.dialog.
Step3: Create file (nt:file) "/apps/aem-learning/components/content/multifield/clientlibs/js.txt" and add the following js files:
Multifield.js
(function() {
var DATA_EAEM_NESTED = "data-eaem-nested";
var CFFW = ".coral-Form-fieldwrapper";
//reads multifield data from a server, creates the nested composite multifields and fills them
function addDataInFields() {
$(document).on("dialog-ready", function() {
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) {
var $field = $($multifield.find("[name='./" + key + "']")[index]);
$field.val(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 {
$field.val(rValue);
}
});
});
}
});
}
function fillValue($field, record) {
var name = $field.attr("name");
if (!name) {
return;
}
//strip ./
if (name.indexOf("./") == 0) {
name = name.substring(2);
}
record[name] = $field.val();
//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 the 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);
})();
This is how we create a Composite Multi-Field, But there are some common issues related to this component.
Below are some of the examples:
Issue 1: When we use the drop-down widget in Composite Multi-Field, Selected values are saved in node but when we re-open the dialog, saved values are not visible in the dialog box.
Fig - Multifield Values saved in node under the content hierarchy
A dropdown will look like below:
Fig - Saved Values not showing up in a drop-down
To solve this problem we need to update multifield.js
Go to Line 61: and replace the else section with the below code:
else{
var select = $field.closest(".coral-Select").data("select");
if(select){
select.setValue(rValue);
}
else{
$field.val(rValue);
}
}
Fig - Drop-down with the expected values
Issue 2: By default, the multifield container looks so weird as it is so small. Let's see how to beautify the look and feel of the same.
Fig- Container with small width
Steps to follow :
Add the "dialog-width-50rem" css class in the dialog hierarchy to content node ("/apps/aem-learning/components/content/multifield/cq:dialog/content" ) shown below-
Fig- Adding the class in content node
Add the "column-full-width" css class in the dialog hierarchy to content node ("/apps/aem-learning/components/content/multifield/cq:dialog/content/items/basic/items/column") shown below-
Fig- Adding the class in the column node
Create file (nt:file) "/apps/aem-learning/components/content/multifield/clientlibs/css.txt" and add the following CSS file
Multifield.css
.dialog-width-50rem{
width:50rem;
}
.coral--dark + .dialog-width-50rem{
width:100%;
}
.coral--dark + .dialog-width-50rem
.column-full-width{ width:60%;
}
.column-full-width{
width:95%;
}
.column-full-width
.coral-Form-fieldlabel{
width:15%;
}
Now you can see the updated layout of the dialog
Fig- Added the css to make the dialog Clearly
Refer to the package and try it yourself.
DEMO PACKAGE INSTALL
Issue 1: When we use the drop-down widget in Composite Multi-Field, Selected values are saved in node but when we re-open the dialog, saved values are not visible in the dialog box.
Fig - Multifield Values saved in node under the content hierarchy
A dropdown will look like below:
Fig - Saved Values not showing up in a drop-down
To solve this problem we need to update multifield.js
Go to Line 61: and replace the else section with the below code:
else{
var select = $field.closest(".coral-Select").data("select");
if(select){
select.setValue(rValue);
}
else{
$field.val(rValue);
}
}
Fig - Drop-down with the expected values
Issue 2: By default, the multifield container looks so weird as it is so small. Let's see how to beautify the look and feel of the same.
Fig- Container with small width
Steps to follow :
Add the "dialog-width-50rem" css class in the dialog hierarchy to content node ("/apps/aem-learning/components/content/multifield/cq:dialog/content" ) shown below-
Fig- Adding the class in content node
Add the "column-full-width" css class in the dialog hierarchy to content node ("/apps/aem-learning/components/content/multifield/cq:dialog/content/items/basic/items/column") shown below-
Fig- Adding the class in the column node
Create file (nt:file) "/apps/aem-learning/components/content/multifield/clientlibs/css.txt" and add the following CSS file
Multifield.css
.dialog-width-50rem{
width:50rem;
}
.coral--dark + .dialog-width-50rem{
width:100%;
}
.coral--dark + .dialog-width-50rem
.column-full-width{ width:60%;
}
.column-full-width{
width:95%;
}
.column-full-width
.coral-Form-fieldlabel{
width:15%;
}
Now you can see the updated layout of the dialog
Fig- Added the css to make the dialog Clearly
Refer to 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.