Expand my Community achievements bar.

SOLVED

Custom navigation component based on specific template

Avatar

Level 1

Requirement: To create a custom navigation component, those component root should be based on specific template, first match the template and fetch the pages under that template and shows in the page when created navigation component drag and drop at page. Simply the dynamic way to call templates and its template selected pages should display at customized navigation component.

 

Note: Not in the authoring, solution required code based.

Topics

Topics help categorize Community content and increase your ability to discover relevant content.

1 Accepted Solution

Avatar

Correct answer by
Level 2

Creating a custom navigation component in AEM that dynamically fetches and displays pages based on a specific template is feasible. Here’s a high-level overview of how you can achieve this:

Steps to Create Custom Navigation Component

1. Component Definition:

  • Define a new AEM component for your custom navigation in the `/apps` directory.
  • Create a corresponding dialog to allow authors to select the template and configure other properties if needed.

2. Sling Model:

  • Create a Sling Model to handle the business logic of fetching pages based on the selected template.
  • Inject the template path and other required properties using annotations like `@ValueMapValue`.

3. JCR Query:

  • Use JCR-SQL2 or Query Builder API to fetch pages that use the selected template.
  • Example JCR-SQL2 query to find pages:
    SELECT * FROM [cq:Page] AS s WHERE ISDESCENDANTNODE([/content]) AND [jcr:content/cq:template] = '/apps/myproject/templates/mytemplate'

4. HTL Script:

  • Create an HTL (Sightly) script to render the navigation.
  • Use the Sling Model to populate the list of pages and iterate over them in the HTL script to build the navigation structure.

Example Implementation

1. Component Definition

Create the component under `/apps/myproject/components/custom-navigation` and add a dialog to allow template selection.

dialog.xml:
<jcr:root
xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Custom Navigation"
sling:resourceType="cq/gui/components/authoring/dialog">
<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">
<tab1
jcr:primaryType="nt:unstructured"
jcr:title="Properties"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<templatePath
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Template Path"
name="./templatePath"
rootPath="/apps/myproject/templates"/>
</items>
</tab1>
</items>
</tabs>
</items>
</content>
</jcr:root>

2. Sling Model

Create a Sling Model to fetch the pages.

NavigationModel.java:
package com.myproject.core.models;

import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import com.day.cq.wcm.api.PageManagerFactory;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.api.resource.QueryBuilder;
import org.apache.sling.api.resource.Query;
import org.apache.sling.api.resource.query.QueryBuilderException;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class NavigationModel {

@inject
private ResourceResolver resourceResolver;

@inject
private QueryBuilder queryBuilder;

@ValueMapValue
private String templatePath;

public List<Page> getPages() {
List<Page> pages = new ArrayList<>();
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
try {
Query query = queryBuilder.createQuery("SELECT * FROM [cq:Page] AS s WHERE ISDESCENDANTNODE([/content]) AND [jcr:content/cq:template] = '" + templatePath + "'", resourceResolver);
query.getResult().getNodes().forEachRemaining(node -> {
Page page = pageManager.getContainingPage(node);
if (page != null) {
pages.add(page);
}
});
} catch (QueryBuilderException e) {
// Handle exception
}
return pages;
}
}

3. HTL Script

Create an HTL script to render the navigation.

custom-navigation.html:
<sly data-sly-use.navigationModel="com.myproject.core.models.NavigationModel">
<ul>
<sly data-sly-list.page="${navigationModel.pages}">
<li>
<a href="${page.path}">${page.title}</a>
</li>
</sly>
</ul>
</sly>

 

Feasibility and Considerations

1. Performance: Ensure the query is optimized to avoid performance issues, especially if there are many pages.
2. Security: Validate the user input for the template path to avoid injection attacks.
3. Caching: Implement caching mechanisms to reduce the load on the repository for frequently accessed navigation components.
4. Error Handling: Handle errors gracefully to ensure a robust implementation.

By following these steps, you can create a custom navigation component in AEM that dynamically displays pages based on a selected template.

View solution in original post

5 Replies

Avatar

Correct answer by
Level 2

Creating a custom navigation component in AEM that dynamically fetches and displays pages based on a specific template is feasible. Here’s a high-level overview of how you can achieve this:

Steps to Create Custom Navigation Component

1. Component Definition:

  • Define a new AEM component for your custom navigation in the `/apps` directory.
  • Create a corresponding dialog to allow authors to select the template and configure other properties if needed.

2. Sling Model:

  • Create a Sling Model to handle the business logic of fetching pages based on the selected template.
  • Inject the template path and other required properties using annotations like `@ValueMapValue`.

3. JCR Query:

  • Use JCR-SQL2 or Query Builder API to fetch pages that use the selected template.
  • Example JCR-SQL2 query to find pages:
    SELECT * FROM [cq:Page] AS s WHERE ISDESCENDANTNODE([/content]) AND [jcr:content/cq:template] = '/apps/myproject/templates/mytemplate'

