Expand my Community achievements bar.

Dive into Adobe Summit 2024! Explore curated list of AEM sessions & labs, register, connect with experts, ask questions, engage, and share insights. Don't miss the excitement.

How to add a clientlibs into the cq author form?

Avatar

Level 4

Hello friends,

I create an clientlib to validation max, min for multifield. Example: clientlib-multifield-max-min-validation

When I set the category as cq.authoring.dialog, It worked fine.

So, I set the category name as test.multifieldValidation and call it. The js can't load on the form. I tried another jquery script in the same file, it loaded. But the script as below can't load.

How to load that clientlib in the cq author for clasic UI and Touch UI?

Thanks you so much,

The CSS:

/*
* Copyright 2016 Nate Yolles
*
* https://github.com/nateyolles/aem-touch-ui-validation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.coral-Multifield.is-invalid {

  border: solid 1px #e14132;
}

.coral-Multifield .marker-multifield {

  display: none;
}

The JS:

utils.js

/*
* Copyright 2016 Nate Yolles
*
* https://github.com/nateyolles/aem-touch-ui-validation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* AEM Touch UI Validation library common utilities
*/
(function(ns, window, document, $, Granite, undefined) {

  'use strict';

  ns.utils = {

   /**
  * Is a string not null, undefined, blank or empty
  *
  * @param {String} The string to test
  * @return {Boolean} Is string not null, undefined, blank or empty
  * @function
   */
   isNotBlank: function(str) {

   return typeof str !== 'undefined' && str !== null && str.trim() !== '';
   },

   /**
  * Get a data attribute from a jQuery object
  *
  * If the data attribute is undefined, return the default value.
  *
  * @param {jQuery} A jQuery object
  * @param {String} Data attribute to retrieve from the jQuery object
  * @param {String} Default value if data attribute is undefined
  * @return {String|Number} The data attribute or default value
  * @function
   */
   getDataAttribute: function($el, property, defaultVal) {

   var value = $el.data(property);

   return value === undefined ? defaultVal : value;
   },

   /**
  * Get a attribute from a jQuery object
  *
  * If the attribute is undefined, return the default value.
  *
  * @param {jQuery} A jQuery object
  * @param {String} Attribute to retrieve from the jQuery object
  * @param {String} Default value if attribute is undefined
  * @return {String|Number} The attribute or default value
  * @function
   */
   getAttribute: function($el, property, defaultVal) {

   var value = $el.attr(property);

   return value === undefined ? defaultVal : value;
   },

   /**
  * Get the validation error HTML as a jQuery object
  *
  * @return {jQuery} The validation error HTML as a jQuery object
  * @function
   */
   getFieldErrorEl: function() {

   return $("<span class='coral-Form-fielderror coral-Icon coral-Icon--alert coral-Icon--sizeS' data-init='quicktip' data-quicktip-type='error' />").clone();
   }

  };

})(window.aemTouchUIValidation = window.aemTouchUIValidation || {}, window, document, Granite.$, Granite);

THE multifield.js

