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