4. HTL Script:

  • Create an HTL (Sightly) script to render the navigation.
  • Use the Sling Model to populate the list of pages and iterate over them in the HTL script to build the navigation structure.

Example Implementation

1. Component Definition

Create the component under `/apps/myproject/components/custom-navigation` and add a dialog to allow template selection.

dialog.xml:
<jcr:root
xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Custom Navigation"
sling:resourceType="cq/gui/components/authoring/dialog">
<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">
<tab1
jcr:primaryType="nt:unstructured"
jcr:title="Properties"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<templatePath
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Template Path"
name="./templatePath"
rootPath="/apps/myproject/templates"/>
</items>
</tab1>
</items>
</tabs>
</items>
</content>
</jcr:root>

2. Sling Model

Create a Sling Model to fetch the pages.

NavigationModel.java:
package com.myproject.core.models;

import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import com.day.cq.wcm.api.PageManagerFactory;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.api.resource.QueryBuilder;
import org.apache.sling.api.resource.Query;
import org.apache.sling.api.resource.query.QueryBuilderException;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class NavigationModel {

@inject
private ResourceResolver resourceResolver;

@inject
private QueryBuilder queryBuilder;

@ValueMapValue
private String templatePath;

public List<Page> getPages() {
List<Page> pages = new ArrayList<>();
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
try {
Query query = queryBuilder.createQuery("SELECT * FROM [cq:Page] AS s WHERE ISDESCENDANTNODE([/content]) AND [jcr:content/cq:template] = '" + templatePath + "'", resourceResolver);
query.getResult().getNodes().forEachRemaining(node -> {
Page page = pageManager.getContainingPage(node);
if (page != null) {
pages.add(page);
}
});
} catch (QueryBuilderException e) {
// Handle exception
}
return pages;
}
}

3. HTL Script

Create an HTL script to render the navigation.

custom-navigation.html:
<sly data-sly-use.navigationModel="com.myproject.core.models.NavigationModel">
<ul>
<sly data-sly-list.page="${navigationModel.pages}">
<li>
<a href="${page.path}">${page.title}</a>
</li>
</sly>
</ul>
</sly>

 

Feasibility and Considerations

1. Performance: Ensure the query is optimized to avoid performance issues, especially if there are many pages.
2. Security: Validate the user input for the template path to avoid injection attacks.
3. Caching: Implement caching mechanisms to reduce the load on the repository for frequently accessed navigation components.
4. Error Handling: Handle errors gracefully to ensure a robust implementation.

By following these steps, you can create a custom navigation component in AEM that dynamically displays pages based on a selected template.

Avatar

Level 1

Potential Problems and Challenges

  • Template Selection in Dialog: Ensuring that the dialog correctly lists and allows the selection of templates.
  • Fetching Pages Based on Template: Efficiently querying the JCR to fetch pages using a specific template.
  • Performance: Querying large repositories can lead to performance issues.
  • Permissions: The navigation component might not have permission to read certain pages.
  • Dynamic Page Rendering: Rendering dynamic content can lead to inconsistent results if the content changes frequently.
  • Component Flexibility: Making the component flexible enough to handle various templates and page structures.

Note: You can directly use navigation component, and then author those component

Avatar

Level 1

@RajeevMa 

Authoring process is making in few steps, but coding and its process it will takes more complexity. Not recommended direct use navigation component.

Avatar

Level 10

Hi @RajeevMa ,

To create a custom navigation component in AEM that fetches pages based on a specific template, you can follow these steps:

1. Create a new component: Create a new component for your custom navigation. This component will be responsible for fetching and rendering the pages based on the specific template.

2. Define the component properties: Define the necessary properties for your custom navigation component. This may include properties to specify the template to filter pages, the number of levels to display, and any other customization options.

3. Implement the component logic: In the component's logic, you will need to fetch the pages based on the specified template. You can use the AEM QueryBuilder API or the JCR API to query for pages that match the template.

4. Render the navigation: Once you have fetched the pages, you can render the navigation using the appropriate HTML markup and CSS styling. You can use a loop to iterate over the fetched pages and generate the navigation structure.

5. Add the component to a template: To make the custom navigation component available for use, you need to add it to a template. Edit the template that you want to associate with the custom navigation component and include the component in the appropriate location.

6. Configure the component properties: When adding the custom navigation component to a page, you can configure the properties to specify the template and any other customization options.

By following these steps, you can create a custom navigation component in AEM that fetches pages based on a specific template and displays them dynamically.

Avatar

Administrator

@RajeevMa Did you find the suggestions from users helpful? Please let us know if you require more information. Otherwise, please mark the answer as correct for posterity. If you have found out solution yourself, please share it with the community.



Kautuk Sahni