Expand my Community achievements bar.

SOLVED

Modify SlingModel of a core component in Custom component

Avatar

Level 3

Hi,

 

I have created a custom component using AEM's core component(Tabs). The use case that we have now is to modify the `id` attribute of each tab. The `id` attribute should have a value of the title of the tab transformed to lowercase and spaces separated with hyphens. 

 

I assume this cannot be done in the Slightly HTL file and this needs to be modified in Sling Model. I tried to access the tabs using ComponentResource but was not able to get the data of the tabs to modify.

 

How do we achieve this?  Any help is appreciated

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

Hello @manasabojja7 ,

To modify each Tab Item ID you need to follow the Sling Delegation Model concept. But the complexity is the "ID" field. This comes from the Component model class. So you need to create another interface and its implementation to hold your custom ID.

In the end, your model will be like this,

package com.aem.demo.core.models;

import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.wcm.core.components.commons.link.Link;
import com.adobe.cq.wcm.core.components.models.ListItem;
import com.adobe.cq.wcm.core.components.models.Tabs;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Delegate;
import org.apache.sling.api.SlingHttpServletRequest;
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.Model;
import org.apache.sling.models.annotations.injectorspecific.Self;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

@Model(adaptables = SlingHttpServletRequest.class, adapters = {TabsExtd.class, ComponentExporter.class},
        resourceType = TabsExtdImpl.RESOURCE_TYPE, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class TabsExtdImpl implements TabsExtd {
    public static final String RESOURCE_TYPE = "aem-demo/components/tabs";

    @Self
    @Delegate
    private Tabs coreTabs;

    @Override
    public List<CustomListItem> getExtItems() {
        List<CustomListItem> customListItems = new ArrayList<>();
        for (ListItem item : coreTabs.getItems()) {
            CustomListItemImpl extItem = new CustomListItemImpl();
            extItem.setId(item.getTitle().toLowerCase().replace(" ", "-"));
            extItem.setLink(item.getLink());
            extItem.setURL(item.getURL());
            extItem.setTitle(item.getTitle());
            extItem.setName(item.getName());
            extItem.setDescription(item.getDescription());
            extItem.setPath(item.getPath());
            extItem.setTeaserResource(item.getTeaserResource());
            customListItems.add(extItem);
        }
        return customListItems;
    }

    interface CustomListItem extends ListItem {

    }
    @Getter
    @Setter
    private class CustomListItemImpl implements CustomListItem {
        private String id;
        private Link link;
        private String URL;
        private String Title;
        private String description;
        private Calendar lastModified;
        private String path;
        private String name;
        private Resource teaserResource;
    }
}

Sady_Rifat_0-1689750524779.png

However, to make it simple I push a commit for you. You will easily pick up from this. https://github.com/Sady-Rifat/aem-demo/commit/7047cd9dcb6601359f61d13b373da552272ca8cc 

View solution in original post

6 Replies

Avatar

Community Advisor

Hi @manasabojja7 

 

You can create your new component, delegating the old one. Below is the example: https://github.com/adobe/aem-core-wcm-components/wiki/Delegation-Pattern-for-Sling-Models

 

Thanks,

Kiran Vedantam.

Avatar

Community Advisor

Since it’s a custom component then you will have its own html file on which you can call your custom sling model class. So wherever you want your custom sling loses property to be injected on html you can call it with your sling model.property. In your case it’s an Id 

Avatar

Community Advisor

@manasabojja7 You could delegate the model by using adapters-

@Model(adaptables = SlingHttpServletRequest.class, adapters = Tabs.class, resourceType = "myproject/components/myTabs")

and could use the below to include the object:

@Self
@Via(type = ResourceSuperType.class)
@Delegate(excludes = DelegationExclusion.class)
private Tabs tabs;

Avatar

Correct answer by
Community Advisor

Hello @manasabojja7 ,

To modify each Tab Item ID you need to follow the Sling Delegation Model concept. But the complexity is the "ID" field. This comes from the Component model class. So you need to create another interface and its implementation to hold your custom ID.

In the end, your model will be like this,

package com.aem.demo.core.models;

import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.wcm.core.components.commons.link.Link;
import com.adobe.cq.wcm.core.components.models.ListItem;
import com.adobe.cq.wcm.core.components.models.Tabs;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Delegate;
import org.apache.sling.api.SlingHttpServletRequest;
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.Model;
import org.apache.sling.models.annotations.injectorspecific.Self;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

@Model(adaptables = SlingHttpServletRequest.class, adapters = {TabsExtd.class, ComponentExporter.class},
        resourceType = TabsExtdImpl.RESOURCE_TYPE, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class TabsExtdImpl implements TabsExtd {
    public static final String RESOURCE_TYPE = "aem-demo/components/tabs";

    @Self
    @Delegate
    private Tabs coreTabs;

    @Override
    public List<CustomListItem> getExtItems() {
        List<CustomListItem> customListItems = new ArrayList<>();
        for (ListItem item : coreTabs.getItems()) {
            CustomListItemImpl extItem = new CustomListItemImpl();
            extItem.setId(item.getTitle().toLowerCase().replace(" ", "-"));
            extItem.setLink(item.getLink());
            extItem.setURL(item.getURL());
            extItem.setTitle(item.getTitle());
            extItem.setName(item.getName());
            extItem.setDescription(item.getDescription());
            extItem.setPath(item.getPath());
            extItem.setTeaserResource(item.getTeaserResource());
            customListItems.add(extItem);
        }
        return customListItems;
    }

    interface CustomListItem extends ListItem {

    }
    @Getter
    @Setter
    private class CustomListItemImpl implements CustomListItem {
        private String id;
        private Link link;
        private String URL;
        private String Title;
        private String description;
        private Calendar lastModified;
        private String path;
        private String name;
        private Resource teaserResource;
    }
}

Sady_Rifat_0-1689750524779.png

However, to make it simple I push a commit for you. You will easily pick up from this. https://github.com/Sady-Rifat/aem-demo/commit/7047cd9dcb6601359f61d13b373da552272ca8cc 

Avatar

Level 3

Thanks for your detailed response @Sady_Rifat 

 

I tried to do a similar implementation by delegating the Sling Model but I see a Null Pointer Exception while trying to read 

 coreTabs.getItems() as the coreTabs is null
Not sure why it's not able to read this. Any thoughts if I would be missing something?