/*
* Copyright 2016 Nate Yolles
*
* https://github.com/nateyolles/aem-touch-ui-validation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Validation for Granite Touch UI multifield component.
*
* Additional properties for granite/ui/components/foundation/form/multifield
* are:
*
* {Long}min
* The minimum number of multifield items
* No default
*
* {Long}max
* The maximum number of multifield items
* No default
*
* During usage, the multifield's add button will disable if the max is reached.
* Upon dialog submission, the size is checked against the min and max fields
* thereby rendering min as a 'required' field validation.
*
* <myMultifield
* jcr:primaryType="nt:unstructured"
* sling:resourceType="granite/ui/components/foundation/form/multifield"
* fieldLabel="My multifield"
* min="{Long}2"
* max="{Long}5">
* <field
* jcr:primaryType="nt:unstructured"
* sling:resourceType="granite/ui/components/foundation/form/textfield"
* name="./myTextfield" />
* </myMultifield>
*/
(function(ns, window, document, $, Granite, undefined) {

  'use strict';

  var MULTIFIELD_SELECTOR = '.coral-Multifield',
   ADD_BUTTON_SELECTOR = '.coral-Multifield-add',
   REMOVE_BUTTON_SELECTOR = '.coral-Multifield-remove';

  $(window).adaptTo('foundation-registry').register('foundation.adapters', {

   type: 'aemTouchUIValidation-field',
   selector: '.coral-Multifield',
   adapter: function(el) {

   var $multifield,
   $addButton,
   $items;

   $multifield = $(el);
   $addButton = $multifield.children('.coral-Multifield-add');

   return {

   setDisabledAdd: function(disabled) {

  $addButton.attr('disabled', disabled);
   },
   size: function() {

  $items = $multifield.children('ol').children();
   return $items.length;
   }

  };
   }

  });

  $.validator.register({

   selector: '.marker-multifield',
   validate: function(el) {

   var $field,
   value,
   min,
   max;

   $field = el.closest(".coral-Form-field");
   value = parseInt(el.val(), 10);
   min = $field.data('min');
   max = $field.data('max');

   if (value > max) {

   return Granite.I18n.get('The field must contain {0} or less items.', max);
   } else if (value < min) {

   return Granite.I18n.get('The field must contain {0} or more items.', min);
   } else {

  el.setCustomValidity(null);
   el.updateErrorUI();
   }

  },
   show: function (el, message) {

   var fieldErrorEl,
   field,
   error,
   arrow;

   fieldErrorEl = ns.utils.getFieldErrorEl();
   field = el.closest(".coral-Form-field");

   field.attr("aria-invalid", "true")

  .toggleClass("is-invalid", true);

   field.nextAll(".coral-Form-fieldinfo")

  .addClass("u-coral-screenReaderOnly");

   error = field.nextAll(".coral-Form-fielderror");

   if (error.length === 0) {

  arrow = field.closest("form").hasClass("coral-Form--vertical") ? "right" : "top";

   fieldErrorEl

  .attr("data-quicktip-arrow", arrow)

  .attr("data-quicktip-content", message)

  .insertAfter(field);
   } else {

  error.data("quicktipContent", message);
   }

  },
   clear: function (el) {

   var field = el.closest(".coral-Form-field");

   field.removeAttr("aria-invalid").removeClass("is-invalid");

   field.nextAll(".coral-Form-fielderror").tooltip("hide").remove();
   field.nextAll(".coral-Form-fieldinfo").removeClass("u-coral-screenReaderOnly");
   }

  });

  /**
  * Assign multifield event handlers and listeners when a Granite UI dialog
  * loads.
  */
  $(document).on('foundation-contentloaded', function(e) {

   var $dialog,
   $multifields;

   $dialog = $(e.target);
   $multifields = $dialog.find(MULTIFIELD_SELECTOR);

   /**
  * Handle multifield add events.
  *
  * Check the maximum length of the multifield set as a data attribute.
  * Disable the multifield addition button if the additional list item
  * reaches the max.
  */
   $multifields.on('multifield:add', function(event) {

   var $multifield,
   $counter,
   api,
   min,
   max;

   $multifield = $(this);
   api = $multifield.adaptTo('aemTouchUIValidation-field');
   $counter = $multifield.children('.marker-multifield');
   min = $multifield.data('min');
   max = $multifield.data('max');

   var count = parseInt($counter.val(), 10) + 1;
   $counter.val(count);

   /*
  * If the field has already been validated, check again because the 'add'
  * button was clicked. Wait until the first validation because we don't
  * want an error message as the user starts adding from nothing.
  */
   if ($counter.validationMessage()) {

  $counter.change();
   }

  $multifield.trigger('multifield:update');
   });

   /**
  * Handle multifield removal events.
  *
  * Enable the multifield 'add' button.
  */
   $multifields.on('multifield:remove', function(event) {

   var $multifield,
   $counter,
   api;

   $multifield = $(this);
   $counter = $multifield.children('.marker-multifield');
   api = $multifield.adaptTo('aemTouchUIValidation-field');

   var count = parseInt($counter.val(), 10) - 1;
   $counter.val(count);

   /* update validation */
   $counter.change();

   api.setDisabledAdd(false);
   });

   /**
  * Handle multifield update events.
  *
  * Disable the 'add' button if the multifield reaches the max limit.
  */
   $multifields.on('multifield:update', function(event) {

   var $multifield,
   api,
   max;

   $multifield = $(this);
   api = $multifield.adaptTo('aemTouchUIValidation-field');
   max = $multifield.data('max');

   if (api.size() >= max - 1) {

  setTimeout(function() {

  api.setDisabledAdd(true);
   }, 10);
   }

  });

   /**
  * Handle multifield init events.
  *
  * Disable the 'add' button if the multifield reaches the max limit.
  */
   $multifields.on('multifield:init', function(event) {

   var $multifield,
   api,
   max;

   $multifield = $(this);
   api = $multifield.adaptTo('aemTouchUIValidation-field');
   max = $multifield.data('max');

   if (api.size() >= max) {

  setTimeout(function() {

  api.setDisabledAdd(true);
   }, 10);
   }

  });

   /**
  * Assign add and remove event handlers on click events for buttons.
  *
  * There's only one add button per multifield so the event is attached
  * directly on the button. The remove buttons must be handled with event
  * delegation as by the time this code runs, the list item has been removed
  * from the DOM making it more difficult to find the parent multifield.
  * Trigger update event handler on load to disable the button if the max
  * is already reached.
  */
   $multifields.each(function() {

   var $multifield = $(this),
   api = $multifield.adaptTo('aemTouchUIValidation-field'),
   min = $multifield.data('min'),
   max = $multifield.data('max');

   var $counter = $('<input type="text" class="marker-multifield coral-InputGroup-input coral-Textfield">')

  .val(api.size());

   $multifield.prepend($counter);

   $multifield

  .find(ADD_BUTTON_SELECTOR)

  .click(function() {

  $multifield.trigger('multifield:add');
   })

  .trigger('multifield:init');

   $multifield.on('click', REMOVE_BUTTON_SELECTOR, function(event) {

  $multifield.trigger('multifield:remove');
   });
   });
  });

})(window.aemTouchUIValidation = window.aemTouchUIValidation || {}, window, document, Granite.$, Granite);

