Components are at the core of building an experience in AEM. The Core Components and the AEM Project Archetype make it simple to get started with a tool set of ready-made, robust components. The WKND Tutorial takes the developer through how to use these tools and how to build custom components in order to create a new AEM site.
Before references this document, make sure you have completed the WKND Tutorial and are thus familiar with the Core Components and the AEM Project Archetype.
Because the WKND Tutorial covers most use cases, this document is intended only as a supplement to those resources. It gives in-depth technical specifics about how components are structured and configured in AEM and is not intended as a getting started guide.
This section covers key concepts and issues as an introduction to the details needed when developing your own components.
Before starting to actually configure or code your component you should ask:
Before you invest time in creating an entirely new component, consider customizing or extending existing components. The Core Components offer a suite of flexible, robust, and well-tested production-ready components.
The Core Components also offer clear customization patterns that you can use to adapt them to the needs of your own project.
Components can also be redefined with an overlay based on the search path logic. However in such case, the Sling Resource Merger will not be triggered and /apps
must define the entire overlay.
It is also possible to override a component dialog using the Sling Resource Merger and defining the property sling:resourceSuperType
.
This means you only need to redefine the required differences, as opposed to redefining the entire dialog.
Your component will be rendered with HTML. Your component needs to define the HTML needed to take the required content and then render it as required, on both the author and publish environments.
It is recommended to keep the code responsible for markup and rendering separate from the code that controls the logic used to select the component’s content.
This philosophy is supported by HTL, a templating language that is purposely limited to ensure a real programming language is used to define the underlying business logic. This mechanism highlights the code that is called for a given view and, if required, allows specific logic for different views of the same component.
This (optional) logic can be implemented in different ways and is invoked from HTL with specific commands:
The structure of an AEM component is powerful and flexible. The main parts are:
A key element of the structure is the resource type.
This is an abstraction that helps to ensure that even when the look and feel changes over time, that the intention stays the time.
The definition of a component can be broken down as follows:
/libs/core/wcm/components
./apps/<myApp>/components
.cq:Component
and have the key elements:
cq:Component
definition.<mycomponent> (cq:Component)
- Hierarchy node of the component.jcr:title
- Component title; for example, used as a label when the component is listed in the Components Browser and Components Consolejcr:description
- Description for the component; used as mouse-over hint in the Components Browser and Components Consolecq:editConfig (cq:EditConfig)
- Defines the edit properties of the component and enables the component to appear in the Components Browser
cq:childEditConfig (cq:EditConfig)
- Controls author UI aspects for child components that do not define their own cq:editConfig
.cq:dialog (nt:unstructured)
- Dialog for this component. Defines the interface allowing the user to configure the component and/or edit content.cq:design_dialog (nt:unstructured)
- Design editing for this componentThe icon or abbreviation for the component is defined via JCR properties of the component when the component is created by the developer. These properties are evaluated in the following order and the first valid property found is used.
cq:icon
- String property pointing to a standard icon in the Coral UI library to display in the component browser
abbreviation
- String property to customize the abbreviation of the component name in the component browser
jcr:title
property.
abbreviation_commentI18n
property, which is then used as translation hint.cq:icon.png
or cq:icon.svg
- Icon for this component, which is shown in the Component Browser
.png
and .svg
files are supported._cq_icon.png
or _cq_icon.svg
for example..png
takes precedent over .svg
if both are present.If none of the above properties (cq:icon
, abbreviation
, cq:icon.png
or cq:icon.svg
) are found on the component:
sling:resourceSuperType
property.jcr:title
property of the current component.To cancel the inheritance of icons from super components, setting an empty abbreviation
property on the component will revert to the default behavior.
The Component Console displays how the icon for a particular component is defined.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink" x="0px" y="0px"
width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
<ellipse cx="5" cy="5" rx="3" ry="3" fill="#707070"/>
<ellipse cx="15" cy="5" rx="4" ry="4" fill="#707070"/>
<ellipse cx="5" cy="15" rx="5" ry="5" fill="#707070"/>
<ellipse cx="15" cy="15" rx="4" ry="4" fill="#707070"/>
</svg>
Many of the nodes/properties needed to define a component are common to both UIs, with differences remaining independent so that your component can work in both environments.
A component is a node of type cq:Component
and has the following properties and child nodes:
Name | Type | Description |
---|---|---|
. |
cq:Component |
This represents the current component. A component is of node type cq:Component . |
componentGroup |
String |
This represents the group under which the component can be selected in the Components Browser. A value beginning with . is used for components that are not available for selection from the UI such as base components from which other components inherit. |
cq:isContainer |
Boolean |
This indicates whether the component is a container component and therefore can contain other components such as a paragraph system. |
cq:dialog |
nt:unstructured |
This is the definition of the edit dialog for the component. |
cq:design_dialog |
nt:unstructured |
This is the definition of the design dialog for the component. |
cq:editConfig |
cq:EditConfig |
This defines the edit configuration of the component. |
cq:htmlTag |
nt:unstructured |
This returns additional tag attributes that are added to the surrounding HTML tag. Enables addition of attributes to the automatically generated divs. |
cq:noDecoration |
Boolean |
If true, the component is not rendered with automatically generated div and css classes. |
cq:template |
nt:unstructured |
If found, this node will be used as a content template when the component is added from the Components Browser. |
jcr:created |
Date |
This is the date of creation of the component. |
jcr:description |
String |
This is the description of the component. |
jcr:title |
String |
This is the title of the component. |
sling:resourceSuperType |
String |
When set, the component inherits from this component. |
component.html |
nt:file |
This is the HTL script file of the component. |
cq:icon |
String |
This value points to the icon of the component and appears in the Components Browser. |
If we look at the Text component, we can see a number of these elements:
Properties of particular interest include:
jcr:title
- This is the title of the component used to identify the component within the Components Browser.jcr:description
- This is the description for the component.sling:resourceSuperType
- This indicates the path of inheritance when extending a component (by overriding a definition).Child nodes of particular interest include:
cq:editConfig
- This controls visual aspects of the component when editing.cq:dialog
- This defines the dialog for editing content of this component.cq:design_dialog
- This specifies the design editing options for this component.Dialogs are a key element of your component as they provide an interface for authors to configure the component on a content page and provide input for that component. See the authoring documentation for details on how content authors interact with components.
Depending on the complexity of the component your dialog may need one or more tabs.
Dialogs for AEM components:
cq:dialog
nodes of type nt:unstructured
.cq:Component
nodes and next to their component definitions.sling:resourceType
property.nt:unstructured
with the required sling:resourceType
property.Within the dialog, individual fields are defined:
Design dialogs are similar to the dialogs used to edit and configure content, but they provide the interface for template authors to pro-configure and provide design details for that component on a page template. Page templates are then used by the content authors to create content pages. See the template documentation for details on how templates are created.
Design dialogs are used when editing a page template, though they are not needed for all components. For example the Title and Image Components both have design dialogs, whereas the Social Media Sharing Component does not.
Coral UI and Granite UI define the look and feel of AEM.
Granite UI provides a large range of the basic widgets needed to create your dialog on the authoring environment. When necessary you can extend this selection and create your own widget.
For additional details see the following resources:
To create a new widget for use in a component dialog requires you to create a new Granite UI field component.
If you consider your dialog as a simple container for a form element, then you can also see the primary content of your dialog content as form fields. Creating a new form field requires you to create a resource type; this is equivalent to creating a new component. To help you in that task, Granite UI offers a generic field component to inherit from (using sling:resourceSuperType
):
/libs/granite/ui/components/coral/foundation/form/field
More specifically Granite UI provides a range of field components that are suitable for use in dialogs, or more generally speaking in forms.
Once you have created your resource type, you can instantiate your field by adding a new node in your dialog, with the property sling:resourceType
referring to the resource type you have just introduced.
You can also use render conditions (rendercondition
) to control who has access to specific tabs/fields in your dialog; for example:
+ mybutton
- sling:resourceType = granite/ui/components/coral/foundation/button
+ rendercondition
- sling:resourceType = myapp/components/renderconditions/group
- groups = ["administrators"]
Once you have create a component, you need to enable it in order to use it. Using it shows how the structure of the component relates to the structure of the resulting content in the repository.
Once a component has been defined it must be made available for use. To make a component available for use in a template, you must enable the component in the policy of the layout container of the template.
See the template documentation for details on how templates are created.
If we create and configure an instance of the Title component on the page: /content/wknd/language-masters/en/adventures/extreme-ironing.html
Then we can see the structure of the content created within the repository:
In particular, if you look at the actual text of a Title Component:
jcr:title
property holding the actual text of the title that the author entered.sling:resourceType
reference to the component definition.The properties defined are dependent on the individual definitions. Although they can be more complex than above they still follow the same basic principles.
Components within AEM are subject to the Resource Type Hierarchy. This is used to extend components using the property sling:resourceSuperType
. This enables the component to inherit from another component.
See the section Reusing Components for more information.
This section explains how to configure the edit behavior of a component. This includes attributes such as actions available for the component, characteristics of the in.place editor, and the listeners related to events on the component.
The edit behavior of a component is configured by adding a cq:editConfig
node of type cq:EditConfig
below the component node (of type cq:Component
) and by adding specific properties and child nodes. The following properties and child nodes are available:
cq:editConfig
node propertiescq:editConfig
child nodes:
cq:dropTargets
(node type nt:unstructured
): defines a list of drop targets that can accept a drop from an asset of the content finder (a single drop target is allowed)cq:inplaceEditing
(node type cq:InplaceEditingConfig
): defines an in-place editing configuration for the componentcq:listeners
(node type cq:EditListenersConfig
): defines what happens before or after an action occurs on the componentThere are many existing configurations in AEM. You can easily search for specific properties or child nodes using the Query tool in CRXDE Lite.
Components must always render some HTML that is visible to the author, even when the component has no content. Otherwise it might visually disappear from the editor’s interface, making it technically present but invisible on the page and in the editor. In such a case the authors won’t be able to select and interact with the empty component.
For this reason, components should render a placeholder as long as they don’t render any visible output when the page is rendered in the page editor (when the WCM mode is edit
or preview
).
The typical HTML markup for a placeholder is the following:
<div class="cq-placeholder" data-emptytext="Component Name"></div>
The typical HTL script that renders the above placeholder HTML is the following:
<div class="cq-placeholder" data-emptytext="${component.properties.jcr:title}"
data-sly-test="${(wcmmode.edit || wcmmode.preview) && isEmpty}"></div>
In the previous example, isEmpty
is a variable that is true only when the component has no content and is invisible to the author.
To avoid repetition, Adobe recommends that implementers of components use an HTL template for these placeholders, like the one provided by the Core Components.
The usage of the template in the previous link is then done with the following line of HTL:
<sly data-sly-use.template="core/wcm/components/commons/v1/templates.html"
data-sly-call="${template.placeholder @ isEmpty=!model.text}"></sly>
In the previous example, model.text
is the variable that is true only when the content has content and is visible.
An example usage of this template can be seen in the Core Components, such as in the Title Component.
The cq:dropTargets
node (node type nt:unstructured
) defines the drop target that can accept a drop from an asset dragged from the content finder. It is a node of type cq:DropTargetConfig
.
The child node of type cq:DropTargetConfig
defines a drop target in the component.
An in-place editor allows the user to edit content directly in the content flow, without the need to open a dialog. For example, the standard Text and Title components both have an in-place editor.
An in-place editor is not necessary/meaningful for every component type.
The cq:inplaceEditing
node (node type cq:InplaceEditingConfig
) defines an in-place editing configuration for the component. It can have the following properties:
Property Name | Property Type | Property Value |
---|---|---|
active |
Boolean |
true to enable the in-place editing of the component. |
configPath |
String |
Path of the editor configuration, which can be specified by a configuration node |
editorType |
String |
The available types are: plaintext for non HTML content, title converts graphical titles into a plaintext before editing begins, and text uses the Rich Text Editor |
The following configuration enables the in-place editing of the component and defines plaintext
as the editor type:
<cq:inplaceEditing
jcr:primaryType="cq:InplaceEditingConfig"
active="{Boolean}true"
editorType="plaintext"/>
The method of handling events on dialog fields is done with listeners in a custom client library.
To inject logic into your field, you should:
To achieve this you need to know about the underlying widget library with which you want to interact. See the Coral UI documentation to identify to which event you want to react.
The cq:listeners
node (node type cq:EditListenersConfig
) defines what happens before or after an action on the component. The following table defines its possible properties.
Property Name | Property Value |
---|---|
beforedelete |
The handler is triggered before the component is removed. |
beforeedit |
The handler is triggered before the component is edited. |
beforecopy |
The handler is triggered before the component is copied. |
beforeremove |
The handler is triggered before the component is moved. |
beforeinsert |
The handler is triggered before the component is inserted. |
beforechildinsert |
The handler is triggered before the component is inserted inside another component (containers only). |
afterdelete |
The handler is triggered after the component is removed. |
afteredit |
The handler is triggered after the component is edited. |
aftercopy |
The handler is triggered after the component is copied. |
afterinsert |
The handler is triggered after the component is inserted. |
aftermove |
The handler is triggered after the component is moved. |
afterchildinsert |
The handler is triggered after the component is inserted inside another component (containers only). |
In the case of nested components there are certain restrictions on actions defined as properties on the cq:listeners
node. For nested components, the values of the following properties must be REFRESH_PAGE
:
aftermove
aftercopy
The event handler can be implemented with a custom implementation. For example (where project.customerAction
is a static method):
afteredit = "project.customerAction"
The following example is equivalent to the REFRESH_INSERTED
configuration:
afterinsert="function(path, definition) { this.refreshCreated(path, definition); }"
With the following configuration the page is refreshed after the component has been deleted, edited, inserted or moved:
<cq:listeners
jcr:primaryType="cq:EditListenersConfig"
afterdelete="REFRESH_PAGE"
afteredit="REFRESH_PAGE"
afterinsert="REFRESH_PAGE"
afterMove="REFRESH_PAGE"/>
Field validation in Granite UI and the Granite UI widgets is done by using the foundation-validation
API. See the foundation-valdiation
Granite documentation for details.
If you have a custom JavaScript that needs to be executed only when the dialog is available and ready, you should listen for the dialog-ready
event.
This event is triggered whenever the dialog loads (or re-loads) and is ready for use, which means whenever there is a change (create/update) in the DOM of the dialog.
dialog-ready
can be used to hook in JavaScript custom code that performs customizations on the fields inside a dialog or similar tasks.
The WCM Mode cookie is set when switching to Preview mode even when the page is not refreshed.
For components with a rendering that are sensitive to the WCM Mode, they need to be defined to refresh themselves specifically, then rely on the value of the cookie.
As a developer, you want easy access to component documentation so that you can quickly understand the component’s:
For this reason, it is quite easy to make any existing documentation markdown you have available within the component itself.
All you need to do is place a README.md
file in the component structure.
This markdown will then be displayed in the Component Console.
The supported markdown is the same as that for Content Fragments.