Select All and De-Select All fields in Dropdown using Coral UI JavaScript in AEM

In this article we will try out a custom Coral UI Javascript in AEM and all a functionality to a dropdown where, dropdown will have n number of values and will have Select All and De-select All option as well in the dropdown. This is really helpful when you have lot’s of options in a dropdown and you have to select all and de-select all.

First create a simple AEM dropdown field with multiselect functionality and add some 5-6 options in the dropdown and then add two option with following text and value combination which will help us select all fields in dropdown as well as de-select all fields in dropdown.


NOTE: The name property used in this article is ./country for the dropdown.



Select All option:

text: Select All (You can keep any name as it just shows on the UI).
value: all (We are keeping all here because we will be using the same value in JS)

Deselect All option:

text: De-Select All (You can keep any name as it just shows on the UI).
value: none (We are keeping none here because we will be using the same value in JS)

Now create a clientlibrary in AEM with category name as cq.myproject.select-deselect. And then attach this clientlib at the cq:dialog node of the component by adding the following property at cq:dialog node.

extraClientlibs – String – cq.myproject.select-deselect

Once we are done setting up everything then paste the following code in your JS file of the clientlibs and checkout the functionality in the dropdown.

(function (document, $){
"use strict";

$(document).on("foundation-field-change", "coral-select[name='./country']", function (e) {
let postSelect = $("coral-tag[aria-selected='true']").val();
let allDropdownselect=($(this).Find("coral-selectlist-item(value='all']")).is(':selected');
let nodropdownSelect = ($(this).find("coral-selectlist-item[value='none']")).is(':selected');
        if (!!allDropdownselect  && !postSelect) {
            $(this) .find("coral-selectlist-item[value! ='none']").attr("selected", true);
            $(this). find("coral-tag[value='all']"). remove();
        }
        if (!!nodropdownSelect  && !postSelect) {
            $(this).find("coral-selectlist-item[value!='none']").attr("selected", false);
            $(this).find("coral-tag[value='none' ]") .remove();
        }
    });
}) (document, Granite.$);



Also if you want to customise the above Coral UI JS as per your name property and dropdown options then you need to update the following props.

Field NamePurpose
./countryUsed as the name property of the dropdown field
allUsed as the value of Select All dropdown field
noneUsed as the value of De-Select All dropdown field

Configuring Maven in MacOS for AEM Build

In this article we will see how we can setup java and maven in our MacOS so that we can give a successful build of our AEM Archetype project.

As we know we can easily setup the Environmental variables in windows but the process in MacOS is bit different.

Firstly we need to simple install the Java 11 using the .dmg file installer which you can get at https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html.



And once you are done installing the Java 11 successfully you can check for the Java version in your terminal using java -version and check if it shows the correct Java version.

Now download the latest or required maven version from https://maven.apache.org/download.cgi and unzip it at any of your local system location. (In my case I have kept it at /Users/NikhilKumar/Downloads/apache-maven-3.9.0).




Now once you are done with the above placement of the unzipped apache-maven-3.9.0 folder, you can open a new terminal which point to the users location (in my case it’s /Users/NikhilKumar).


Now next thing is to check for /.bash_profile file in the users path or you can simply open the terminal of macOs and type vi ~/.bash_profile. Now you will get an empty vi editor and now just add the below set of lines which will set the maven path to the path of the dowloaded maven version.

export M2_HOME="/Users/NikhilKumar/Downloads/apache-maven-3.9.0"
PATH="${M2_HOME}/bin:${PATH}"
export PATH

Now once you are done adding the above code just press Esc then Shift + Colon(:) and then type wq and press enter. (This means we are saving the piece of lines that we have added in bash_profile).

Once you are done with above steps you can either re-open a new terminal to check for maven version or just do source .bash_profile and then check for maven version and it should show you the corresponding maven version.

Now we can go ahead to our AEM Maven project and give a maven build and it should successfully download the dependencies and build should be success.

