Is it possible to override the Breadcrumb or Page List component to use a different page title than the Navigation Title?
I have a requirement where I need to use a shorter title in the Breadcrumb component than in the Page List Component, however, both are using the Navigation Title. If there is a way that I can use one title type for one component and another for the other component, that would be ideal.
I'm a relative beginner with Java and don't entirely understand how the Delegation Pattern for Sling Models (https://github.com/adobe/aem-core-wcm-components/wiki/Delegation-Pattern-for-Sling-Models) could tie into what I'm trying to do here, but if there is a tutorial or some information that might help guide a solution, I would appreciate the insight.
Solved! Go to Solution.
Views
Replies
Total Likes
Hi @jetate,
It depends what you mean by "one title type for one component and another for the other component", because there are many title types. However, let's have a look at why the Navigation Title is used.
The breadcrumb model returns a list of NavigationItems (the interface). These are in fact instances of BreadcrumbItemImpl (the class). They in turn fetch the title property (which is used in the rednering HTL script using the following algorithm:
@Override
public String getTitle() {
String title = page.getNavigationTitle();
if (title == null) {
title = page.getPageTitle();
}
if (title == null) {
title = page.getTitle();
}
if (title == null) {
title = page.getName();
}
return title;
}
So all you need to do is override this behavior slightly. Let's say for example that you want to use the Page Title and not the Navigation Title in a breadcrumb, you should create the following item:
import com.adobe.cq.wcm.core.components.models.NavigationItem;
import lombok.Getter;
import lombok.experimental.Delegate;
public class CustomNavigationItem implements NavigationItem {
// Here we delegate everything to the original item, except the getTitle method (see interface below)
@Delegate(excludes = DelegationExclusion.class)
private final NavigationItem delegate;
// Rather than getting a title from a page using an the algorithm I showed above, this item will simply return
// whatever String was given to it in its constructor
@Getter
private final String title;
public CustomNavigationItem(final NavigationItem item, final String title) {
this.delegate = item;
this.title = title;
}
private interface DelegationExclusion {
String getTitle();
}
}
Then, return these CustomNavigationItems rather than the default items using this model:
import com.adobe.cq.wcm.core.components.models.Breadcrumb;
import com.adobe.cq.wcm.core.components.models.NavigationItem;
import com.day.cq.wcm.api.PageManager;
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.Model;
import org.apache.sling.models.annotations.Via;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.via.ResourceSuperType;
import java.util.Collection;
import java.util.stream.Collectors;
@Model(
adaptables = {Resource.class, SlingHttpServletRequest.class},
adapters = Breadcrumb.class,
resourceType = "demo/components/content/breadcrumb",
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class CustomBreadcrumb implements Breadcrumb {
@Self
@Via(type = ResourceSuperType.class)
private Breadcrumb delegate;
@ScriptVariable
private PageManager pageManager;
@Override
public Collection<NavigationItem> getItems() {
// For each item, create an instance of our custom item
return delegate.getItems().stream().map(item -> {
// Set the title to be the page title
final String pageTitle = pageManager.getPage(item.getPath()).getPageTitle();
return new CustomNavigationItem(item, pageTitle);
}).collect(Collectors.toList());
}
@Override
public String getExportedType() {
return delegate.getExportedType();
}
}
Here is the result! The page properties:
Ans as you can see, the page title is used, not the navigation title! Sorry, didn't have time for CSS
I've been generous with comments on the parts that are relative to your use-case. If you want a step-by-step tutorial to explain exactly how it all works, I wrote one recently here: https://levelup.gitconnected.com/aem-extend-core-component-models-using-resource-type-association-an...
Hi @jetate,
It depends what you mean by "one title type for one component and another for the other component", because there are many title types. However, let's have a look at why the Navigation Title is used.
The breadcrumb model returns a list of NavigationItems (the interface). These are in fact instances of BreadcrumbItemImpl (the class). They in turn fetch the title property (which is used in the rednering HTL script using the following algorithm:
@Override
public String getTitle() {
String title = page.getNavigationTitle();
if (title == null) {
title = page.getPageTitle();
}
if (title == null) {
title = page.getTitle();
}
if (title == null) {
title = page.getName();
}
return title;
}
So all you need to do is override this behavior slightly. Let's say for example that you want to use the Page Title and not the Navigation Title in a breadcrumb, you should create the following item:
import com.adobe.cq.wcm.core.components.models.NavigationItem;
import lombok.Getter;
import lombok.experimental.Delegate;
public class CustomNavigationItem implements NavigationItem {
// Here we delegate everything to the original item, except the getTitle method (see interface below)
@Delegate(excludes = DelegationExclusion.class)
private final NavigationItem delegate;
// Rather than getting a title from a page using an the algorithm I showed above, this item will simply return
// whatever String was given to it in its constructor
@Getter
private final String title;
public CustomNavigationItem(final NavigationItem item, final String title) {
this.delegate = item;
this.title = title;
}
private interface DelegationExclusion {
String getTitle();
}
}
Then, return these CustomNavigationItems rather than the default items using this model:
import com.adobe.cq.wcm.core.components.models.Breadcrumb;
import com.adobe.cq.wcm.core.components.models.NavigationItem;
import com.day.cq.wcm.api.PageManager;
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.Model;
import org.apache.sling.models.annotations.Via;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.via.ResourceSuperType;
import java.util.Collection;
import java.util.stream.Collectors;
@Model(
adaptables = {Resource.class, SlingHttpServletRequest.class},
adapters = Breadcrumb.class,
resourceType = "demo/components/content/breadcrumb",
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class CustomBreadcrumb implements Breadcrumb {
@Self
@Via(type = ResourceSuperType.class)
private Breadcrumb delegate;
@ScriptVariable
private PageManager pageManager;
@Override
public Collection<NavigationItem> getItems() {
// For each item, create an instance of our custom item
return delegate.getItems().stream().map(item -> {
// Set the title to be the page title
final String pageTitle = pageManager.getPage(item.getPath()).getPageTitle();
return new CustomNavigationItem(item, pageTitle);
}).collect(Collectors.toList());
}
@Override
public String getExportedType() {
return delegate.getExportedType();
}
}
Here is the result! The page properties:
Ans as you can see, the page title is used, not the navigation title! Sorry, didn't have time for CSS
I've been generous with comments on the parts that are relative to your use-case. If you want a step-by-step tutorial to explain exactly how it all works, I wrote one recently here: https://levelup.gitconnected.com/aem-extend-core-component-models-using-resource-type-association-an...
Views
Replies
Total Likes
Views
Replies
Total Likes
The code worked on one server and failing on other.
Views
Replies
Total Likes
Facing NPE on delegate.getITems().
The Breadcrumb object is not getting instance of self and its null at
@Deleted Account
@Via(type = ResourceSuperType.class)
private Breadcrumb delegate;
Views
Replies
Total Likes
Views
Like
Replies
Views
Likes
Replies