AEM Granite UI Multifield Maximum Items Validation

For almost all cases of AEM implementations, adding validation for maximum items for the Granite UI Multifield is a must. In this article, we will put together a script that will register a new validator (foundation.validation.validator) to the foundation-registry (the foundation-registry is used in the Granite UI framework). In this solution, we will be utilizing the jQuery Validation library (the validation library used by Granite UI is achieved by using the jQuery Validation plugin).

We must register a client library specifically targeting the author, which is specific to customizing page authoring, which will inject JavaScript into the context of the page and execute the registry and scripts.

After the installation of this validation code, you should be able to just include two additional properties to the multifield Granite UI component, and it should be validating as expected:

Two fields:
granite:class=”granite-ui-validation-multifield-max-items”
validation=”max-items-2″

Example:

1
2
3
4
5
6
7
8
<navigationLinksMultifield
    jcr:primaryType="nt:unstructured"
    granite:class="granite-ui-validation-multifield-max-items"
    validation="max-items-2"
    sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
    composite="{Boolean}true"
    fieldLabel="Navigation Links">
    ...

Follow the steps below to have this setup

If you would like, completely download the clientlib-author client library here. This ZIP archive will include all the steps from 1-7.

Step 1

In your repository, create a new client library folder under your brand’s /apps/clientlib folder like:
path: ./sourcedcode/ui.apps/src/main/content/jcr_root/apps/sourcedcode/clientlibs/clientlib-author


Step 2

In the newly created folder, create an .content.xml file and include the text found below:
path: ./sourcedcode/ui.apps/src/main/content/jcr_root/apps/sourcedcode/clientlibs/clientlib-author/.content.xml

1
2
3
4
5
<?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="[cq.authoring.editor.sites.page.hook]"
    dependencies="[cq.authoring.editor.sites.page]"/>

This method is specific to customizing page authoring in AEM, for more information about this topic, click here.


Step 3

In the newly created folder, create an js folder (leave this empty).
path: ./sourcedcode/ui.apps/src/main/content/jcr_root/apps/sourcedcode/clientlibs/clientlib-author/js


If you would like, completely download the clientlib-author client library here. This ZIP archive will include all the steps from 1-7.

Step 4

In the newly created js folder, create a file named granite-ui-multifield-max-items.js and paste in the JavaScript found below:
path: ./sourcedcode/ui.apps/src/main/content/jcr_root/apps/sourcedcode/clientlibs/clientlib-author/js/granite-ui-multifield-max-items.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
;(function ($, Coral) {
    'use strict';

    /**
     * Granite UI Validation - Multifield Max Items
     *
     * @class MultifieldMaxItems
     * @classdesc registering Granite UI validation for the multifield maximum items.
     * This custom JavaScript validation ensures the number of multifield items does not exceed the maximum number
     * of items in the touch ui.
     *
     * Example Granite UI Multifield block:
     * <navigationLinksMultifield
          jcr:primaryType="nt:unstructured"
          granite:class="granite-ui-validation-multifield-max-items"
          validation="max-items-2"
          sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
          ...
     */

    var MultifieldMaxItems = function () {

        var CONST = {
            TARGET_GRANITE_UI: '.granite-ui-validation-multifield-max-items',
            VALIDATION_PROPERTY: 'validation',
            VALIDATION_VALUE_PREFIX: 'max-items',
            ERROR_MESSAGE: 'Max allowed items is {0}',
            ERROR_TOOLTIP_ID: Coral.commons.getUID(),
            DEFAULT_MAX_SIZE:  100
        };

        /**
         * Initializes the MultifieldMaxItems
         *
         * @private
         */

        function init() {

            // if dialog does not exist on the page, escape.
            if (!$(CONST.TARGET_GRANITE_UI)) return;

            // register foundation validation for multi-field, max size
            $(window).adaptTo('foundation-registry').register('foundation.validation.validator', {
                selector: CONST.TARGET_GRANITE_UI,
                validate: function (el) {
                    var max = _getMaxSizeValue(el);
                    if (el.items.length > max) {
                        return CONST.ERROR_MESSAGE.replace('{0}', max);
                    }
                },
                show: function (el, message) {
                    _handleError(el, message);
                },
                clear: function (el) {
                    var max = _getMaxSizeValue(el);
                    if (el.items.length <= max) {
                        // set field to valid if max size fits within the rules
                        var $el = $(el);
                        var fieldAPI = $el.adaptTo('foundation-field');
                        if (fieldAPI && fieldAPI.setInvalid) {
                            fieldAPI.setInvalid(false);
                            // remove tooltip
                            $('#' + CONST.ERROR_TOOLTIP_ID).remove();
                        }
                    }
                }
            });
        }

        /**
         * Gets the max-items value set in the "validation" granite ui property.
         *
         * @private
         * @param {HTMLElement} el is the validation HTML block with an existing validation attribute
         */

        function _getMaxSizeValue(el) {
            var validationName = el.getAttribute('data-' + CONST.VALIDATION_PROPERTY);
            if (validationName) {
                var max = validationName.replace(CONST.VALIDATION_VALUE_PREFIX + '-', '');
                if (max) {
                    return parseInt(max);
                }
            }
            return CONST.DEFAULT_MAX_SIZE;
        }


        /**
         * Handles the displaying of error messages
         *
         * @private
         * @param {HTMLElement} el is the validation HTML block with an existing validation attribute
         * @param {String} message is error message returned by the validate method, in the registry of foundation.validation.validator.
         */

        function _handleError(el, message) {
            var $el = $(el);

            // set foundation field to show as invalid
            var fieldAPI = $el.adaptTo('foundation-field');
            if (fieldAPI && fieldAPI.setInvalid) {
                fieldAPI.setInvalid(true);
            }

            // set foundation field to show as invalid
            var error = $el.data('foundation-validation.internal.error');
            if (error) {
                error.content.innerHTML = message;
                if (!error.parentNode) {
                    $el.after(error);
                    error.show();
                }
            } else {
                error = new Coral.Tooltip();
                error.variant = 'error';
                error.interaction = 'off';
                error.placement = 'bottom';
                error.target = el;
                error.content.innerHTML = message;
                error.open = true;
                error.id = CONST.ERROR_TOOLTIP_ID;
                $el.data('foundation-validation.internal.error', error);
                $el.after(error);
            }
        }

        return {
            init: init
        }

    }();

    MultifieldMaxItems.init();

}(jQuery, Coral));