Thank you for reading.
Happy Coding!

Extending Core Component’s Model and Creating it’s new Implementation | AEM

The Core Components implement several patterns that allow easy customization, from simple styling to advanced functionality reuse.

The Core Components were designed from the beginning to be flexible and extensible. A look at an overview of their architecture reveals where customizations can be made.

Now let’s see how we can customise our AEM Core components business logic. We will try to customise the OOTB image core component. And the customisation that we are going to do is that, if image alt text is not authored, we will pick up the image alt text from the image name.

@Model(adaptables = SlingHttpServletRequest.class,
       adapters = Image.class,
       resourceType = "myproject/components/image")
public class ExtendedImageComponent implements Image {
   

   String alt;

   @ValueMapValue
   String altText;

   @ValueMapValue
   String fileReference;

    @Self @Via(type = ResourceSuperType.class)
    private Image image;

    @Override 
    public String getAlt() {
        return alt;

    }

    public Image getImage(){
        return image;
    }

    @PostConstruct
    protected void init(){

        if(null == altText && null!= fileReference){
            String [] imgPathArray = fileReference.split("/");
            alt = imgPathArray[imgPathArray-1];
        }

    }
  
}

In above java class we have used the adapter for Image.class and implemented the OOTB functionality for Image Component and using @Self annotation we have created a object of Image class so that we will go ahead and update the image.{{variable}} with model.image.{{variable}} to load the untouched functionality of the OOTB java class. whereas the customized altText here can be referred by model.alt.

No we can go ahead and replace the data-sly-use.image=”com.adobe.cq.wcm.core.components.models.Image” with data-sly-use.model=”com.myproject.core.components.models.ImageComponent”.

Now we can replace the custome altText which was image.alt to model.alt and rest of the image variable with model.image.{{variable}}.


Thank you for reading.
Happy Coding!

Fetching Multifield data of AEM Dialog in HTL using Single Java Class

If you are looking for an article to create a multifield and fetch it’s data in HTL using just single Java class then you are at the right place. So let’s go ahead and start the creation of a multifield in dialog and then use a java class to fetch the data and then render it on the HTML.

Below is the dialog.xml for the multifield which is of type composite which means the data that you enter in the fields of multifield will be saved in different nodes in content hierarchy i.e, item 0, item 1, and so on.

Data saved as part of content
cq:dialog structure in crx/de
<?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"
    helpPath="https://www.adobe.com/go/aem_cmp_text_v2">
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
            <tabs
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/tabs"
                maximized="{Boolean}true">
                <items jcr:primaryType="nt:unstructured">
                    <details
                        cq:showOnCreate="{Boolean}false"
                        jcr:primaryType="nt:unstructured"
                        jcr:title="Details"
                        sling:resourceType="granite/ui/components/foundation/container">
                        <items jcr:primaryType="nt:unstructured">
                            <container
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/foundation/container">
                                <items jcr:primaryType="nt:unstructured">
                                    <cards
                                        jcr:primaryType="nt:unstructured"
                                        jcr:title="Details"
                                        sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
                                        composite="{Boolean}true"
                                        fieldLabel="Details">
                                        <field
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/container"
                                            name="./details">
                                            <items jcr:primaryType="nt:unstructured">
                                                <title
                                                    jcr:primaryType="nt:unstructured"
                                                    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                                    fieldLabel="Title"
                                                    name="./title"/>
                                                <link
                                                    jcr:primaryType="nt:unstructured"
                                                    sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
                                                    fieldLabel="Link"
                                                    name="./link"/>
                                            </items>
                                        </field>
                                    </cards>
                                </items>
                            </container>
                        </items>
                    </details>
                </items>
            </tabs>
        </items>
    </content>
</jcr:root>

Now lets create our Model Class and try to fetch the values of the multifield. Also follow the comments mentioned in the Java class to identify how fields are fetched and used.

package com.myproject.core.models;

