avatar
navigate on next/previous page aem AEM

• Normally, navigation to the next or previous page in AEM is handled from the client-side in the ui.front-end module, typically using TypeScript as one of the approaches.

» Java interface For Sling Model

package com.flagtick.core.models;

public interface SampleModel {
    String getSamplePath();
}

» Sling Model

package com.flagtick.core.models.impl;

import com.flagtick.core.models.SampleModel;
import com.flagtick.core.utils.LinkUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.annotations.Default;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

import javax.annotation.PostConstruct;

@Model(adaptables = SlingHttpServletRequest.class,
        adapters = SampleModel.class,
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class SampleModelImpl implements SampleModel {
	@ValueMapValue
    private String samplePath;
	private String sampleMappedPath;
	@PostConstruct
    protected void init() {
        sampleMappedPath = resolver.map(LinkUtils.sanitizeLink(samplePath));
    }
	@Override
    public String getSamplePath() {
        return sampleMappedPath;
    }
}

» LinkUtils.java

package com.flagtick.core.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class LinkUtils {

	public static final String CONTENT_DAM_ROOT_PATH = "/content/dam";
	public static final String HTML_EXTENSION = ".html";

	public static String sanitizeLink(String link) {
        if (StringUtils.isBlank(link)) {
            return "";
        } else if (link.startsWith("/content/") && !link.startsWith(CONTENT_DAM_ROOT_PATH)) {
            return link + HTML_EXTENSION;
        }
        return fixExternalLink(link);
    }
}

» _cq_dialog > .content.xml

<samplePath
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/form/pathbrowser"
        fieldLabel="Sample Page Path"
        name="./samplePath"
        rootPath="/content/flagtick/us/en"/>

» sample.html

<sly>
    <div class="flagtick-sample"
         data-cmp-is="Sample"
         data-sly-use.model="com.flagtick.core.models.SampleModel"
         data-sample-path="${model.samplePath}"
		 ...
	</div>	
</sly>

Note: In our example, the data-sly-attribute.sample-path directive is used to output the data-sample-path attribute based on the samplePath property of the Sling Model.

Typically in Sightly, the order in which you declare data attributes and the data-sly-use directive matters. The data-cmp-is attribute, which is often used to identify the component type, is usually declared before other data attributes.

+-- <sly data-sly-use.model="com.flagtick.core.models.SampleModel">
    +-- data-cmp-is="Sample"
    |   +-- data-sample-path="${model.samplePath}"
    |       +-- ...
+-- </sly>

» component.ts

export default abstract class Component {
  private static readonly CMP_HOOK = 'cmpHook';

  private static readonly CMP_HOOK_LIST = 'cmpHooklist';

  private elementName: string;

  private elementDashName: string;

  $cmp: HTMLElement;

  $elements: { [k: string]: HTMLElement } = {};

  protected constructor(cmp: HTMLElement) {
    this.$cmp = cmp;
    this.elementName = this.$cmp.dataset.cmpIs ? this.$cmp?.dataset?.cmpIs : '';
    this.elementDashName = this.elementName?.split(/(?=[A-Z])/).join('-').toLowerCase();
    this.protect();
    if (this.$elements) {
      this.cacheElements();
    }
  }

  private protect(): void {
    this.$cmp.removeAttribute('data-cmp-is');
  }

  private cacheElements(): void {
    const hooks = this.$cmp.querySelectorAll(`[data-cmp-hook-${this.elementDashName}]`);

    if (!hooks) {
      return;
    }

    Array.from(hooks).forEach((hook) => {
      const capitalized = `${this.elementName?.charAt(0).toUpperCase()}${this.elementName?.slice(1)}`;
      if (hook instanceof HTMLElement) {
        const key = hook.dataset[`${Component.CMP_HOOK}${capitalized}`];
        if (key) {
          this.$elements[key] = hook;
        }
      }
    });
  }
}

» Sample.ts

import Component from '@utils/Component';
import $ from 'jquery';

export default class Sample extends Component {
	private $component;
	samplePath: string;
	constructor(cmp: HTMLElement) {
		super(cmp);
		this.$component = $(cmp);
		this.samplePath = this.$cmp.dataset.samplePath || '';
		this.navigateSamplePage();
	}
	navigateSamplePage = (): void => {
		if (!window.location.href.includes(this.samplePath)) {
		  window.location.href = this.samplePath;
		}
	};
}
24
You need to login to do this manipulation!