Creating HTL Component

Components in aem are basic building blocks of content in web pages. Components in AEM can be developed in two ways, using JSP and HTML. It is recommended to use HTL (HTML Template Language) for creating components. Refer Sling Models and HTL for more details.

Model: Sling Model
View: HTL

Follow below steps to create a HTL Component and associated Sling Model.
In this DIY session we will create Promo Hero Component which has a Image, Tile, Description and Link fields.

1. Open CRXDE and Go to project folder (/apps/aemexplained/components/content).
2. Right-click on content folder and select create component.
3. Enter Label, Title and ComponentGroup in create component window.

4. Click Next and OK.
5. Rename promoherocomponent.jsp to promoherocomponent.html
6. Create cq:dialog under /apps/aemexplained/components/content/promoherocomponent as shown below.

<?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="Promo Hero"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
        <items jcr:primaryType="nt:unstructured">
            <column
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/container">
                <items jcr:primaryType="nt:unstructured">
                    <image
                        jcr:primaryType="nt:unstructured"
                        sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
                        fieldLabel="Image"
                        name="./image"
                        rootPath="/content/dam"/>
                    <title
                        jcr:primaryType="nt:unstructured"
                        sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                        fieldLabel="Title"
                        name="./title"/>
                    <description
                        jcr:primaryType="nt:unstructured"
                        sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                        fieldLabel="Description"
                        name="./description"/>
                    <buttons
                        jcr:primaryType="nt:unstructured"
                        sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
                        class="full-width"
                        composite="{Boolean}true"
                        fieldDescription="Click '+' to add a new button"
                        fieldLabel="Buttons">
                        <field
                            jcr:primaryType="nt:unstructured"
                            sling:resourceType="granite/ui/components/coral/foundation/container"
                            name="./buttons">
                            <items jcr:primaryType="nt:unstructured">
                                <well
                                    jcr:primaryType="nt:unstructured"
                                    sling:resourceType="granite/ui/components/foundation/container">
                                    <layout
                                        jcr:primaryType="nt:unstructured"
                                        sling:resourceType="granite/ui/components/foundation/layouts/well"/>
                                    <items jcr:primaryType="nt:unstructured">
                                        <name
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                            fieldLabel="Name"
                                            name="name"/>
                                        <link
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
                                            fieldLabel="Link"
                                            name="link"
                                            rootPath="/content/aemexplained"/>
                                    </items>
                                </well>
                            </items>
                        </field>
                    </buttons>
                </items>
            </column>
        </items>
    </content>
</jcr:root>

7. Import created component and dialog to the project.
8. Create Sling Model for the component as shown below. we create Interface so as to hide data members and method body.

PromoHero.java

package com.aem.explained.platform.core.models;

import java.util.List;

public interface PromoHero {

    String RESOURCE_TYPE ="aemexplained/components/content/promoherocomponent";

    String getImage();

    String getTitle();

    String getDescription();

    List<Link> getButtons();

}

PromoHeroComponent.java

package com.aem.explained.platform.core.components;

import com.aem.explained.platform.core.models.Link;
import com.aem.explained.platform.core.models.PromoHero;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.ExporterOption;
import org.apache.sling.models.annotations.Model;
import javax.inject.Inject;
import java.util.List;

@Model( adaptables = Resource.class,
        adapters = {PromoHero.class, PromoHeroComponent.class},
        resourceType = PromoHero.RESOURCE_TYPE,
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
@Exporter(name = "jackson", extensions = "json", options = {
        @ExporterOption(name = "MapperFeature.SORT_PROPERTIES_ALPHABETICALLY", value="true")
})
public class PromoHeroComponent implements PromoHero {

    @Inject
    private String image;

    @Inject
    private String title;

    @Inject
    private String description;

    @Inject
    private List<Link> buttons;

    @Override
    public String getImage() {
        return image;
    }

    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public List<Link> getButtons() {
        return buttons;
    }
}

Since we are using interface as Sling Model, we need to mention interface and its implementation class in adapter property of @Model annotation. defaultInjectionStrategy is mentioned to make all injection in model to optional. @Exporter annotation is used to export model in json format when requested.

In the above model we have used Link Model as a multi field item. Below is the Link Model and its implementation class.

Link.java

package com.aem.explained.platform.core.models;

public interface Link {

    String getName();

    String getLink();
}

LinkComponent.java

package com.aem.explained.platform.core.components;

import com.aem.explained.platform.core.models.Link;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.ExporterOption;
import org.apache.sling.models.annotations.Model;
import javax.inject.Inject;

@Model( adaptables = Resource.class,
        adapters = {Link.class, LinkComponent.class},
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
@Exporter(name = "jackson", extensions = "json", options = {
        @ExporterOption(name = "MapperFeature.SORT_PROPERTIES_ALPHABETICALLY", value="true")
})
public class LinkComponent implements Link {

    @Inject
    private String name;

    @Inject
    private String link;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getLink() {
        return link;
    }
}

9. Now, open promoherocomponent.html file and initialise com.aem.explained.platform.core.models.PromoHero to get all the values saved in dialog. HTL for component is given below.

<div data-sly-use.promohero="com.aem.explained.platform.core.models.PromoHero"
     data-sly-use.templates="core/wcm/components/commons/v1/templates.html"
     data-sly-test.isEmpty="${promohero.image || promohero.title}"
     class="img-container ">
  <img data-sly-test="${promohero.image}" src="${promohero.image}" class="img-responsive "
       title="${promohero.title}"/>
  <div class="card" data-sly-test="${promohero.title || promohero.description}">
    <h3>${promohero.title}</h3>
    <p>${promohero.description}</p>
    <div class="buttons" data-sly-list.button="${promohero.buttons}">
      <a class="button" href="${button.link @extension='html'}">${button.name}</a>
    </div>
  </div>
</div>
<sly data-sly-call="${templates.placeholder @ isEmpty = !isEmpty, classAppend = 'cmp-image cq-dd-image'}"></sly>

Here “core/wcm/components/commons/v1/templates.html” is used to display placeholder when component is unauthored.
10. Now build code and deploy in AEM by using build configuration or by running maven command.

mvn clean install -PautoInstallPackge

11. Open any page and add Promo Hero Component. (If you are not able to see Promo Hero Component in allowed components, open the page template editor and select layout container policies, in the allowed components list, check Promo Hero Component/AEM Explained componentGroup. Save and refresh page now you should be able to add component).

12. Open Promo Hero dialog by double-click and author values.

13. Add link by clicking “Add” button.

14. Submit dialog.

You have successfully created HTL Component.
we have used following features Sling Models, Sling Exporters, HTL.


Happy Learning….!

Design a site like this with WordPress.com
Get started