7 Replies

Avatar

Community Advisor

Hi

For Touch UI cq.authoring.dialog will work

and for classic UI check Adobe Experience Manager Help | Validating Adobe Experience Manager Classic UI dialog values

What is the issue here? if you want to upload js/css for touch UI dialog, you need to use category= cq.authoring.dialog



Arun Patidar

Avatar

Level 4

Hi I know cq.authoring.dialog will work. However, I can't use it. Because it will effect to all app on the AEM.

Example: I have three apps A, B, C. I want to modify a CSS for app B in editor. http://localhost:4502/editor.html/content/b/en/test.html

I create an clienlib-editor with category b.editor I don't want to use cq.authoring.dialog . I want to use category b.editor for app B only. I don't want that CSS appear on apps A and C.

Avatar

Community Advisor

Hi,

If you can check app inside js and write if condition to execute logic.

e.g.

(function(ns, window, document, $, Granite, undefined) {

  'use strict';

if(Granite.author.pageInfoHelper.json.status.path.includes("/content/b/")){  // you can use indexOf function as well

     //your logic

}

})(window.aemTouchUIValidation = window.aemTouchUIValidation || {}, window, document, Granite.$, Granite);

For Restricting CSS

You can add ClientLibs css in headlibs.html, just check wcmmode for edit and check app b using Java or JS Server side Api.

based on both condition include css.



Arun Patidar

Avatar

Level 4

Hi,

I can't do that. Because, the page don't have any component. I want the css load when the page load. How to load the CSS into the head. How to load my category in this.