import javax.annotation.PostConstruct;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.sling.api.resource.Resource;

import org.apache.sling.api.SlingHttpServletRequest;

import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;

import org.apache.sling.models.annotations.injectorspecific.SlingObject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Model(adaptables = SlingHttpServletRequest.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class MultiDataFetcher {

	private static final Logger log = LoggerFactory.getLogger(SupportCarousel.class);

	@SlingObject
	private Resource componentResource;     //Used to get the current resource of the component

	private List<Map<String, String>> details = new ArrayList<>();

	public List<Map<String, String>> getMultiDataMap() {

		return details;

	}

	@PostConstruct
	protected void init() {

		Resource supportPoints = componentResource.getChild("details");  //now we will get the details resource which holds the data in different item node.
		if (supportPoints != null) {
			for (Resource supportPoint : supportPoints.getChildren()) {

				Map<String, String> detailsMap = new HashMap<>();
				detailsMap.put("title",
						supportPoint.getValueMap().get("title", String.class));     // Saving the property into a map, which will be fetched in HTML.
				detailsMap.put("link",
						supportPoint.getValueMap().get("link", String.class));

				details.add(detailsMap);
			}
		}

	}

}

Now once we are ready with out List which holds the data of the multifield, we will now go ahead and render the data in the HTML

<div data-sly-use.multiDataFetcher="${'com.myproject.core.models.MultiDataFetcher'}">
                <sly data-sly-list.item="${multiDataFetcher.multiDataMap}">
                                ${item.title}
        ${item.link}
                </sly>
</div>
  1. Initially we are calling the Java Sling Model using data-sly-use and creating an object of the class.
  2. Then we are using data-sly-list to get the items of the List.
  3. Now we can print out the specific field that we want to render using item.title or item.link.

NOTE: title and link are the two fields which are part of the multifield in dialog.

Try using itemList.index to render the position of the data in the list and this counter starts from 0 similar to arrays in Java.

Thank you for reading.
Happy Coding!

Calling POST Servlet using resourceTypes,selectors and extensions in AEM

Saw some query on Adobe community forum on how to make a POST call to a servlet using sling.servlet.resourceTypes and using selectors. So here I am with my short article on how we can call a POST servlet using the same.

Creating a resource in crx:
Let’s say I have a node in AEM crx at /etc/mycompany/paths/states and add the resourceType on states node i.e mycompany/servlets/states


Once we are done creating the resource in CRX, we will create a corresponding JAVA class and define this resourceType along with selectors and extension to load the data.

@Component(service = Servlet.class, property = {
		Constants.SERVICE_DESCRIPTION + "=" + "Servlet to get the States details",
		ServletResolverConstants.SLING_SERVLET_METHODS + "=" + HttpConstants.METHOD_POST,
		ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES + "=" + "mycompany/servlets/states",
		ServletResolverConstants.SLING_SERVLET_SELECTORS + "=" + "api.getStates",
		ServletResolverConstants.SLING_SERVLET_EXTENSIONS + "=" + "json" })
public class GetStateDetailsServlet extends SlingAllMethodsServlet {

	private static final long serialVersionUID = 1L;

	private static final Logger LOG = LoggerFactory.getLogger(GetStateDetailsServlet.class);

	@Override
	protected void doPost(final SlingHttpServletRequest request, final SlingHttpServletResponse response) {
		
				response.getWriter().println("Hello World!!");
			
	}
}



Once we are done creating the corresponding servlet for using below property:

1. sling.servlet.resourceTypes : We provide the resourceType of the CRX node.
2. sling.servlet.selectors : We provide the selectors for our servlet URL.
3. sling.servlet.extensions : We provide the extension which we want to use along with the selectors to load the response.

So, the final end point that we will access to get the response will be something like this localhost:4502/etc/mycompany/paths/states.api.getStates.json




Thanks for reading. Let me know if I missed anything or we need any addition.
Happy Coding!!