Custom Multifield Validation in AEM

Satyam Gupta
8 min readDec 19, 2024

--

Hello, Developers! This blog is designed to guide you through the process of creating custom validation for a multifield within a dialog in AEM. It will explain how the multifield validation works and offer clear explanations accompanied by relevant examples.

Lets Understand the requirement first:

In my recent project, I was assigned a task to create a multifield with validation for a minimum and maximum number of items. Specifically, it requires adding a minimum of 1 field and a maximum of 6 fields for an Author.

According to the requirement, we need to implement validation for the multifield within the dialog. To accomplish this, we can create custom clientlibs within the component, which will be discussed in this blog.

Solution:

Here, I will provide an example where I create a component named “Top Navigation” that includes a multifield with validation.

Here are the steps for validating a multifield:

  1. Prepare Your Multifield Dialog: Ensure that your multifield dialog is ready and functioning correctly.
  2. Create Custom Clientlibs: Develop custom clientlibs within the component to handle the validation logic.
  3. Implement Validation Logic: Write the necessary validation code to enforce the minimum and maximum number of items (e.g., minimum of 1 and maximum of 6 fields for an Author).
  4. Integrate Clientlibs with Dialog: Integrate the custom clientlibs with your multifield dialog to apply the validation rules.
  5. Test the Validation: Thoroughly test the multifield dialog to ensure that the validation works as expected, preventing users from adding fewer than 1 or more than 6 fields.

Steps:

1. Prepare Your Multifield Dialog:

Ensure that your multifield dialog is ready and functioning correctly.

2. Create Custom Clientlibs:

Develop custom clientlibs within the component to handle the validation logic.

This is the clientlib structure with topnav-custom-validation.js:

Let’s go over each file and folder:

→ Create a folder named clientlibs and include a content.xml file. In the content.xml file, add the property jcr:primaryType with the value sling:Folder.

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:Folder"/>

→ Within the clientlibs folder, create a new folder named editor, and include a content.xml file. In this file, add the property
jcr:primaryType with the value cq:ClientLibraryFolder.

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="cq:ClientLibraryFolder"/>

→ Inside this editor folder create a new folder named as JS and inside this create a file named as topnav-custom-validation.js

