Disclaimer : Ideas/Implementions provided in this blog express my personal view. Adobe or Me will not be held responsible for damage caused on your system because of information. Please don't try any suggestion in production system without proper testing. This is not an Adobe supported blog
AEM OOTB provides a simple multi field widget at /libs/granite/ui/components/foundation/form/multifield. But this widget can't handle the multiple fields.
Objective:
Create new composite multi field widget which displays each field(set) in a separate tab. The tabular separation of field sets helps to organize the fields easily rather than showing each field set one after the other vertically
You can download the package here
The component looks like:
The content after authoring looks like:
- Create the path /apps/authoring/touchui/widgets/tabular-multifield, with 'tabular-multifield' being sling:Folder node type
- On tabular-multifild, set sling:resourceSuperType to 'granite/ui/components/foundation/form/field'
- Create 'render.jsp' in 'tabular-mutlifield' with the following content
<%@include file="/libs/granite/ui/global.jsp" %><%
%><%@page session="false"
import="java.util.HashMap,
java.util.Iterator,
java.util.List,
java.util.ArrayList,
org.apache.sling.api.resource.ResourceResolver,
org.apache.sling.api.wrappers.ValueMapDecorator,
com.adobe.granite.ui.components.AttrBuilder,
com.adobe.granite.ui.components.ComponentHelper,
com.adobe.granite.ui.components.ComponentHelper.Options,
com.adobe.granite.ui.components.Config,
com.adobe.granite.ui.components.Field,
com.adobe.granite.ui.components.Tag,
com.adobe.granite.ui.components.Value" %><%--###
Example::
+ myinput
- name = "myTabularMultiField"
- sling:resourceType = "/apps/authoring/touchui/widgets/tabular-multifield"
+ field
- sling:resourceType = "granite/ui/components/foundation/container"
+ items
+ pathfield
- sling:resourceType = "granite/ui/components/foundation/form/pathbrowser"
- name = "./one"
+ textfield
- sling:resourceType = "granite/ui/components/foundation/form/textfield"
- name = "./two"
###--%>
<%
Config cfg = cmp.getConfig();
ValueMap vm = (ValueMap) request.getAttribute(Field.class.getName());
Tag tag = cmp.consumeTag();
AttrBuilder attrs = tag.getAttrs();
attrs.add("id", cfg.get("id", String.class));
attrs.addRel(cfg.get("rel", String.class));
attrs.addClass(cfg.get("class", String.class));
attrs.add("title", i18n.getVar(cfg.get("title", String.class)));
String name = cfg.get("name", String.class);
attrs.add("name", name);
attrs.addClass("tabular-Multifield");
attrs.add("data-init", "tabularmultifield");
attrs.addOthers(cfg.getProperties(), "id", "rel", "name", "class", "title", "fieldLabel", "fieldDescription", "renderReadOnly");
Resource field = cfg.getChild("field");
List<TabInfo> tabsInfo = getTabsInfo(request, resourceResolver, name);
%><div <%= attrs.build() %>><%
if (cfg.get("deleteHint", true)) {
for (TabInfo tabInfo : tabsInfo) {
%><input type="hidden" name="./<%= name != null ? name + "/" : "" + tabInfo.getNodeName() %>@Delete"><%
}
}
%>
<div class="coral-TabPanel" data-init="tabs">
<nav class="coral-TabPanel-navigation">
<%for (TabInfo tabInfo : tabsInfo) {%>
<a class="coral-TabPanel-tab" href="#" data-toggle="tab">
<span><%=tabInfo.getName()%><span>
<i class="close-tab-button js-tabular-Multifield-remove coral-Icon coral-Icon--close coral-Icon--sizeXS"></i>
</span></span>
</a>
<%}%>
<a class="coral-TabPanel-tab js-tabular-Multifield-add" href="#" data-target="#field-add" data-toggle="tab">
<i class="coral-Icon coral-Icon--add coral-Icon--sizeXS"></i>
</a>
</nav>
<div class="coral-TabPanel-content">
<%for (TabInfo tabInfo : tabsInfo) {%>
<section class="coral-TabPanel-pane" role="tabpanel">
<% include(field, tabInfo.getValues(), cmp, request); %>
</section>
<%}%>
<section class="dummy-section coral-TabPanel-pane" style="display:none">
</section>
</div>
</div>
<script class="js-tabular-Multifield-input-template" type="text/html"><% include(field, cmp, request); %></script>
</div><%!
private void include(Resource field, ComponentHelper cmp, HttpServletRequest request) throws Exception {
// include the field with no value set at all
ValueMap existingVM = (ValueMap) request.getAttribute(Value.FORM_VALUESS_ATTRIBUTE);
String existingPath = (String) request.getAttribute(Value.CONTENTPATH_ATTRIBUTE);
request.removeAttribute(Value.FORM_VALUESS_ATTRIBUTE);
request.removeAttribute(Value.CONTENTPATH_ATTRIBUTE);
cmp.include(field, new Options().rootField(false));
request.setAttribute(Value.FORM_VALUESS_ATTRIBUTE, existingVM);
request.setAttribute(Value.CONTENTPATH_ATTRIBUTE, existingPath);
}
private void include(Resource field, ValueMap map, ComponentHelper cmp, HttpServletRequest request) throws Exception {
ValueMap existing = (ValueMap) request.getAttribute(Value.FORM_VALUESS_ATTRIBUTE);
request.setAttribute(Value.FORM_VALUESS_ATTRIBUTE, map);
cmp.include(field, new Options().rootField(false));
request.setAttribute(Value.FORM_VALUESS_ATTRIBUTE, existing);
}
private List<TabInfo> getTabsInfo(HttpServletRequest request, ResourceResolver resolver, String name) throws Exception {
List<TabInfo> tabsInfo = new ArrayList<TabInfo>();
String contentPath = (String) request.getAttribute(Value.CONTENTPATH_ATTRIBUTE);
if (name != null) {
contentPath = contentPath + "/" + name;
}
Resource contentRes = resolver.getResource(contentPath);
if (contentRes != null) {
for (Iterator<Resource> it = contentRes.listChildren(); it.hasNext(); ) {
Resource r = it.next();
String rName = r.getName();
if (rName.startsWith("tab")) {
String tabIndex = rName.replace("tab", "");
String tabName = "Tab " + tabIndex;
ValueMap vm = r.adaptTo(ValueMap.class);
TabInfo tabInfo = new TabInfo(tabName, rName, vm);
tabsInfo.add(tabInfo);
}
}
}
return tabsInfo;
}
class TabInfo {
private String name;
private String nodeName;
private ValueMap values;
TabInfo(String name, String nodeName, ValueMap values) {
this.name = name;
this.nodeName = nodeName;
this.values = values;
}
String getName() {
return name;
}
String getNodeName() {
return nodeName;
}
ValueMap getValues() {
return values;
}
}
%>
2. Create "cq.authoring.dialog" clientlibs in /apps/authoring/touchui/widgets/tabular-multifield/clientlibs
- create js.txt in the clientlibs folder with the following content
#base=js tabular-multi-field.js
- create css.txt in the clientlibs folder with the following content
#base=css tabular-multi-field.css
- create tabular-multi-field.js file at /apps/authoring/touchui/widgets/tabular-multifield/clientlibs/js/tabular-multi-field.js with the following content
(function ($, console) {
"use strict";
var listTemplate =
"<div class=\"coral-TabPanel\" data-init=\"tabs\"><nav class=\"coral-TabPanel-navigation\"></nav><div class=\"coral-TabPanel-content\"></div></div>";
CUI.TabularMultifield = new Class({
toString: "TabularMultifield",
extend: CUI.Widget,
/**
@extends CUI.Widget
@classdesc A composite field that allows you to add/remove multiple instances of a component.
The component is added based on a template defined in a <code><script type="text/html"></code> tag.
The current added components are managed inside a <code>div.tabular-Multifield</code> element.
@desc Creates a TabularMultifield component.
@constructs
*/
construct: function (options) {
this.script = this.$element.find(".js-tabular-Multifield-input-template");
this.tabsContainer = this.$element.find(".coral-TabPanel").data("tabs");
var prevTabIndex = 0;
var tabs = this.tabsContainer._getTabs();
tabs.each(function() {
var $this = $(this);
var i = $this.text().trim().replace("Tab ", "");
if (!isNaN(i)) {
var index = parseInt(i);
if (index > prevTabIndex) {
prevTabIndex = index;
}
}
});
this.tabIndex = prevTabIndex === 0 ? 1: prevTabIndex + 1;
if (this.tabsContainer.length === 0) {
this.tabsContainer = $(listTemplate).prependTo(this.$element).find(".coral-TabPanel").data("tabs");
}
var tabularCompositeFieldName = this.$element.attr("name") || "";
if (tabularCompositeFieldName) {
tabularCompositeFieldName+= "/";
}
var panels = this.tabsContainer._getPanels();
var self = this;
panels.each(function(){
var $this = $(this);
var panelIndex = $this.index();
var tabNumber = $(tabs[panelIndex]).text().trim().replace("Tab ", "");
self.adjustInputNames($this, tabularCompositeFieldName + "tab" + tabNumber);
self._handleRadioButtons($this);
});
this._adjustMarkup();
this._addListeners();
},
_handleRadioButtons: function(item) {
item.find("input[type='radio']").each(function(){
var $this = $(this);
if ($this.attr("checked") == "checked") {
$this.attr("aria-selected", true);
$this.prop("checked", "checked");
}
});
},
/**
* Enhances the markup required for multifield.
* @private
*/
_adjustMarkup: function () {
this.$element.addClass("coral-Multifield");
},
/**
* Initializes listeners.
* @private
*/
_addListeners: function () {
var self = this;
var tabularCompositeFieldName = self.$element.attr("name") || "";
if (tabularCompositeFieldName) {
tabularCompositeFieldName+= "/";
}
//add
this.$element.on("click", ".js-tabular-Multifield-add", function (e) {
var item = $(self.script.html().trim());
self.adjustInputNames(item, tabularCompositeFieldName + "tab" + self.tabIndex);
self.handleCheckBoxesDefaultValue(item);
var tabCount = self.tabsContainer._getTabs().length;
var tabContent = "<span>Tab " + self.tabIndex + "<span> <i class=\"close-tab-button js-tabular-Multifield-remove coral-Icon coral-Icon--close coral-Icon--sizeXS\"></i>"
self.tabsContainer.addItem({tabContent:tabContent, panelContent:item, index:tabCount-1});
self.tabIndex++;
$(self.$element).trigger("cui-contentloaded");
});
//remove
this.$element.on("click", ".js-tabular-Multifield-remove", function (e) {
var $this = $(this);
var tabIndex = $(this).closest(".coral-TabPanel-tab.is-active").index();
self.tabsContainer.removeItem(tabIndex);
var tabCount = self.tabsContainer._getTabs().length;
if (tabCount === 1) {
self.tabIndex = 1;
}
});
},
handleCheckBoxesDefaultValue: function(item) {
item.find("input[type='checkbox'][data-check-in-multifield='true']").each(function(){
var $this = $(this);
$this.prop("checked", true);
});
},
adjustInputNames: function (item, prefix) {
item.find("[name]").each(function(){
var $this = $(this);
var name = $this.attr("name");
$this.attr("name", "./" + prefix + name.substr(1));
});
}
});
CUI.Widget.registry.register("tabularmultifield", CUI.TabularMultifield);
if (CUI.options.dataAPI) {
$(document).on("cui-contentloaded.data-api", function (e) {
CUI.TabularMultifield.init($("[data-init~=tabularmultifield]", e.target));
});
}
})(jQuery, window.console);
- create tabular-multi-field.css at /apps/authoring/touchui/widgets/tabular-multifield/clientlibs/css/tabular-multi-field.css with the following content
.tabular-Multifield a[data-toggle="tab"] i {
font-size: 0.5rem;
padding-right: 1rem;
padding-left: 0.5rem;
}
.tabular-Multifield a[data-toggle="tab"] {
text-transform: none;
}
Now you can use this multi field widget in authoring dialog, with the following example structure:
<tabular_multifield
jcr:primaryType="nt:unstructured"
sling:resourceType="/apps/authoring/touchui/widgets/tabular-multifield"
fieldLabel="My Tabular Multi Field"
name="tabularFields">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<one
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/pathbrowser"
fieldLabel="path browser"
name="./path"
rootPath="/content"/>
<two
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldLabel="text field"
name="./text"/>
<position
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/radiogroup"
fieldDescription="alignment"
fieldLabel="Choose"
text="Alignment">
<items jcr:primaryType="nt:unstructured">
<left
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/radio"
class="slide-item"
name="./overlayPosition"
text="Left"
value="left"/>
<center
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/radio"
checked="true"
class="slide-item"
name="./overlayPosition"
text="Center"
value="center"/>
<right
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/radio"
class="slide-item"
name="./overlayPosition"
text="Right"
value="right"/>
</items>
</position>
<checkbox
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/checkbox"
check-in-multifield="{Boolean}true"
fieldDescription="My Checkbox"
fieldLabel="checkbox field"
name="./mycheckBox"
text="My Checkbox"
value="checked"/>
</items>
</field>
</tabular_multifield>



Thank for shareing the valuable information. For more information visit our website.
ReplyDeleteUipath Training Institute In Ameerpet