<head>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <meta name="viewport" content="user-scalable=no">

    <meta name="user.preferences.winmode" content="multi">

    <title>home</title>

   

    <link rel="stylesheet" href="https://forums.adobe.com/libs/granite/ui/components/coral/foundation/clientlibs/foundation.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/etc/clientlibs/granite/coralui2/optional/rte.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/etc/clientlibs/granite/coralui2.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/etc/clientlibs/granite/coralui2/optional/imageeditor.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/granite/ui/clientlibs/notifications.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/common/clientlibs/notifications.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/authoring/editors/clientlibs/core.css" type="text/css">

    <link rel="stylesheet" href="https://forums.adobe.com/etc/clientlibs/granite/ui/quickactions.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/granite/ui/components/endor/clientlibs.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/granite/ui/components/foundation/clientlibs/foundation.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/authoring/tour/tour.css" type="text/css">

    <link rel="stylesheet" href="https://forums.adobe.com/libs/mcm/campaign/components/touch-ui/s7-parameters/clientlib.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/projects/admin/translation/customsearch/pathbrowser/tagspicker/clientlibs.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/common/tagspicker/clientlibs.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/authoring/dialog/slideshow/slideshow/slideshow.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/authoring/dialog/sizefield/clientlibs/sizefield.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/authoring/dialog/richtext/clientlibs/richtext.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/authoring/dialog/policy/clientlibs.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/authoring/dialog/image/clientlibs/image.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/authoring/dialog/fileuploadfield/clientlibs/fileuploadfield.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/authoring/dialog/clientlibs/dialog.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/etc/designs/lyf/clientlib-editor.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/apps/acs-commons/authoring/vendor/jquery.fonticonpicker.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/etc/clientlibs/acs-commons/vendor/fontawesome.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/apps/acs-commons/touchui-widgets.css" type="text/css">

   

    <link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/authoring/editors/clientlibs/sites/page.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/authoring/searchfield/clientlibs/searchfield.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/gui/components/authoring/editors/clientlibs/plugin/annotate.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/dam/cfm/clientlibs/authoring.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/personalization/touch-ui/clientlibs/targeteddialog.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/tsdk.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/personalization/touch-ui/clientlibs/multisitetargeting.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/personalization/touch-ui/clientlibs/audiencemanagement.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/personalization/touch-ui/clientlibs/targeteditor.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/wcm/designimporter/components/touch-ui/clientlibs/editor.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/wcm/msm/content/touch-ui/authoring/commons.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/wcm/msm/content/touch-ui/authoring/editor.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/commerce/gui/components/common/productpicker/clientlibs.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/commerce/gui/components/common/collectionpicker/clientlibs.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/commerce/gui/components/common/assetpicker/clientlibs.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/commerce/components/addtocartimage/clientlib-authoring.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/contexthub/code/kernel/segment-engine/authoring-hook.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/mcm/campaign/components/touch-ui/clientlibs/core.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/apps/acs-commons/authoring/editor-styles.css" type="text/css"><link rel="stylesheet" href="http://localhost:4502/etc/clientlibs/granite/typekit/ruf7eed/c/ruf7eed-d.css" media="all">

Avatar

Community Advisor

Hi,

You can write HTL in page component as well. like below:

.

...

<link rel="stylesheet" href="https://forums.adobe.com/libs/commerce/components/addtocartimage/clientlib-authoring.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/cq/contexthub/code/kernel/segment-engine/authoring-hook.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/libs/mcm/campaign/components/touch-ui/clientlibs/core.css" type="text/css">

<link rel="stylesheet" href="https://forums.adobe.com/apps/acs-commons/authoring/editor-styles.css" type="text/css">

<link rel="stylesheet" href="http://localhost:4502/etc/clientlibs/granite/typekit/ruf7eed/c/ruf7eed-d.css" media="all">

<sly data-sly-use.app="App.js">

    <div data-sly-test.appB="${app.isAppB}" data-sly-unwrap></div>

    <div data-sly-test.author="${wcmmode.edit || wcmmode.design}" data-sly-unwrap></div>

    <sly data-sly-test="${author && appB}">

        <sly data-sly-use.clientLib="/libs/granite/sightly/templates/clientlib.html"

            data-sly-call="${clientLib.css @ categories='category.AppB'}" data-sly-unwrap/>

    </sly>

</sly>

App.js will check the path of application and if it is App B it will return true as isAppB otherwise false.



Arun Patidar

Avatar

Level 4

Hi,

I don't know the file to be put that code. Can you tell me the file to put that code?

This is my page structure in my app.

page-structure.PNG

Please help me.

Thank you so much,

BienHV

Avatar

Community Advisor

Hi,

You can put inside haed.html or headerlibs.html.

At the end should be inside head tag



Arun Patidar