• 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;
}
};
}