Exadel’s Marketing Technology Team is constantly striving to improve client experiences across the board.

In this article, we’d like to walk you through the AEM Sling models and how easily they can be combined with the Exadel Authoring Kit ― created by AEM developers at Exadel.

Building AEM components with Sling models

Generally, contemporary AEM components are based on AEM Sling models. The pattern that allows you to use Sling models in AEM is called injection. We create fields, attach Sling injector annotations to them, add getters and, thus, receive data-filled objects.

Many developers can hardly imagine how diversified the range of injectors and corresponding AEM annotations is. You can come across the standard Sling annotations. More annotations are added in the well-known ACS AEM Commons package. In addition, you can create your own custom injectors with AEM annotations.

Concerning which injector annotations to use, you might think that @Inject annotation is the remedy for all injection cases since multiple tutorials tell you so. But, that’s not quite true, and, using @Inject, in particular, is less favorable. You can find evidence in the code provided by Adobe Consulting Services, or in this article offering a deep dive into Sling models.

The Exadel Marketing Technology Team is successfully utilizing multiple AEM annotations from Apache Sling. We’ve learned to combine annotations coming from Sling with the annotations from the Exadel Authoring Kit, as well as Lombok annotations necessary for generating accessors for Java class fields. The combinations we create help us reduce boilerplate code. You can visualize things better with the examples below.

The correlation between naming

The Exadel Authoring Kit’s annotations such as @DialogField, @TextField, and Apache Sling annotations such as @ValueMapValue, @Default, even Lombok’s annotations such as @Getter can be put onto the same entities and work together successfully.

AEM Sling models

Mind that in @DialogField, you don’t have to specify the name attribute which describes the name of the stored property. If it is missing, it will be implied from the name of the annotated variable. This will also work with @ValueMapValue if it doesn’t have the name set as well.

However, if @DialogField contains a name that isn’t the same as the name of the annotated field, you need to specify the same name in @ValueMapValue.

This happens because the @DialogField name defines which name the value is saved with from the AEM dialog into the node. When it is not default, you need to specify which property should read the value for the @ValueMapValue annotation.

It’s interesting that the @DialogField(name = “…”) can be a relative path. This lets you combine values into subnodes for the sake of convenience and beauty. However, in this case, the @ValueMapValue should also refer to a relative path.

Additionally, the ability of the @ValueMapValue to work with relative paths is really convenient even when it’s not connected with @DialogField. For instance, if you’re using @ImageUpload that usually saves the path to the image into the related node, you can extract this value from the related node without extra effort with @ValueMapValue.

You can use the Sling’s @Default annotation to attribute the default value if you can’t inject the property nodes.

Supplying fieldsets with Sling-injected data

We often use fieldsets in AEM Touch UI dialogs to organize data. Generally, a fieldset is handy when you want to reuse the data structure that is required in different places. For example, most web pages have links and link buttons, and nearly every link is characterized by a title, reference, and “open in a new window” flag. You may naturally want to combine these three values into the fieldset.

You can add fieldsets into a dialog in AEM the following way:

To benefit from a fieldset, you need two ingredients: an arbitrary Java class that will represent the fieldset itself, and a Java field or method in another class annotated with @FieldSet.

Note that @FieldSet can be attached to the field that references the class the Kit will use to create widgets for the fieldset itself. Then you can kill two birds with one shot: provide input grouping in the AEM dialog and simultaneously apply the composition pattern for the Sling model.

Other than that, you can add @FieldSet to a field of any type.

If you’re using the composition, then your major concern is how JCR values that correspond to the dialog fields will be injected into the fields of the fieldset class.

Annotating fieldsets with Sling

To begin with, your fieldset should also be a Sling model. If your current model is adapted from the resource, you can’t inject anything that is bound to a Sling request. On the other hand, you can adapt virtually anything bound to a resource when adapting your mode from a request.

Additionally, you need to decide which Sling annotations to use inside the fieldset class.

The easiest way is to use @Self. AEM will try to inject the properties of the current request into the fields of the fieldset.

Be careful though. If you get two similar fieldsets in a single parent class (let’s say primaryLink and secondaryLink), then the same values will be injected into both of them.

Therefore, it’s better if the injected values originate from the sibling node. This requires a bit of preparation from both the Exadel Authoring Kit and Sling annotations in AEM.

Adding the Exadel Authoring Kit’s annotations to fieldsets

On the Kit’s end, you add @FieldSet(namePrefix = “primaryLink/”) and send the fieldset data to the child node called primaryLink.

To get it back in an AEM component (a Sling model), you can use the @ChildResource annotation or @ChildResourceFromRequest.

These have their limitations. For instance, @ChildResource only manages Sling models that are adapted from the resource and @ChildResourceFromRequest. Is only there when the ACS AEM Commons package is installed. Luckily, the Exadel Authoring Kit 2.1 version brought its brand new @Child annotation that works with either resources or requests and has many extra features.

