AEM front-end notebook

Nate Steiner
6 min readOct 17, 2018

This post is a grab-bag of various tips and tricks I’ve learned along the way (or have searched for and found the answers). This is not in any particular order, it’s also a work-in-progress so I’ll add more as I go.

Override link checker for particular links

So you don’t get those chain icons showing broken links for special cases. Two ways to do it, in both cases just add these to the anchor tag in your template, they won’t be rendered in the final HTML output: x-cq-linkchecker=”valid” or x-cq-linkchecker=”skip”

Store images/fonts in clientlib in a way that can be accessed

Use the folder name resources store fonts and other assets that you want to access via your clientlib. But in general it’s better to keep images editable and stored in the DAM so they can be modified without code changes.

Process SCSS/SASS to CSS in minimal and AEM friendly way

There’s a million ways to do this. You can use a more feature-rich webpack approach, but if you just want to slightly improve AEM’s built in LESS processing feature, then you can have Gulp process SCSS files inline (create CSS output wherever the SCSS file is found). Here’s a gist for how I do that: https://gist.github.com/nsteiner/d262df03ecf0cac020136d4c34f2a165

Features of this method: inline sourcemap generation, autoprefixing, and SASS processing. That’s it. 🙂

Usage: Put the package and gulpfile in the root dir (or elsewhere if you prefer) and run npm install to get the dependencies. Then either run gulp whenever you want to, or fold it into maven or a build script. The big win with this method is that it’s very hands-off, so you can put your SCSS files (and CSS output) wherever it makes sense to within your clientlib folders.

Recompiling old AEM JSPs

On older AEM installs you sometimes have to recompile JSPs to clear errors and see updates. There’s a special URL you can use to access a button for clearing: http://localhost:4502/system/console/slingjsp

This page should have a “Recompile all JSPs” button it, click and retest.

Unwrapping AEM component wrappers

AEM will add wrapper DIV tags with classnames to your components, and usually that’s a good thing (for the admin UI purposes). If you need to remove it, you can add this to the component’s .content.xml file: cq:noDecoration=”{Boolean}true”

Customizing AEM component wrappers

Removing the component wrapper as described above can be problematic for the admin UI. If you just want to ensure that some class names are added to it, you can add a node to configure this. One way to do that is to add this within the jcr:root tag of .content.xml file for the component:

<cq:htmlTag
jcr:primaryType="nt:unstructured"
class="my_important_class another_class" />

This seems to work better for for nested components especially.

AEM CSS class names you may want to avoid

AEM generally gives you the tools to produce HTML markup the way you want, and usually scopes it’s own styles nicely (e.g. .cq-Editable-dom) but here are some classnames you might want to avoid, just in case. I’ll add to this list if I find more.

  • .section
  • .page

Avoiding zero height parsys and other weird artifacts of a half-working touch-ui edit mode experience

If you’ve started from nearly scratch and are being very selective about what AEM code is surfaced client-side, you’ll want to ensure that the “init” component is included, perhaps on the main page component itself. Not having this will cause a bunch of problems for the edit mode interface.

<sly data-sly-include="/libs/wcm/core/components/init/init.jsp"/>

Searching the content

A common scenario occurs after pages or chunks of content are established, you’ll need to find out how many times a component is used in a site, or how many pages were generated by way of a specific template. You can use SQL to search for these types of things in CRX/DE by using the query tool, choose SQL2 if you’re using the examples below.

Example Below: searching for all pages made with a specific template

SELECT * FROM [nt:unstructured] AS page WHERE ISDESCENDANTNODE(page, "/content/project_name/en") AND [cq:template] = "/apps/project_name/templates/template_name"

Example Below: same as pervious example but adding a filter for nodes made before a certain date

SELECT * FROM [nt:unstructured] AS page WHERE ISDESCENDANTNODE(page, "/content/project_name/en") AND ([cq:template] = "/apps/project_name/templates/template_name") AND ([jcr:created]  < '2019-01-30T00:25:12.687Z')

Self-close IMG tags

Image tags in HTL with data-sly-test in them, seem to work as expected only when they are self-closed in the old XHTML style <img src="foo.jpg" />

Heading in a dialog

Sometimes it’s helpful to add a heading inside your dialogs, here’s how to do that. The tag name dialogHeading in the example below is arbitrary.

<dialogHeading
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/heading"
level="3"
text="My Heading"/>

Pulling one component into another

In order to get a fully editable component inside another one, you need to both declare a new node path (just a name in this case) and also point to the component using the resourceType attribute.

<sly data-sly-resource="${ @ path='mySpecialNodeName', resourceType='project/path/to/components/componentName'}" />

Other options include:

  • create a par, so authors can drag in the component(s)
  • if authoring isn’t needed, use an include to the file inside the component (start from /apps/ in this case)

Using Numbers

In AEM, adding a numberfield to your dialog does not generate a number, it generates a string. This can be confusing when you want to use the value later for comparison. For example, if you have a long list of items, and you want the author to be able to limit how many are shown. Using data-sly-test will fail because you can’t compare the number of items in a list against a string. To fix, you can adjust the value of your field to a number using @typehint which (when used in the dialog) ends up being a little extra hidden node you add.

So if you had a numberfield in your dialog named maxItems then you’d add this underneath it:

<maxItemsTypeHintjcr:primaryType="nt:unstructured"sling:resourceType="granite/ui/components/foundation/form/hidden"ignoreData="{Boolean}true"name="./maxItems@TypeHint"value="Decimal"/>

The important pieces here are that you use the ignoreData attribute on this new hint node, and that you reference the field you want to use in the name, and add @TypeHint to the end of it. There are other field types, but in this case, we want Decimal

What happened to my footer? Or: Authoring has a bug with viewport height CSS

If you find that in author mode your page keeps growing in height, it may be because of a bug where use of viewport height CSS is misunderstood by the authoring environment. You can work around this by providing alternate CSS for the authoring context. One option would be to overwrite the VH style with an author-only clientlib. Another option is to provide yourself a utility class so we know when the authoring environment is in use.

<sly data-sly-test.author="${wcmmode.edit || wcmmode.preview || wcmmode.design}"></sly><body class="${author ? 'wcmmode':'' @ context = 'attribute'}">

Then you can use your new .wcmmode class as part of a stronger selector.

Identify a component instance in a page

You can use the component’s node name, it should be unique. This is probably not suitable for all cases (I think weird names are possible), but when you add a second instance of a component, it will automatically get a unique node name. As in myComponent will get myComponent_copy or something similar depending on how it was created. If you need that unique id, you can reference it with:

${resource.name}

Another option would be to create a field for authoring a unique slug. This is also not foolproof as authors could miss-use this.

Make use of the total number of items in a list for simple cutoffs in HTL alone

Unfortunately there’s no ${itemList.total} but if you need to, for example, render a button when there’s more than 4 items — you can do this type of logic with:

<sly data-sly-list="${whatever}">
<sly data-sly-test.showButtonForMore="${itemList.last && itemList.count > 4}"/>
</sly>
<button data-sly-test="${showButtonForMore}">More Items</button>

More…

More tidbits will be put in here over time. I update this page pretty frequently.

--

--