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.
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
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
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.
For another in-depth tutorial for how to add Granite UI RichText Max Characters Length Validation, click here
Very good solution!