Content as a Service has revolutionized content management systems in the delivery of content. Content is created once, independent of channels, and delivered without additional custom application code. This saves a lot of time in the creation and maintenance of content and reduces the cost of operation.
Adobe Experience Manager (AEM) has out-of-the-box (OOTB) features like Content Services, Assets HTTP API, and Content Fragment. These features help marketers create, maintain, and publish content for multiple channels like web pages, mobile apps, IoT devices, screens, and many more.
For understanding the basics of Content Fragments, please do read Working with Content Fragments.
The OOTB Content Fragment Data Types are basic widgets with specific UI behavior. For marketers, the basic behavior will not fulfill the authoring requirement. Thankfully, AEM is flexible so we can extend the UI behavior of the basic data types with ease to create new data types.
Create Multi-Select Dropdown
In this blog, I am going to extend the Enumeration data type to create a new Multi-Select Dropdown data type.
1. Overlay OOTB Enumeration Data Type
Overlay the OOTB Enumeration Data Type from libs (/libs/settings/dam/cfm/models/formbuilderconfig/datatypes/items) folder to apps (/apps/settings/dam/cfm/models/formbuilderconfig/datatypes/items) folder. See the below image:
2. Customize the Enumeration
After overlaying the OOTB node, change the node name to enumerationmultiselect. Along with the default attributes present on the node, add tagsfield value in fieldProperties. Tagsfield points to the OOTB /libs/dam/cfm/models/editor/components/datatypeproperties/tagsfields/tagsfields.jsp data type which provide multiple select behavior. See the below image:
3. Add Custom Behavior
The following steps add custom HTML to the generated DOM element which will be used to read and save the selected values in the multiselect. To achieve this overlay /libs/dam/cfm/models/editor/components/datatypeproperties folder to /apps as shown in the below image:
Create a JSP file uniqueidfield.jsp, below uniqueidfield folder, and add below code snippet. The below code creates the hidden element to the HTML DOM element:
<%@ page import="org.apache.sling.api.resource.ValueMap" %> <% %><%@include file="/libs/granite/ui/global.jsp" %><% %><%@ page session="false" contentType="text/html" pageEncoding="utf-8"%><% ValueMap fieldProperties = resource.adaptTo(ValueMap.class); String key = resource.getName(); String xssKey = xssAPI.encodeForHTMLAttr(key); %> <input type="hidden" name="./content/items/<%= xssKey %>/granite:class" value="enumeration-multi-select">
Add the newly created datatype property to the enumerationmultiselect node in fieldProperties attribute as shown below image:
4. Save Selected Values
By default, the values selected in the multi-select value will not be saved to the fragment. To achieve this, we need to add custom JavaScript code. In your custom application code, under the clientlibrary folder, add a new folder clientlib-author and add category dam.cfm.authoring.contenteditor.v2 as shown in the below image:
Create a new JS file named enumerationmultiselect.js add it to the js.txt file under the clientlib-author folder. Add the below code snippet to the JS file:
(function ($) { 'use strict'; var CFM, MASTER = "master", CFM_EDITOR_SEL = ".content-fragment-editor"; $( window ).load(function() { if (window.Dam != undefined) { CFM = window.Dam.CFM; getEnumerationContent(); var url = window.location.pathname; if(url.indexOf("/metadata-editor.html/") < 0) { extendRequestSave(); } } }); function getEnumerationContent() { if (CFM.EditSession != undefined) { var url = CFM.EditSession.fragment.urlBase + "/jcr:content/data.2.json"; $.ajax(url).done(loadContentIntoEnumerationContentField); } } function loadContentIntoEnumerationContentField(data) { var coralSelect = $('#aem-cfm-editor-elements-form .enumeration-multi-select'); $.each(coralSelect, function() { var name = $(this).attr('name'); var selectValue = data.master[name].split(','); setSelectValue($(this), selectValue); var tagList = getTagList(selectValue); $(this).find('.coral3-Select-tagList').append(tagList); }); } function setSelectValue(eachSelect, selectValue) { var selectedItem = $(eachSelect).find('coral-select-item'); $.each(selectedItem, function() { var value = $(this).attr('value'); if (selectValue.includes(value)) { $(this).attr('selected', ""); } }); } function getTagList(selectValue) { var html = ""; $.each(selectValue, function(index, value) { html = html + "<coral-tag value=\"" + value + "\">" + value + "</coral-tag>"; }); return html; } function getVariation() { var variation = $(CFM_EDITOR_SEL).data('variation'); variation = variation || "master"; return variation; } function getFormData() { var coralSelect = $('#aem-cfm-editor-elements-form .enumeration-multi-select'); var value = {}; $.each(coralSelect, function() { var tags = $(this).find('.coral3-Select-tagList .coral3-Tag'); var name = $(this).attr('name'); var values = []; $.each(tags, function() { var tagValue = $(this).val(); values.push(tagValue); }); value[name] = values.join(); }); return value; } function extendRequestSave(){ if (CFM.editor != undefined) { var originFn = CFM.editor.Page.requestSave; CFM.editor.Page.requestSave = requestSave; function requestSave(callback, options) { originFn.call(this, callback, options); var formData = getFormData(); if(formData.length == 0){ return; } var url = CFM.EditSession.fragment.urlBase + ".cfm.content.json", variation = getVariation(), createNewVersion = (options && !!options.newVersion) || false; formData[":type"] = "multiple"; formData[":newVersion"] = createNewVersion; formData["_charset_"] = "utf-8"; if(variation !== MASTER){ formData[":variation"] = variation; } var request = { url: url, method: "post", dataType: "json", data: formData, cache: false }; CFM.RequestManager.schedule({ request: request, type: CFM.RequestManager.REQ_BLOCKING, condition: CFM.RequestManager.COND_EDITSESSION, ui: (options && options.ui) }) } } } }(jQuery));
Create A Content Fragment Model
Now time to validate. Navigate to the Content Fragment Model console and you will see the new Enumeration Multi Select Data Type as showing in the below image:
Create a simple model by adding the drag and dropping the new Enumeration Multi Select Data Type and then click Save. With the new content fragment model, create a content fragment. We can then select multiple values from the enumeration.
The below video shows the behavior of the new Enumeration Multi-Select behavior on the content fragment.
For more information on this topic and related items, you can also read the below blogs. The below blogs will provide more ideas to customize content fragment model data types:
Informative Blog. Can we add multi tabs like the way we add it in dialog?
The component gets created and shows up in content fragment model creation but won’t appear when creating content fragment
I am afraid we cannot create a tab structure in content fragments. If you really want to do that, you might need to find a way to add a root panel that behaves like a tab structure.
The field doesn’t show in the created content fragment or the model is not appearing while creating a new content fragment. Which way it is?
@Rajat, have you tried this in AEM 6.5 ? I followed the exact steps you suggested however overlaying /libs/dam/cfm/models/editor/components/datatypeproperties is not allowed in AEM 6.5. And I didn’t get about the tab structure you’re talking about.
I mean to ask that when you use the multi select enumeration field in content fragment it just doesn’t show up the UI there like you have added in gif
@Shivam, I have it working in 6.5 but the customization was done in 6.4 and it still works in 6.5.
@Rajat when I try to do the step Add Custom Behavior, it doesn’t let you overlay node so I manually created those folders and I followed rest all steps as shown however to me the Widget is not appearing.
Also can you share the location where when doing the step:
Create a new JS file named enumerationmultiselect.js add it to the js.txt file under the clientlib-author folder. Add the below code snippet to the JS file:
you add these files in exactly which clientlib-author? Can you share the complete address for the same?
Hi @Rajat, I followed your steps to create a multiple-select dropdown, it appears in Content Fragment Model Editor, however when I drag and drop it, the “Properties” tab at top-right became empty, after click “Save”, it disappeared.
The “fieldProperties” are:
labelfield
maptopropertyfield
optionsmultifield
placeholderfield
requiredfield
uniquefield
tagsfield
/apps/dam/cfm/models/editor/components/datatypeproperties/uniqueidfield/uniqueidfield.jsp
The 1st six properties are default. I am using AEM 6.5. I can send screenshots if there is a way.
Any idea?
Thanks
Hi guys. For the ones who have the problem with the Properties not visible on the UI, you must add to the enumerationmultiselect node this property: fieldMetaTypes String[] enumerationmultiselect. The metatype must be the same like the node name. Unfortunately after this the multi select does still not work, and I was not able to figure it out why, and another strange thing is that the content fragment model created with this custom component is disappearing from time to time, so it’s unreliable. And unfortunately I don’t have any more time to spend on it, although it would have been great if it would have worked