Step 5

In your repository, create a css.txt and js.txt file
path: ./sourcedcode/ui.apps/src/main/content/jcr_root/apps/sourcedcode/clientlibs/clientlib-author/css.txt

path: ./sourcedcode/ui.apps/src/main/content/jcr_root/apps/sourcedcode/clientlibs/clientlib-author/js.txt

A valid client library must include these two files.


Step 6

Edit the js.txt file to include:

path: ./sourcedcode/ui.apps/src/main/content/jcr_root/apps/sourcedcode/clientlibs/clientlib-author/js.txt

1
2
#base=js
granite-ui-multifield-max-items.js

Step 7

Edit your Granite UI Multifield component to include two new properties:
granite:class=”granite-ui-validation-multifield-max-items”
validation=”max-items-2″

Example: In this example, we have ensured that the maximum items that can be used in the multifield is 2 items.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<navigationLinksMultifield
    jcr:primaryType="nt:unstructured"
    granite:class="granite-ui-validation-multifield-max-items"
    validation="max-items-2"
    sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
    composite="{Boolean}true"
    fieldLabel="Navigation Links">
    <field
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/container"
        name="./navLinks">
        <items jcr:primaryType="nt:unstructured">
            <linkLabel
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                fieldLabel="Link Label"
                name="./linkLabel"
                required="true"/>
            <linkPath
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
                fieldLabel="Link Path"
                name="./linkPath"
                required="true"
                rootPath="/content"/>
            <linkTarget
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/form/select"
                fieldLabel="CTA Link Target"
                name="./linkTarget">
                <items jcr:primaryType="nt:unstructured">
                    <self
                        jcr:primaryType="nt:unstructured"
                        selected="{Boolean}true"
                        text="Same Page"
                        value="_self"/>
                    <blank
                        jcr:primaryType="nt:unstructured"
                        text="New Tab"
                        value="_blank"/>
                </items>
            </linkTarget>
        </items>
    </field>
</navigationLinksMultifield>

Step 8

The Granite UI Multifield validation should be working as expected.

Granite UI Multifield Max Items Validation Screenshot


You have successfully added Granite UI Multifield maximum items validation!

For another in-depth tutorial for how to add Granite UI RichText Max Characters Length Validation, click here


Hello, I am an enthusiastic Adobe Community Advisor and a seasoned Lead AEM Developer. I am currently serving as an AEM Technical Lead at MNPDigital.ca, bringing over a decade of extensive web engineering experience and more than eight years of practical AEM experience to the table. My goal is to give back to the AEM Full Stack Development community by sharing my wealth of knowledge with others. You can connect with me on LinkedIn.

One thought on “AEM Granite UI Multifield Maximum Items Validation

Leave a Reply

Your email address will not be published. Required fields are marked *


Back To Top