The document discusses customizing the user interface in AEM 6.1. It covers extending component dialogs by providing a diff instead of copying the entire dialog structure. It also covers customizing page properties by using flags to control which properties are shown in different views. Additionally, it discusses customizing search forms by leveraging reusable search predicates and the Sling resource merger to overlay forms.
2. About us
Andreea Miruna Corbeanu
Software Engineer, AEM Sites
Site Admin, Granite UI
Christian Meyer
Software Engineer, AEM Sites
Page Authoring, Granite UI
3. User Interface Customisation
AEM projects require customization of the UI
Extension points are available in the product
Let’s learn how to use them!
6. Extending Component Dialogs
Add a field to the Image dialog?
1. Extend Image component
sling:resourceSuperType=
“foundation/components/image”
2. Provide complete dialog structure
(=> copy/paste + add new field)
Before AEM 6.1
7. Extending Component Dialogs
Add a field to the Image dialog?
1. Extend Image component
sling:resourceSuperType=
“foundation/components/image”
2. Provide dialog diff only
New in AEM 6.1
8. Sling Resource Merger
Adds an extra section in the left
navigation in AEM
Introduced in AEM 6.0
√ Customize* out of the box UI
x Extend* component dialogs
(*) By providing the diff only
9. Adds an extra field to the
inherited dialog
Sling Resource Merger
New in AEM 6.1
√ Customize* out of the box UI
√ Extend* component dialogs
(*) By providing the diff only
10. Sling Resource Merger
=> Search path-based merge
resourceResolver.getResource(“/mnt/overlay/“ + relativePath)
=> Resource type hierarchy-based merge
resourceResolver.getResource(“/mnt/override/“ + absolutePath)
Used in Touch-optimized UI to
√ Allow customization of the product UI
√ Allow cq:dialogs extension
11. Extending Component Dialogs
1. Always start by creating node skeleton of the part to redefine
(subtree of nt:unstructured nodes, without properties)
2. Then, only provide the “diff”
• Add a node? => simply add it with its properties
• Add a property? => simply add the property
• Update a node? => simply add changed properties
• Reorder a node? => use sling:orderBefore=“nodeName”
• Remove a node? => use sling:hideResource=true
• Remove a property? => use sling:hideProperties=[“propertyName”]
18. Customizing Page Properties
Use flags to control rendering of nodes for a given view
• cq:showOnCreate {Boolean}
• cq:hideOnEdit {Boolean}
• allowBulkEdit {Boolean}
19. Demo
Customize Page Properties views
• Reorder “Title” field before “Name” field
• Only keep a subset of fields in “Create Page Wizard”
• Allow “Title” field to be bulk edited
23. What is a Search Predicate?
Wraps Granite UI component
Backed by AEM Search API
Contains a JavaScript client library
• resetSearchFilters event
• loadSavedQuery event
26. Demo
• Customize the Page Authoring Side Panel Asset Search
• Customize the Side Admin Search Rail
27. Summary
Extending Components Dialogs
Override as a diff
Use sling:hideResource, sling:hideProperties, sling:orderBefore
Customizing Page Properties
Extend Page Component Dialog
Use cq:showOnCreate, cq:hideOnEdit, allowBulkEdit flags
Customizing Search Forms
Create a new custom search predicate
Use the predicate in Site admin search rail
Configure existing search predicates for Page Authoring side panel
Before diving into the extensions points, let’s first do a quick recap of the main concepts that you should be familiar with in order to understand the rest of the presentation, and more generally speaking, in order to understand how AEM Touch UI work.
First thing is that our UI is actually content-driven; each console, each page, each admin screen in the product is backed by a structure of content nodes in the JCR. This implies that we can simply customize the UI by changing some node properties in the repo. And this is usually achieved by “overlaying” the product-related code (in that case: the UI content nodes) from /libs under /apps.
In AEM 6.0, the previous version, we’ve introduced a more convenient way of overlaying resources by leveraging the latest Sling Resource Merger capabilities. In a nutshell, the big new thing is that when doing an overlay of an element of the UI, you don’t have to reproduce the exact same content node structure corresponding to the resource that you want to overlay; instead, you can simply provide the overlay as a “diff” of the resource. From a technical point of view, the major change here is that we’ve introduced a new resource provider, that is able to merge resources based on search paths (= so, merge the resources from /apps and /libs) instead of having the resource from /apps taking precedence over the one from /libs.
Last but not least, let’s talk a little bit about Granite UI. As I said previously, our UI is based on content nodes, and each of those content nodes has a Sling resource type property leading to a Sling component responsible for its rendering. In other words, our UI is actually composed of reusable, composable and modular Sling components, and those granular components are actually coming from our framework called Granite UI Foundation. So we are using Sling components to compose our UI, in similar way like you could be using CQ Foundation components to compose CQ pages.
Having that said, I’ll now let Andreea start with the customisation of the search forms.
Andreea
When developing components, a common use-case is to inherit from another component (= the super component) in order to extend it. By doing that, we also expect to reuse most of the super component’s functionalities. However, a common problem with component inheritance was that it was quite hard to reuse dialogs.
Prior to AEM 6.0, one had to copy/paste the super dialog and then work on it. Basically, the only way of extending a super’s component dialog was to do complete local override of that dialog. This was working in an “all or nothing” way: we always had to redefine the exact whole dialog structure even if we just wanted to add a single field.
In AEM 6.0, we were able to leverage the Granite UI “include” component to reuse (by including) complete subtrees of a given content node structure, and this was then used to allow reusing complete dialogs parts. For instance, we were then able to include some tabs of the super dialog, and only redefine a given tab to add a new field. This was better but still not optimal.
In AEM 6.1, we now can leverage the latest SRM capabilities to do “smarter” overrides, more working like “diffs”, in opposition to the “all or nothing” overrides. This is similar to the new way of overlaying UI parts of the product in the sense that we can now provide the overriding dialog as a “diff”. Concretely speaking, instead of having to copy the super dialog content structure, we can now only provide the different parts using the SRM vocabulary.
To extend a given part of the super dialog, you should always start by creating the corresponding node skeleton that you want to override. And by node skeleton here, I mean that you don’t have to copy all of the properties of the nodes part of the subtree that you’re extending: in other words, you can simply create a subtree a nt:unstructured nodes without any properties (because you don’t want to duplicate all the information that are already defined in the super dialog).
And then, once you have your node skeleton in place, you can only specify the different pieces. If you want to add something (either a field or a property), then you just normally add it —nothing special here.If you want to update only a subset of properties of a field, then, following the “diff” principle, you could only add the changed properties. Particular case: if you want, for some reason, to change the ordering of a field, then you could use the sling:orderBefore property.Lastly, if you want to remove something, you could either use sling:hideResource to remove a given field, or sling:hideProperties to remove a given property.
Now, remember that in the end, the dialog you’ll get will result from the merge of the super dialog, with the “diff” dialog that you’ve just created.
As you may have noticed, the way we override component dialogs is now pretty similar to the way we overlay UI parts of the product. Both techniques are indeed using the SRM capabilities and rely on the fact that resources are merged, either based on their super resource type hierarchy, or on sling search paths. But before going any further, I would like to clarify how this all fits together since we could easily get confused when mixing both use-cases = when you override a component dialog of a component that is also overlaid in /apps for instance.
First thing to understand is that the SRM comes with two different resource providers that were actually designed for different purposes. One allows to merge resources based on their resource type hierarchy, and is available at /mnt/override; whereas the other allows to merge resources based on sling search paths, and is available at /mnt/overlay.
In Touch UI, we’ve leveraging both merged resource providers to allow you to override component dialogs and overlay UI parts in a similar manner. To recap, we’re using /mnt/override for component dialogs and /mnt/overlay for UI elements.
Just to make sure that the concept is clear, here’s an example of a component dialog resolution. The common mistake that people make here is that they usually think that if the foundation list component has been overlaid under /apps, the overlaid resource will be merged with the one from /libs, which is not case: dialogs are merged according to their resource type hierarchy, and if at some point, one of the dialog resource is also overlaid, then the overlay will take complete precedence.
Basically, the take-away here is the following —regarding the /apps vs. /libs overlay technique-: don’t overlay components like you overlay elements of the UI. Even if you can now overlay UI elements using diffs, you should still overlay components (for instance, like apps/foundation/components/page) using the old-fashioned “all or nothing” way. Because we’re not leveraging the SRM during the super component resolution.
Now that we’ve seen how to generally extend component dialogs, let’s look at a specific use-case and see how page properties can be customized in AEM 6.1.
In AEM 6.1, the Page Properties have different views throughout the product: there is the create page view, the edit page view and the bulk edit page view. Even though the properties that you can see in those views are different; you should understand that all of those views are actually based on the Page Component Dialog, where the Page Properties are actually defined.
That’s the “Create Page”, visible when creating a new Page out of a Template
That’s the “normal” edit page view, available from the Site Admin or the Page Editing.
And that’s the bulk edit view, available from the Site Admin.
As the Page Component is being frequently extended in customers projects, we’ve introduced a convenient way of customizing its different views too. And you can do that by simply extending the Page Component Dialog.
The idea here is to leverage the extension point we’ve seen previously, to extend the Page Component Dialog, and add some properties to control the rendering of a field in a given view.
By default, every field of the Page Component Dialog is shown in the edit view, and hidden in the other views. To allow a given field in the create page view, just flag the field with “cq:showOnCreate”. On the other side, to hide it from the edit view, flag it with “cq:hideOnEdit”. Also, to make a given field bulk editable, simply flag it with “allowBulkEdit”.
And this is that simple.
However, in the case of Bulk Edit, be aware that the implementation of your field has to support bulk edit. But don’t worry, this is already the case for most of the Granite UI components; however, if you want to allow your custom field to bulk editing, then you’ll probably have to update its implementation so that it knows how to behave in bulk edit. If you’re interested in this topic, the official documentation explains how you can do that. I won’t cover it in the presentation as it would take too much time.
(Basically, in order for your component to play nicely with the bulk editing, it has to fulfil the following contract:
Regarding the markup: you have to assign it the “foundation-field-mixed” class when it contains a mixed value, so that it won’t get submitted when submitting the form. Otherwise, you can encounter some unexpected dataloss.
Regarding the JS API: you have to provide a setMixed() and setDisabled() method if your field is a composite field so that the client code can interact with your field. To do so, you can leverage the Granite UI adapter pattern, so that your field could be adapted to a foundation-field-mixed that exposes the setMixed and setDisabled methods.)
Create Page: http://localhost:4502/libs/wcm/core/content/sites/createpagewizard.html/content/geometrixx
Edit Page: http://localhost:4502/libs/wcm/core/content/sites/properties.html/content/geometrixx/en
Bulk Edit Page: http://localhost:4502/libs/wcm/core/content/sites/properties.html?item=%2Fcontent%2Fgeometrixx%2Fen&item=%2Fcontent%2Fgeometrixx%2Ffr
Let’s now dive into the demo, and see how by extending the Page Component Dialog we can also customize the different views we’ve mentioned before.
I’m working here on a custom page component, which is inheriting from the Foundation Page, and I’ve changed the Geometrixx pages so that they use my component.
First thing to notice, is that I’m overriding the cq:dialog node and only providing the parts that I’m interested in changing.
For instance, I’m adding an RTE field labeled “abstract” under the first tab, within the “More titles” section, which I want to place before the “description” field and to allow to be bulk editable. BTW if you’re interested in the whole “how can I create custom widgets” in Touch UI, this one could be worth looking at.
And I’m also customizing the “Create” view by showing the Thumbnail tab instead to the Advanced tab simply by using flags on the corresponding resources that I want to hide/show.
Last but not least, I can also update some existing fields: for example here, I can make the “subtitle” field required (that way, it’ll be automatically shown on “create”), and add a custom validation for that field. To add validation, I simply specify a validation property with the value of my custom validator, which is defined in my clientlib. That’s basically how we do now validation in TouchUI.