Skip to main content

Generate id for AEM components

We all know that when we add an AEM component to a page, it creates the DOM structure on the pages based upon our HTL file.

Sometimes we require id attribute to be set in our div for n number of reasons. If we put id attribute in the HTL file itself then it will become static and if in include that component multiple times on the page, the same id will be repeated for all instance; which is not a neat solution.

Wouldn't it be nice if we have a mechanism to generate an id randomly and uniquely for each instance of the component on the page? In this post, we are going to do just that.

Hence, without further ado, let's get started -

Step #1

Create a component that will have the field for unique id. We are making this id editable as well to give more flexibility to the content authors. Below is the cq:dialog.xml file -

<?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="ID Demo"
    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">
            <tab
                jcr:primaryType="nt:unstructured"
                jcr:title="Properties"
                sling:resourceType="granite/ui/components/foundation/container">
                <layout
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
                <items jcr:primaryType="nt:unstructured">
                    <columns
                        jcr:primaryType="nt:unstructured"
                        sling:resourceType="granite/ui/components/foundation/container">
                        <items jcr:primaryType="nt:unstructured">
                            <uniqueId
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/foundation/form/textfield"
                                class="field-whitespace"
                                fieldDescription="Enter the unique id"
                                fieldLabel="ID*"
                                name="./uniqueId"
                                required="{Boolean}true"/>
                            <title
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/foundation/form/textfield"
                                class="field-whitespace"
                                fieldDescription="Enter the title"
                                fieldLabel="Title"
                                name="./title"/>
                            <description
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/foundation/form/textarea"
                                class="field-whitespace"
                                fieldDescription="Enter the description"
                                fieldLabel="Description"
                                name="./description"/>
                        </items>
                    </columns>
                </items>
            </tab>
        </items>
    </content>
</jcr:root>

Step #2

Create the HTL file with the following code - 

<sly data-sly-use.id="${'org.redquark.demo.core.models.IdGeneratorModel' @ prefix='idDemo_'}" />
<div id="${properties.uniqueId}">
 ID Demo - #${properties.uniqueId}
 <div>
     <h3>Title: ${text.title}</h1>
     <p>Description: ${text.description}</p>
 </div>
</div>
Here, we are calling a SlingModel called IdGeneratorModel which returns the unique id which we are setting as an id attribute.

Step #3

We are now going to create the java code that will create a random id of 8 characters and then return it to the HTL file - 


package org.redquark.demo.core.models;

import java.nio.charset.Charset;
import java.util.Random;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class generates a random id for the main div tag of any component
 * 
 * @author Anirudh Sharma
 */
@Model(adaptables = { Resource.class, SlingHttpServletRequest.class })
public class IdGeneratorModel {

 private final Logger log = LoggerFactory.getLogger(this.getClass());

 @Inject
 private String prefix;

 @SlingObject
 private SlingHttpServletRequest request;

 @PostConstruct
 protected void init() {

  // Getting the reference of the current node
  Node currentNode = request.getResource().adaptTo(Node.class);

  // Stored id, if any
  String storedId = null;

  try {
   // Getting the stored id from the node
   storedId = currentNode.getProperty("uniqueId").getString();
  } catch (RepositoryException e) {
   log.error(e.getMessage(), e);
  }

  try {
   if (storedId == null || storedId.isEmpty()) {
    currentNode.setProperty("uniqueId", prefix + "_" + generateId(8));
    request.getResourceResolver().adaptTo(Session.class).save();
   }
  } catch (RepositoryException e) {
   log.error(e.getMessage(), e);
  }

 }

 /**
  * This method generates the random id
  * 
  * @return {@link String}
  */
 private String generateId(int n) {

  // Length is bounded by 256 Character
  byte[] array = new byte[256];
  new Random().nextBytes(array);

  String randomString = new String(array, Charset.forName("UTF-8"));
  // Create a StringBuffer to store the result
  StringBuffer r = new StringBuffer();

  // Append first 20 alphanumeric characters
  // from the generated random String into the result
  for (int k = 0; k < randomString.length(); k++) {
   char ch = randomString.charAt(k);
   if (((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) && (n > 0)) {
    r.append(ch);
    n--;
   }
  }
  // return the resultant string
  return r.toString();
 }
}
The above code is nothing but a sling model that takes the prefix field from the HTL code and prepend it to the generated random id. We are then setting that value in the JCR so that the component dialog can read it from there.

Step #4 

Add the component on the page and inspect it using the browser's dev tools. The id will be set as follows - 
 

Conclusion

So in this post, we saw how we can generate a random and unique id for a component when it is added to any page. You can find the code to this in my GitHub repository.

I hope you enjoyed this post. Your suggestions and feedback are always welcome. Feel free to befriend me on FacebookTwitter or Linked In or say Hi by email.

Comments

Popular posts from this blog

Day 00: AEM Developer Series

Hello everyone! Welcome to this AEM development series. We can all see the revolution of Digital Marketing today. Companies are dying to be a part of it and they have made this a war for the Digital Marketing tools.
Adobe is way ahead in this war and has gained a lot of market capture. They are leaders in the Digital Marketing platforms since 2014-15. One of the flagship product in Adobe's Digital Marketing suite is Adobe Experience Manager (AEM).
Since AEM is in huge demand, the people who know how to develop on AEM are also in huge demand. But developing on AEM is not easy as it is made up of various open-source technologies such as Apache Felix (OSGi), Apache Sling, Apache Oak and Adobe's own technologies like Granite, HTL etc. Learning all these technologies in conjunction can sometimes become confusing and frustrating 😫.
When I first started learning AEM in 2016, I was dumbfounded to see there is so much going on under the hood. I then spent months to gather all the res…

Day 05: Working with Sling Servlets in AEM

Day 03: Setting up AEM Development Environment