Search code examples
aem

Make a component linkable or not based on the dialog


I have a component and want to give authors the option in the dialog to add a link path. If this link path is filled in, then I want the component wrapper to be an <a> tag. If it's not filled in, I want it to remain a <div>

<div class="section" data-sly-test="${!properties.path}">
    <img src="${properties.icon}" alt="${properties.alt}" />
    <div data-sly-test="${properties.heading}">
        <h2 data-sly-element="${properties.headingSize}">${properties.heading}</h2>
    </div>
</div>
<a href="${properties.path}" class="section" data-sly-test="${properties.path}">
    <img src="${properties.icon}" alt="${properties.alt}" />
    <div data-sly-test="${properties.heading}">
        <h2 data-sly-element="${properties.headingSize}">${properties.heading}</h2>
    </div>
</a>

Is there a cleaner way to do this than creating two separate build outs of the whole component with a data-sly-test switch? I've struggled on a lot of examples like this where the wrapping tag/div is changed by the dialog. Looking for something similar to how data-sly-element behaves on the <h2> within my code here.


Solution

  • There are multiple ways to achieve what you are trying to do.

    Using data-sly-element and data-sly-attribute

    data-sly-attribute doesn't add the attribute to the tag if the value of the attribute is empty/null. Hence, it can be used as shown below to replace the anchor tag with a div if the path is unavailable.

    <a class="section" data-sly-attribute.href="${properties.path}" data-sly-element="${properties.path ? 'a' : 'div'}">
        <img src="${properties.icon}" alt="${properties.alt}" />
        <div data-sly-test="${properties.heading}">
            <h2 data-sly-element="${properties.headingSize}">${properties.heading}</h2>
        </div>
    </a>

    Using data-sly-unwrap

    Unwrap only removes the containing tag and doesn't remove all the inner tags. Hence, this can be used as shown below.

    <div class="section" data-sly-unwrap="${properties.path}">
        <a href="${properties.path}" class="section" data-sly-unwrap="${!properties.path}">
            <img src="${properties.icon}" alt="${properties.alt}" />
            <div data-sly-test="${properties.heading}">
                <h2 data-sly-element="${properties.headingSize}">${properties.heading}</h2>
            </div>
        </a>
    </div>

    Using data-sly-template and data-sly-call

    This is similar to what you have initially written, but instead of duplicating the entire inner HTML, it can be moved to a template and called twice.

    <div class="section" data-sly-test="${!properties.path}">
        <sly data-sly-call="${tpl}"></sly>
    </div>
    <a href="${properties.path}" class="section" data-sly-test="${properties.path}">
        <sly data-sly-call="${tpl}"></sly>
    </a>
    
    <!--/* The template */-->
    <template data-sly-template.tpl="">
        <img src="${properties.icon}" alt="${properties.alt}" />
        <div data-sly-test="${properties.heading}">
            <h2 data-sly-element="${properties.headingSize}">${properties.heading}</h2>
        </div>
    </template>

    For more information on HTL Block statements, refer this official doc.