Expand my Community achievements bar.

SOLVED

Customize Core Teaser Component to add two additional required image fields to author image for mobile and tablet

Avatar

Level 2

I have a requirement to customize the core teaser component to add two additional image fields for author to author image for mobile and tablet. How can I achieve this requirement. The teaser component has sling:resourceSuperType as core/wcm/components/teaser/v2/teaser.

1 Accepted Solution

Avatar

Correct answer by
Level 4

Hi @ksh_ingole7 ! Wow, do you really use JSP nowadays for your components? I don't think it is a best practice and the way Adobe recommends for extending Core components. We use JSP only for extending dialog Coral components' backend code. For anything else, all my peers stopped to use them once AEM 6.2 and the Core Components were presented back in 2017.

 

@Sayali1 As for me, in your case the correct way of extending it would be updating the property imageDelegate in the parent .content.xml of your component from the default (core/wcm/components/image/v3/image) to another image component that will have the desired dialog & behavior you want. You can extend that new image component from the Core Image component (v3) or create a simplified new one.
Then, very likely, you will have to create the new Sling model for the new Teaser (extend from the v2/TeaserImpl using injection via resourceSuperType and a delegate pattern (@Delegate from Lombok the easiest way)) where you will cover the creating/using the image resource(s). Also, update the dialog for the new Image component + new Sling model for it to cover additional image source references in srcset (or in a new field) + rewrite its image.html to add media queries to the <img> tag or replace it with the <picture> tag with media queries and new sources.

After that, you can use this new image component everywhere on your project so all other images will have those new fields for mobile and tablet. We used this approach on many projects (only few of them were with Teaser components and imageDelegate though).
If you use Dynamic Media on your project, I anticipate there will be significantly more effort to reach what you want, but still achievable.

The Core components' code is written in a not very convenient way, not always with SOLID principles that hard to extend, and sometimes they have pesky out-of-the-box bugs that you are forced to deal with, but in general, they still can be extended, and used in production  Also, they cover many other questions that may arise during the project lifetime that you don't aware of in the very beginning of a project 

Don't forget to go through the AEM WKND tutorial and other resources like those ones provided by @Shiv_Prakash_Patel if you are a novice in extending AEM components.

 

Update. Forgot to mention. The easiest way to author how it is necessary for you without even coding - placing three different versions of the Teaser component on a page with the default AEM Grid style, then using the default "Layout" page editing mode and the "Hide Component" button to hide teasers that should not be displayed for the selected screen width.

yuriy_shestakov_0-1690186677232.png

 

View solution in original post

6 Replies

Avatar

Community Advisor

Hi @Sayali1 

 

  1. Create a New Component: Create a new component that extends the core teaser component. This new component will allow you to add the two additional image fields. You can create this component in your project's folder structure.

     
  2. Create a Dialog: In the myteaser component's cq:dialog node, create two additional fields of type "Image" for mobile and tablet. These fields will allow authors to upload images for mobile and tablet view.

  3. Override the Core Component: In your myteaser component's JSP file (myteaser.jsp), you need to include the core teaser component's rendering while adding your customizations for the mobile and tablet images. You can achieve this by using the <cq:include> tag.

    Here's an example of how your myteaser.jsp file could look:

     
    <%@include file="/libs/foundation/global.jsp"%> <cq:include path="teaser" resourceType="core/wcm/components/teaser/v2/teaser"/> <%-- Add your custom mobile and tablet image rendering here --%> <div class="mobile-image"> <cq:include path="mobileImage" resourceType="foundation/components/image"/> </div> <div class="tablet-image"> <cq:include path="tabletImage" resourceType="foundation/components/image"/> </div>
     
     

    In this example, we're using <cq:include> to include the core teaser component's rendering first and then adding the custom mobile and tablet image fields.

Thanks

Avatar

Correct answer by
Level 4

Hi @ksh_ingole7 ! Wow, do you really use JSP nowadays for your components? I don't think it is a best practice and the way Adobe recommends for extending Core components. We use JSP only for extending dialog Coral components' backend code. For anything else, all my peers stopped to use them once AEM 6.2 and the Core Components were presented back in 2017.

 

@Sayali1 As for me, in your case the correct way of extending it would be updating the property imageDelegate in the parent .content.xml of your component from the default (core/wcm/components/image/v3/image) to another image component that will have the desired dialog & behavior you want. You can extend that new image component from the Core Image component (v3) or create a simplified new one.
Then, very likely, you will have to create the new Sling model for the new Teaser (extend from the v2/TeaserImpl using injection via resourceSuperType and a delegate pattern (@Delegate from Lombok the easiest way)) where you will cover the creating/using the image resource(s). Also, update the dialog for the new Image component + new Sling model for it to cover additional image source references in srcset (or in a new field) + rewrite its image.html to add media queries to the <img> tag or replace it with the <picture> tag with media queries and new sources.

