June 06, 2021
Howdy fellow developers 👋. In this post, we will finally get our hands dirty with code by creating components for pages and templates for creating pages. Before we begin, let’s see what are components and templates in AEM.
In previous posts, we discussed that digital marketers use AEM to show content of their website to the end users. The content creation process in AEM is hassle-free and quick. Those digital marketers can also change the website content on the fly. All of this is possible with components which provide maximum level of abstraction for non-technical users.
Components are the basic building blocks of an AEM website. They can be easily dragged and dropped on to a page. The digital marketers (or content authors) add content (which can be anything — text, image, video, tables etc.) via these components on the websites. Content authors don’t have to worry about the internal details of the implementation of these components.
It is the job of AEM developers to convert the business requirements into a fully functional component. Once a component is developed, it can be configured to use anywhere on the website any number of times.
Any website is composed of webpages. In AEM, these webpages are created by content authors, and they use components on these pages for putting content. But as you must have seen, most websites have common layout in many webpages; only the content is different on different pages. For e.g., header, footer, navigation, sidebar etc. are same in almost every page of a website. The content in these components doesn’t usually change.
It is obviously not a good idea to create these common components on every page again and again. I mean where is reusability 🤔? Wouldn’t it be great if we create this common content once and use it on every page automatically? Yes, of course 😍.
AEM templates are the blueprint for every page on the website. In simpler terms, we can create many pages based on a template and all these pages will have the same layout. We can have multiple templates also. For e.g., we can have different templates for creating product details and landing pages. This is cool, ain’t it 🤩? Well to be honest, AEM is all about cool things.
In this section, we will be creating components for our template i.e., those components which will be common to all the pages based on a template.
/apps/aemtutorials/components
➡ right click ➡ Create… ➡ Create Folder… ➡ Save (CTRL + S).
/apps/aemtutorials/components/structure
➡ Create… ➡ Create Component… and add following details in the dialog ➡ Next ➡ OK ➡ Save.
Here we are using Group as .hidden. It means this component is not for content the authors to use. This component will be used for creating the template which rarely needs to be changed. If it is necessary to change it, only super admin should be able to do it.You must have noticed while creating component we set Super Type property as core/wcm/components/page/v2/page
. What does this mean 🤔?
It means we are leveraging Core Components (discussed here). By using sling:resourceSuperType
property we are inheriting the properties of the core component’s page component in our page component. It works similar to the OOP concept of inheritance i.e., if some property is not present in the current entity, it will use the same properties defined in the parent of the entity.
Since for this tutorial, we are not going to create our custom layout, hence we deleted the page.jsp file. In real world scenarios, we put our custom implementations including scripts, styles etc. in the component. This can be done by overriding the behavior of core page component.
/conf/aemtutorials/settings/wcm/template-types
and create a new node base-page of type of cq:Template
with the following properties - <?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0"
jcr:mixinTypes="[mix:lockable]"
jcr:primaryType="cq:Template"
ranking="{Long}1">
<jcr:content
jcr:description="Base template for creating pages"
jcr:primaryType="cq:PageContent"
jcr:title="Base Template Type"/>
</jcr:root>
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="cq:Page">
<jcr:content
jcr:primaryType="cq:PageContent"
sling:resourceType="aemtutorials/components/structure/page"/>
</jcr:root>
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="cq:Page">
<jcr:content
jcr:primaryType="nt:unstructured"
sling:resourceType="wcm/core/components/policies/mappings">
<root
jcr:primaryType="nt:unstructured"
sling:resourceType="wcm/core/components/policies/mapping"/>
</jcr:content>
</jcr:root>
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="cq:Page">
<jcr:content
cq:deviceGroups="[mobile/groups/responsive]"
jcr:primaryType="cq:PageContent"
sling:resourceType="aemtutorials/components/structure/page">
<root
jcr:primaryType="nt:unstructured"
sling:resourceType="wcm/foundation/components/responsivegrid"/>
<cq:responsive jcr:primaryType="nt:unstructured">
<breakpoints jcr:primaryType="nt:unstructured">
<phone
jcr:primaryType="nt:unstructured"
title="Smaller Screen"
width="{Long}650"/>
<tablet
jcr:primaryType="nt:unstructured"
title="Tablet"
width="{Long}1200"/>
</breakpoints>
</cq:responsive>
</jcr:content>
</jcr:root>
Now, we have our template ready, we will create a sample component and a page (based on the template created above) to put component on it.
/apps/aemtutorials/components
in CRXDE and create a new folder named content./apps/aemtutorials/components/content
➡ Create… ➡ Create Component… and add following details in the dialog ➡ Next ➡ OK ➡ Save.
/apps/aemtutorials/components/content/text
and delete everything in it.text.html
file - <sly data-sly-use.template="core/wcm/components/commons/v1/templates.html" />
<sly data-sly-call="${template.placeholder @ isEmpty=!properties.text}" />
<sly data-sly-test="${properties.text}">
<h3>Text entered by the user is: ${properties.text}</h3>
</sly>
You must be wondering what is this weird looking code 🤔? This is HTL (HTML Template Language), a language developed by Adobe on top of plain HTML. You can use JavaScript type syntax in it for basic operations such as conditionals, loops, operators etc. But remember, HTL still enables markup, therefore, it has limited capabilities as far as business logic is concerned.
The first two lines of above code are used for putting a placeholder until the component’s field(s) are not configured. It uses HTML5’s data-*
attribute to store information with HTL specific sly
.
Line 3 is using a basic conditional data-sly-test
which acts as truthy/falsy conditions in JavaScript. In this case, if properties.text
exists, then only it will display the <h3>
tag in the markup.
We will discuss HTL in more detail in the later part of this development series.
/apps/aemtutorials/components/content/text
of type nt:unstructured with the following children nodes and properties - <?xml version="1.0" encoding="UTF-8"?>
<jcr:root
xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Text"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<text
jcr:primaryType="nt:unstructured"
jcr:title="Text"
sling:resourceType="granite/ui/components/foundation/section">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"
margin="{Boolean}false"/>
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<text
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldDescription="Enter the text of your choice"
fieldLabel="Text"
name="./text"
useFixedInlineToolbar="{Boolean}true"/>
</items>
</column>
</items>
</text>
</items>
</content>
</jcr:root>
cq:dialog is the node that defines the component’s dialog and its properties. Thus, whenever a user opens the component’s dialog for editing, fields mentioned in the cq:dialog will be shown. In this case, there is only one field named Text.
Also, see this definition carefully -
<text
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldDescription="Enter the text of your choice"
fieldLabel="Text"
name="./text"
useFixedInlineToolbar="{Boolean}true"/>
Here the name property defines the identifier which signify the name under which field value is saved in the JCR. This can be accessed in the code using properties (predefined) object.
sling:resourceType defines the path in the JCR which denotes the location of scripts used to render the fields/components.
Congratulations 👏 👏 👏 👏 we have created your first AEM component. It’s time to pat our backs.
~/ui.apps/src/main/content/jcr_root/apps/aemtutorials
➡ IntelliVault ➡ Pull from CRX…~/ui.content/src/main/content/jcr_root/conf/aemtutorials/
and /ui.content/src/main/content/jcr_root/content/aemtutorials
.Since this post only discusses the basics of creating a component in AEM, the component only has basic functionality without any fancy styling. As we will dive deeper into AEM development, we will discuss these things also.
Phewww 😫 this was a long post and a lot of work. But we didn’t give up and created our first AEM template, page and component. We should be proud of ourselves 😄.
I have pushed this project on GitHub — AEM Tutorials. You can refer to it in case you miss something. Also, if you like the post then fork it, star it and contribute into it.
I would love to hear your thoughts on this post and would like to have suggestions from you to make this post better.
Happy Learning 😊 and Namaste 🙏.