July 04, 2020
Howdy fellow developers 👋. In this post, we will start our formal development on AEM by creating components for pages and templates for creating pages. Before we being, let’s see what are components and templates in AEM.
In the previous posts, we discussed that digital marketers use AEM to show content to the users of their websites. The content creation process in AEM is hassle free and quick. This content is configurable also. This is possible due to the components.
Components are the basic building blocks of an AEM website. They can be easily dragged and dropped on to the 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 technicalities of the website.
It is the job of the 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 their different 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. 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 🤩?
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.
You must have noticed while creating component we set Super Type property as core/wcm/components/page/v2/page
. What does this mean 🤔?
It mean we are leveraging Core Components (discussed here). Using sling:resourceSuperType
property we inherit 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.
Navigate to /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>
Now, create a new node “initial” under “base-page” with the following 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"
jcr:primaryType="cq:Page">
<jcr:content
jcr:primaryType="cq:PageContent"
sling:resourceType="aemtutorials/components/structure/page"/>
</jcr:root>
Create another node “policies” under “base-page” with the following 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="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>
Finally, create another node “structure” under “base-page” with the following 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="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
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.Paste the following code in the 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.
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 JS. In this case, if properties.text
exists, then only it will execute the <h3>
tag inside it.
We will discuss HTL in more detail in the later part of this development series.
Create a new node “cq:dialog” under the /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 open a 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 for rendering 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 about 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 🙏.