After that, you can use this new image component everywhere on your project so all other images will have those new fields for mobile and tablet. We used this approach on many projects (only few of them were with Teaser components and imageDelegate though).
If you use Dynamic Media on your project, I anticipate there will be significantly more effort to reach what you want, but still achievable.

The Core components' code is written in a not very convenient way, not always with SOLID principles that hard to extend, and sometimes they have pesky out-of-the-box bugs that you are forced to deal with, but in general, they still can be extended, and used in production  Also, they cover many other questions that may arise during the project lifetime that you don't aware of in the very beginning of a project 

Don't forget to go through the AEM WKND tutorial and other resources like those ones provided by @Shiv_Prakash_Patel if you are a novice in extending AEM components.

 

Update. Forgot to mention. The easiest way to author how it is necessary for you without even coding - placing three different versions of the Teaser component on a page with the default AEM Grid style, then using the default "Layout" page editing mode and the "Hide Component" button to hide teasers that should not be displayed for the selected screen width.

yuriy_shestakov_0-1690186677232.png

 

Avatar

Level 2

Hi @yuriy_shestakov @Shiv_Prakash_Patel    I have created a image component which refers to the core image component and made the required dialog changes to the myproject/Image component. I have given imageDelegate property in my apps/new/teaser pointing to apps/myproject/Image component so that the apps/new/teaser component can get the apps/myproject/Image dialog structure which I need to author the three images. I wanted the paths of the three images authored to be displayed in HTL using sling model . But not able to figure out how can I read the image src path of the three images  in my model class which implements com.adobe.cq.wcm.core.components.models.Teaser. I have added adapters ={Teaser.class, Image.class} in my model. 

Avatar

Level 4

Hi @Sayali1 

 

I had some time so prepared a small sample for you. The core image model is too complex to extend in a small amount of time so I have created my own component with a picture element.

Here are the steps of how to do this.

You created your own new Teaser component, so update there the `imageDelegate` property to a new simple-image component (this way It will use its internal template /apps/core/wcm/components/teaser/v2/teaser/image.html to render the specified component).

You should also extend the core teaser dialog in your new Teaser dialog this way:

<?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="My Teaser"
          sling:resourceType="cq/gui/components/authoring/dialog"
          extraClientlibs="[core.wcm.components.teaser.v2.editor,core.wcm.components.image.v3.editor]"
>
  <content jcr:primaryType="nt:unstructured">
    <items jcr:primaryType="nt:unstructured">
      <tabs jcr:primaryType="nt:unstructured">
        <items jcr:primaryType="nt:unstructured">
          <!-- Version with the Simple Image component -->
          <image
              jcr:primaryType="nt:unstructured"
              jcr:title="Asset"
              sling:resourceType="core/wcm/components/include/imagedelegate"
              path="/apps/myproject/components/simple-image/cq:dialog/content/items/tabs/items/asset"/>
          <!--
            In case if you have an image component that you extend from Core Image
            (using /mnt/override to get parent component fields)
          -->
          <!--
          <image
              jcr:primaryType="nt:unstructured"
              jcr:title="Asset"
              sling:resourceType="core/wcm/components/include/imagedelegate"
              path="/mnt/override/apps/myproject/components/image/cq:dialog/content/items/tabs/items/asset"/>
           -->
        </items>
      </tabs>
    </items>
  </content>
</jcr:root>

This way it will use the Simple Image dialog in the Teaser's dialog for selecting images.

The Simple Image component I didn't extend from anything and put only those fields that are necessary for testing:

