Header Content Footer
Discover best selling sofas, Lounge chairs and get an extra 10% off using the code SEDONA10

AEM Touch UI Page Editor allows developers to build dialogs for introducing the content. In most cases, dialogs are enough. However, sometimes the HTML and JavaScript behavior does not fit on the dialog logic: hidden HTML fragments, tabs, JavaScript initialization methods, …

Normally, developers reload the entire page to relaunch all initialization JavaScript methods, or editors are forced to enable preview mode, interact with certain call to action, and enable some hidden HTML fragments. This may cause User Experience deterioration on page editing.

On this post, some tips will be explained about how to enhance the user experience of the edit mode of our AEM pages.

It is well known that JavaScript code is absolutely essential to develop high quality website in terms of Usability and User Experience. Sometimes, we need to use some JavaScript frameworks and features which do not fits with the AEM Touch UI Page Editor interface, specially after saving content in a dialog. This situation often leads to blank spaces, or JavaScript runtime errors. Well, it is possible to mitigate this by using _cq_editConfig.xml file in the component implementation.

Suppose to have a component with a title, some text, and a “Learn more” CTA. When the page is loaded, only the first 10 characters of the text is shown. If the “Learn more” CTA button is clicked, complete text will be shown.

This is the html code of our component.

${properties.title || 'Please edit title'}

${properties.text || 'Please edit text'}

This is a simple implementation of Learn More button. Function initLearnMore() resets the initial status of the component. As we can see this method will be executed when the page is loaded.

    function initLearnMore(){
        var text = $('.learn-more-content').html();
        var numChars = 10;
        if (typeof text !== "undefined" && text.length > numChars){
            var beginning = text.substring(0,numChars);
            var ending = text.substring(numChars, text.length);
            $('.learn-more-content').html(beginning + '...' + ending + '');
            $('.learn-more-cta').show();
        }
        $('.learn-more-cta').click(function(){
            $('.learn-more-cta').hide();
            $('.learn-more-dots').hide();
            $('.learn-more-tail').show();
        });
    }
    $(document).ready(function(){
        initLearnMore();
    });

After clicking the save button of a dialog, the Page Editor will refresh only the html of the corresponding component as default behavior. In our example, This behavior causes the button Learn More to disappear and the text is not shortened.

File _cq_editConfig.xml is the xml representation of node “cq:editConfig”. This node allows to configure wide range of facets of an AEM component. One of them is the node “cq:listeners”. This node allows to define how should the component behave before and after a user perform a determinate action, such us edit, insert, delete or move. According to the official AEM documentation, each behavior has predefined these values: REFRESH_SELF, REFRESH_PARENT and REFRESH_PAGE. But those behavior also accepts custom JavaScript code in it.

At this point, probably we might think “easy, call to initLearnMore() function and the component will be properly refreshed”. It is not so easy, there is an important fact we have to keep in mind: the JavaScript code is declared on a clientlib which is called on corresponding page component, and the page component is encapsulated inside an iframe on Page Editor. The custom behavior will be called outside that iframe, which means that initLearnMore() function is not defined. How do we solve this? By using JavaScript method window.postMessage(), a method which enables a safety communication between Page Editor and content iframe.

To make a properly refresh of the component in this case is to place this piece of code on _cq_editConfig.xml file.



   

As we can see, afteredit behaviour has JavaScript as a value. This code gets the element “ContentFrame”. This element is the iframe element which contains the content of our page. The next sentence is the actual postMessage call. The next step is to add a handler corresponding to the event launched on postMessage call. The handler code is something like this:

window.addEventListener('message', (e) => {
    if (e.data === 'afterEdit') {
       initLearnMore();
    }
 }, false);

This fragment of JavaScript code should be placed on a clientlib available only for AEM Page Editor, because this is not necessary on our actual content.

This is a very simple example just to demonstrate how to develop a custom refresh method and avoid to refresh all the entire page on Page Editor.

Thanks to JavaScript, we can made our HTML content changes according to certain events like click events or certain data stored on local storage. However, this could be a problem while we are editing on AEM Page Editor, specially when not all html content is shown when the page is fully loaded and we have to interact with a CTA to reveal hidden content. It is not possible to interact with the HTML buttons or other CTA in our AEM components, if the edit mode is enabled on AEM Touch UI Page Editor. The user is compelled to activate Preview mode in order to interact with a CTA.