How Sling annotations work with AEM multifields

Now let us move to AEM multifields. From the back end point of view, a multifield is usually built upon a separate Java class which is most often a Sling model as well.

Just like @FieldSet, the @Multifield annotation can recognize which class to use as the source of AEM dialog fields.
This is possible since the annotation can look at the field type an AEM dialog stands on. So, @Multifield can recognize the type of the list. Just the same way it works with the fieldset, you can specify this class manually.

As a result of using an AEM multifield (if it has more than two fields), a subnode will be created. You should inject data into a Sling model from the subnode.

Luckily, standard @ChildResource can inject both a resource and a collection of resources, and so can the brand new Exadel Authoring Kit’s @Children annotation and injector. Other than that, it allows filtering the injected resources with a condition, specifying properties’ prefixes and postfixes and more.

Not every multifield result is created in a subnode, though. There are two exceptions to this rule.

Managing simple and complex AEM multifields

The first is an AEM multifield that has only one field in the defining Java class.

The second is a multifield built with @Multiple. Such an AEM multifield doesn’t need a dedicated Sling model class, because it creates a property that is structured like an inline array of values. You will be able to inject this value in an array-typed or list-typed field with the use of @ValueMapValue.

You can use other typical Sling annotations. In our projects, we often employ @SlingObject, @ScriptVariable, @OSGIService, or others. Nonetheless, these annotations will hardly be placed side by side in the same fields with the Sling annotations of the Kit.

Speaking of placing Sling annotations side by side with Exadel Authoring Kit, many developers dislike it when there are too many annotations in a Java class field. Sometimes, it even diverts them from using annotations in favor of configuration files per se. The team of Exadel Authoring Kit has been thinking about how to solve the issue which appeared since version 2 became live.

Bringing order to annotations in AEM components

There are basically two solutions. First of all, the annotations of the Exadel Authoring Kit can be placed both onto the fields and the methods. Why is this good? You can remove all the class members marked with Kit’s annotations and into another special class that will be coupled with the Sling model class separately. The natural solution is to split the dialog class into a class itself and an interface.

We can place the annotations of the Kit on the methods of the interface, and Sling annotations can be left in the class fields. Naturally, this class will implement the given interface, and you can make the class fields have the names that match the names of interface methods. This is especially convenient if you’re using Lombok, since it will automatically create getters that implement interface methods. And if you decide to rename an interface method in your integrated development environment (IDE), you won’t miss renaming a field in a class because the IDE will notify you of a possible mistake.

There’s a complication, though. If you put the annotations of Exadel Authoring Kit on the interface methods, you’ll need to take care of the order in which widgets will appear. You can easily do it through the @Place annotation with the before and after properties. You can use either of them, but not two simultaneously.

AEM Sling models

On the other hand, you can attach annotations of Exadel Authoring Kit to the class fields, whereas the @Model annotation will be placed on the interface.

Splitting an AEM dialog into “views”

The second solution is connected with the notion of views. With the Exadel Authoring Kit, you create an AEM Touch UI dialog for the given component, adding @AemComponent to the class. So, you’re not obliged to place all AEM dialog fields in the same class! With the views property, you can specify an unlimited number of other classes that will also take part in building the Touch UI interface. For instance, you can put @Dialog into one class and @DesignDialog into another, and @EditConfig into the third and the like.

If there are multiple fields in an AEM dialog, you may even want to divide up the dialog into several classes, establishing correlations between the classes. The fields of the parent class will be rendered above the fields of the child unless we have stated otherwise with before or after.

Exadel Authoring Kit and Apache Sling: Bring it together and shake it without stirring

Before wrapping up, pay special attention to the following: even though the AEM annotations of the Kit and Sling annotations work together, they still work at different layers. The bespoke annotations won’t have any influence on data stored in Java Content Repository (JCR) which Sling injects. For example, if we consider the default property in the @TextField annotation of the toolkit, it only records the default HTML attribute of the input component. If you open a dialog in AEM and close it without saving, it won’t affect JCR at all. Likewise, if you open a dialog in AEM and insert your value into a textfield, the textfield’s default attribute will just be ignored. And only if you save an AEM dialog without inserting your own value, the value of the default property will be stored in JCR. And this has nothing to do with Apache Sling’s @Default annotation which is used for reading (or rather, injecting) data and not for storing.

AEM Sling models made simple

If you understand the bespoke workarounds, you can create flexible AEM dialogs with complex structures that can organically blend with Sling models. We hope this article will be handy as you start working with Sling models in AEM.

We’d be delighted to provide a free consultation and offer our assistance to help you create an effective interface for your project, so don’t hesitate to get in touch with us!

Authors: Aliaksei Charniakou, Stepan Miakchilo, Liubou Masiuk

How can we help you?
Contact Us