<?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"
          sling:resourceType="cq/gui/components/authoring/dialog"
          title="Simple Image">
  <content
      jcr:primaryType="nt:unstructured"
      sling:resourceType="granite/ui/components/coral/foundation/container">
    <items jcr:primaryType="nt:unstructured">
      <tabs
          jcr:primaryType="nt:unstructured"
          sling:resourceType="granite/ui/components/coral/foundation/tabs">
        <items jcr:primaryType="nt:unstructured">
          <asset
              jcr:primaryType="nt:unstructured"
              jcr:title="Asset"
              sling:resourceType="granite/ui/components/coral/foundation/container">
            <items jcr:primaryType="nt:unstructured">
              <file
                  jcr:primaryType="nt:unstructured"
                  sling:resourceType="cq/gui/components/authoring/dialog/fileupload"
                  allowUpload="{Boolean}false"
                  fieldLabel="Image"
                  fileReferenceParameter="./fileReference"
                  mimeTypes="[image/gif,image/jpeg,image/png,image/tiff,image/svg+xml]"
                  name="./uploadingIsDisabledSoNotUsed"
                  required="{Boolean}true"/>
              <tablet-image
                  jcr:primaryType="nt:unstructured"
                  sling:resourceType="cq/gui/components/authoring/dialog/fileupload"
                  allowUpload="{Boolean}false"
                  fieldDescription="Optional tablet image"
                  fieldLabel="Tablet Image"
                  fileReferenceParameter="./tabletImageFileReference"
                  mimeTypes="[image/gif,image/jpeg,image/png,image/tiff,image/svg+xml]"
                  name="./uploadingIsDisabledSoNotUsed"/>
              <mobile-image
                  jcr:primaryType="nt:unstructured"
                  sling:resourceType="cq/gui/components/authoring/dialog/fileupload"
                  allowUpload="{Boolean}false"
                  enableNextGenDynamicMedia="{Boolean}true"
                  fieldDescription="Optional mobile image"
                  fieldLabel="Mobile Image"
                  fileReferenceParameter="./mobileImageFileReference"
                  mimeTypes="[image/gif,image/jpeg,image/png,image/tiff,image/svg+xml]"
                  name="./uploadingIsDisabledSoNotUsed"/>
              <alt
                  jcr:primaryType="nt:unstructured"
                  sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                  fieldDescription="Textual alternative of the meaning or function of the image, for visually impaired readers."
                  fieldLabel="Alternative text for accessibility"
                  name="./alt"
                  required="{Boolean}true"/>
            </items>
          </asset>
        </items>
      </tabs>
    </items>
  </content>
</jcr:root>

The fileReference parameter name is the same as was in Core Image, and other ones I have created analogically.

I didn't create a model for it as it's just a sample, used properties, and hard-coded breakpoints, so the HTML code for the apps/myproject/components/simple-image/simple-image.html will be:

<picture
    data-sly-set.mobileImage="${properties.mobileImageFileReference}"
    data-sly-set.tabletImage="${properties.tabletImageFileReference}"
    data-sly-set.desktopImage="${properties.fileReference}"
    data-sly-test.isConfigured="${desktopImage}"
>
  <source media="(min-width: 1024px)" srcset="${desktopImage}"
          data-sly-test="${desktopImage && (tabletImage || mobileImage)}">
  <source media="(min-width: 768px)" srcset="${tabletImage}" data-sly-test="${tabletImage}">
  <img src="${mobileImage}" alt="${properties.alt}" data-sly-test="${mobileImage}">

  <!--/* Display tablet image on mobile if no mobile image is configured */-->
  <img src="${tabletImage}" alt="${properties.alt}"
       data-sly-test="${!mobileImage && tabletImage}">
  <!--/* Display desktop image on mobile if neither mobile nor tablet images are configured */-->
  <img src="${desktopImage}" alt="${properties.alt}"
       data-sly-test="${!mobileImage && !tabletImage}">
</picture>

<!--/* Component placeholder */-->
<sly data-sly-use.templates="core/wcm/components/commons/v1/templates.html"
     data-sly-call="${templates.placeholder @ isEmpty=!isConfigured}"/>

 

If you do need to use the core image functionality, you will have to create your own teaser model, where you should do something with that getWrappedImageResource and return your own  org.apache.sling.api.resource.ResourceWrapper instance that will be passed to your image.html for rendering. You should pass to that wrapper resource mobileImageFileReference and tabletImageFileReference along with the default fileReference and other already existing properties. But the main problem is with the Core Image model - there are a lot of things, it uses the <img> tag, gets widths/sizes from policies for it, leverages AEM out-of-the-box adaptive image resizing selectors, can use Dynamic Media, etc..., so it will be hard to get rid (re-write) of all those things and re-write it the way you want. Also, many functions and classes that could be used are private/internal so you'll need to copy them to your project from Core Components sources. Note that if you still want to do that, use /mnt/override path in your teaser dialog (the commented piece of code in my sample).

 

 

Avatar

Community Advisor

@Sayali1 core components are version-able i.e. when a new version of AEM comes, it will not affect your current functionality. And if you want to use the latest features available in the new version, just change the reference version of core components in your custom component.

You could create a proxy component and customize it as per your need. Modify the dialog and other files accordingly to your need.

 

https://experienceleague.adobe.com/docs/experience-manager-core-components/using/developing/customiz...