It is possible to add a custom button in the component’s toolbar. These custom buttons are intended to add custom functionalities to determinate components. Also it can be used to interact with the component’s content. Let’s see how to create a custom button with an example. Our Learn-More component described before has a button which shows complete text of our component. If we are editing the content of this component and we want to see full text instead of minimized text, we have to switch to Preview mode, click “Learn More” button and activate again Edit mode in order to keep editing the content of current page. If we want to add a specific button to trigger the click event on our component’s toolbar, we have to create a clientlib with the category “cq.authoring.editor.sites.page.hook” configured. Also we have to include the next JavaScript code to this new clientlib.

(function ($, ns, channel, window, undefined) {
     "use strict";
     var actionIcon = "coral-Icon--preview";
     var actionTitle = "Click on learn more button";
     var actionName = "Click on Learn more button";
     var learnMoreAction = new ns.ui.ToolbarAction({
         name: actionName,
         icon: actionIcon,
         text: actionTitle,
         execute: function (editable) {
             editable.dom.find(".learn-more-cta").trigger("click");
         },
         condition: function (editable) {
             return "frontend-sample/components/content/learn-more-text" === editable.type;
         },
         isNonMulti: true
     });
     channel.on("cq-layer-activated", function (event) {
         if (event.layer == "Edit") {
             ns.EditorFrame.editableToolbar.registerAction(actionName, learnMoreAction);
         }
     });
 }(jQuery, Granite.author, jQuery(document), this));

This is the way to define and register a Toolbar action. Granite.author is a namespace inside Granite library which contains all scripts and objects for the Page Editor. One of those objects is the object Granite.author.ui.ToolbarAction. This object represents an action or button inside a component’s toolbar. Method execute receives as parameter an object Granite.author.Editable. As we can see on the code above, Editable has an element called “dom”, this element is a jQuery object which represents the HTML content of our component. So, inside execute method is placed the code for trigger the click event.

There is another method called condition. This method must return true if the button should appear in our component. This methods has also one parameter of type Granite.author.Editable. As we can see in our example we use the element “type”. This element represents the resource type of current component, so, as we can see on the example, “type” element is compared to the resource type of our component Learn More. This means that this action will be shown only on the toolbar of Learn More components.

Creating the ToolbarAction is not enough. We have to register our new action in the Toolbar. In the end of the JavaScript, there is a specific event handler. If Edit mode is enabled, the action is registered whit the method call:

   ns.EditorFrame.editableToolbar.registerAction(actionName, learnMoreAction);

As we can see, create custom toolbar actions are pretty simple. We only have to create a Clientlib as described before, and keep in mind that the execute method is executed outside the iframe which holds our actual page content.

Sometimes we need to add a custom functionality related to the whole page or a group of components inside the page. Such cases requires to add a button on the header bar instead on a toolbar of a specific component.

It is possible to add a new button on AEM Page Editor in the same way that a toolbar button. To illustrate this, we will implement a button to know which Learn More component has empty title. First of all, we need also a clientlib with the category “cq.authoring.editor.sites.page.hook” in order to use Granite.author library. This library has a specific object which represents the header bar. This object is Granite.author.ui.globalBar. Here is a sample code which marks each empty title with a red border.

(function ($, ns, channel, window, undefined) {
   "use strict";
   channel.on("cq-layer-activated", function (event) {
      if (event.layer == "Edit") {
         ns.ui.globalBar.addButton("checkTitles", "coral-Icon--preview", "Check titles").click(function () {
            var items = $(Granite.author.ContentFrame.getDocument()).find(".main.items h3.title");
            for (var i = 0; i < items.length; i++) {
               if ($(items[i]).html() == "") {
                  items[i].parentElement.style.border = "thick solid #FF0000";
               }
            }
         });
       }
   });
}(jQuery, Granite.author, jQuery(document), this));

As we can see, the code is a little bit similar to the toolbar button. In this case method addButton requires a custom CSS class for the button, the Coral UI class of the icon and the title of the button. The title will be shown if the mouse remains some seconds over the button. Here we can see the result: we have a third button on the left side of the header bar, and if we click it we will se a red border on those titles which are empty.

It is important to know that method addButton only adds a button and returns corresponding jQuery object. This means that we can develop many events was we need and also implement any condition we need to show or disable the button.

In conclusion, AEM provides these front-end tools to integrate the HTML code Touch-Enabled Page Editor and generate a properly User Experience. It is a common mistake to avoid to take in consideration how it looks like and how the content of our website will be edited during the design of the HTML, CSS and JavaScript of it. This may lead to a poor User Experience during content editing process.

Leave a Reply