(function ($, Coral) {
"use strict";
console.log("--- custom clientlibs loaded--")
let registry = $(window).adaptTo("foundation-registry");

// require validation for multifield max and min item
registry.register("foundation.validation.validator", {
selector: "[data-validation=top-nav-multifield-validation]",
validate: function (element) {
// insert your custom validation logic there
});
})(jQuery, Coral);

Let`s discuss above code in detail:

To implement a custom validator, we first need to adapt our window object into the registry.

let registry = $(window).adaptTo("foundation-registry");
  • This piece of code is creating a registry for the component, similar to how the Sling API uses the adaptTo() method.
  • Then, using this registry, we can register the validator with the following piece of code.
  • Next, we have the Selector: Essentially, this selector specifies which validator will execute in this validation.
selector: "[data-validation=top-nav-multifield-validation]"

Let me explain how it work, I have defined a validator property in this selector which is data-validation. and the value of this validator property is whatever i define in this selector.
For example here we define property name is
top-nav-multifield-validation .

So, it will check in which field this property is present, and the validator will apply to that field.

  • Now, we are adding this validation property to the field that we want to validate.
                  <navigationOptions
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
fieldLabel="Navigation Options"
fieldDescription="Click 'Add' to insert a new item; you must add at least 1 navigation option and can add up to 6.."
composite="{Boolean}true"
validation="top-nav-multifield-validation">
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container"
name="./navigationOptions">
<items jcr:primaryType="nt:unstructured">

//add the required fields what you want

</items>
</field>
</navigationOptions>

You can see this data-validation property is available as an attribute in your coral-multifield tag when you inspect your Top navigation dialog in Chrome:

  • Next you need to write your validation method:
validate: function (element) {
let el = $(element);
// write your custom validation logic there
},

It will pass your field as an element here, and then in el, you can get your field object and write your custom logic there.

3. Implement Validation Logic:

Write the necessary validation code to enforce the minimum and maximum number of items (e.g., minimum of 1 and maximum of 6 fields for an Author).

→ For the validation, we need to add two data attributes: min-item and max-item.

So for this, AEM provides a way using granite:data, and if you want to insert a class, you can use granite:class.

This granite:class will help you to add CSS in clientlib.

Lets add this granite:data and granite:class in our dialog →

                <navigationOptions
jcr:primaryType="nt:unstructured"
granite:class="top-nav-multifield-validation"
sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
fieldLabel="Navigation Options"
fieldDescription="Click 'Add' to insert a new item; you must add at least 1 navigation option and can add up to 6.."
composite="{Boolean}true"
validation="top-nav-multifield-validation">
<granite:data
jcr:primaryType="nt:unstructured"
min-items="{Long}1"
max-items="{Long}6"/>
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container"
name="./navigationOptions">
<items jcr:primaryType="nt:unstructured">
<headingLabel
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Heading Label"
fieldDescription="Enter a heading label with a character limit of 20"
name="./headingLabel"
maxlength="20"
required="{Boolean}true"/>
<landingPageURL
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathbrowser"
fieldLabel="Landing Page URL"
fieldDescription="Add the page URL associated with the heading"
name="./landingPageURL"
required="{Boolean}true"/>
</items>
</field>
</navigationOptions>

These data attribute also you can see once you inspect the dialog:

Finally, let’s implement the custom validation logic there:

(function ($, Coral) {
"use strict";
console.log("--- custom clientlibs loaded--")
let registry = $(window).adaptTo("foundation-registry");

// require validation for multifield max and min item
registry.register("foundation.validation.validator", {
selector: "[data-validation=top-nav-multifield-validation]",
validate: function (element) {
let el = $(element);
let max = el.data("max-items");
let min = el.data("min-items");
let items = el.children("coral-multifield-item").length;
console.log("{} {} ", max, min, items)
if (items > max) {
return (
"You can add maximum " +
max +
" navigation option."
);

}
if (items < min) {
return "Require to add minimum " + min + " navigation option.";
}
},
});
})(jQuery, Coral);

The min-item and max-item values are derived from the attributes min and max respectively.

let items = el.children("coral-multifield-item").length;
  • This line gives the number of fields the author has created. The coral-multifield-item is the tag name of each multifield you can see there.
    For now, I have added three fields inside the multifield.

→ Next, you can write an if condition to check and compare the field values. Based on whether the condition is true or false, you can return a validation message that will be displayed in the dialog.

4. Integrate Clientlibs with Dialog:

Integrate the custom clientlibs with your multifield dialog to apply the validation rules.

Step 1:

Add the categories property to the content.xml file of the editor node.

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="cq:ClientLibraryFolder"
categories="[wknd.components.topnavigation.editor]"/>

Step 2:

Create a file named js.txt under the editor node and include the validation JavaScript file within it.

#base=js

topnav-custom-validation.js

Step 3:

Include an extraClientlibs property in the jcr:root tag within the content.xml file located in the cq_dialog folder.

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
xmlns:granite="http://www.adobe.com/jcr/granite/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Top Navigation"
sling:resourceType="cq/gui/components/authoring/dialog"
extraClientlibs="[wknd.components.topnavigation.editor]">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<navigationOptions
jcr:primaryType="nt:unstructured"
granite:class="top-nav-multifield-validation"
sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
fieldLabel="Navigation Options"
fieldDescription="Click 'Add' to insert a new item; you must add at least 1 navigation option and can add up to 6.."
composite="{Boolean}true"
validation="top-nav-multifield-validation">
<granite:data
jcr:primaryType="nt:unstructured"
min-items="{Long}1"
max-items="{Long}6"/>
<field
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container"
name="./navigationOptions">
<items jcr:primaryType="nt:unstructured">
<headingLabel
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Heading Label"
fieldDescription="Enter a heading label with a character limit of 20"
name="./headingLabel"
maxlength="20"
required="{Boolean}true"/>
<landingPageURL
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathbrowser"
fieldLabel="Landing Page URL"
fieldDescription="Add the page URL associated with the heading"
name="./landingPageURL"
required="{Boolean}true"/>
</items>
</field>
</navigationOptions>
</items>
</column>
</items>
</content>
</jcr:root

The clientlibs are now completely integrated with the dialog.

5. Test the Validation:

Thoroughly test the multifield dialog to ensure that the validation works as expected, preventing users from adding fewer than 1 or more than 6 fields.

Case 1: If the author attempts to remove all fields from the Top Navigation dialog, a message will be displayed as defined by our validation logic.

Case 2: If author try to add more than 6 field in the Top Navigation dialog, a message will be displayed as defined by our validation logic.

In conclusion, we have achieved the expected results, indicating that our validation code is functioning correctly.

You are now capable of adding Custom validation to any multifield within a cq:dialog in AEM.

Additionally, you can include CSS in the clientlibs just as we did with JavaScript. Create a css.txt file and list your CSS files in it, similar to how we added files in js.txt. To add CSS in the dialog, you need to use the granite class as a xml tag property for field, which I have already included in the code above as granite:class.

So, In Conclusion,

we have thoroughly discussed all aspects, including how the custom validation in multifield works and the way the code is written.

This concludes our discussion on Custom Validation inside a CQ dialog multifield in AEM. We have covered how it works and its implementation with examples.

Now you can understand all about the Custom multifield Validation in AEM.

I hope you found this article interesting and informative. Please share it with your friends to spread the knowledge.

You can follow me for upcoming blogs follow.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Satyam Gupta
Satyam Gupta

Written by Satyam Gupta

AEM Full Stack Developer | Java

No responses yet

Write a response