Expand my Community achievements bar.

Don’t miss the AEM Skill Exchange in SF on Nov 14—hear from industry leaders, learn best practices, and enhance your AEM strategy with practical tips.
SOLVED

Cannot access core sling model Navigation variables

Avatar

Level 3

I replicated in my code the navigation core component that uses the path 

'com.adobe.cq.wcm.core.components.models.Navigation"
 
But I can't access the 'item' variable to display items that I need
 
Inside my nav tag I have 
data-sly-use.navigation="com.adobe.cq.wcm.core.components.models.Navigation"
 
And if I display ${navigation} it returns 
com.adobe.cq.wcm.core.components.internal.models.v2.NavigationImpl@77b55338
And if I display ${navigation.items} it returns com.adobe.cq.wcm.core.components.internal.models.v2.NavigationItemImpl@100833c1
 
 
 
1 Accepted Solution

Avatar

Correct answer by
Community Advisor

@NathanVieira 

 

You can readily use all the existing code by using Delegation https://experienceleague.adobe.com/docs/experience-manager-core-components/using/developing/customiz...

 

You just need to use @Via annotation. Following would not be required.

LinkManager
LocalizationUtils
Utils 

Aanchal Sikka

View solution in original post

5 Replies

Avatar

Community Advisor

Hello @NathanVieira 

 

The WCM Core Sling Models are tied to specific resource types. Example:


public static final String RESOURCE_TYPE = "core/wcm/components/navigation/v1/navigation";

https://github.com/adobe/aem-core-wcm-components/blob/main/bundles/core/src/main/java/com/adobe/cq/w...

 

When Navigation 2 component was created in WCM core component, a dummy v2 Sling Model was also created, which is an extension of v1 https://github.com/adobe/aem-core-wcm-components/blob/main/bundles/core/src/main/java/com/adobe/cq/w... 

 

For your implementation, you might have to create a Model, that binds to your component and then use the WCM Core Navigation component via Delegate pattern.

 

 


Aanchal Sikka

Avatar

Level 3

I was able to create my model and bind to my component, and I need the same variables that the core component Navigation uses. 

 

But i have these erros 

import com.adobe.cq.wcm.core.components.internal.Utils
(The import com.adobe.cq.wcm.core.components.internal cannot be resolved)
 
import com.adobe.cq.wcm.core.components.internal.LocalizationUtils;
(The import com.adobe.cq.wcm.core.components.internal cannot be resolved)
 
com.adobe.cq.wcm.core.components.commons.link.LinkManager
(The import com.adobe.cq.wcm.core.components.commons.link.LinkManager cannot be resolved)
 
I know I can't directtly access the file in the adobe internal folder but how can I fix these error and use 
 
LinkManager
LocalizationUtils
Utils 

Avatar

Level 3
package com.myproject.core.models.impl;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import javax.annotation.PostConstruct;
import javax.mail.Quota.Resource;

import com.adobe.cq.wcm.core.components.internal.Utils;
import com.adobe.cq.wcm.core.components.util.AbstractComponentImpl;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Via;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.wcm.core.components.internal.LocalizationUtils;
import com.adobe.cq.wcm.core.components.commons.link.LinkManager;
import com.adobe.cq.wcm.core.components.models.Navigation;
import com.adobe.cq.wcm.core.components.models.NavigationItem;
import com.day.cq.wcm.api.LanguageManager;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageFilter;
import com.day.cq.wcm.api.components.Component;
import com.day.cq.wcm.api.designer.Style;
import com.day.cq.wcm.msm.api.LiveRelationshipManager;

/**
 * Navigation model implementation.
 */
@Model(adaptables = SlingHttpServletRequest.class,
    adapters = {Navigation.class, ComponentExporter.class},
    resourceType = {NavMenuHeaderImpl.RESOURCE_TYPE})

public class NavMenuHeaderImpl implements Navigation {
     
     /**
      * The resource navigation component resource type.
      */
      public static final String RESOURCE_TYPE = "apps/myproject/components/atom_nav-header";
 
      /**
       * Name of the resource / configuration policy property that defines the accessibility label.
       */
      private static final String PN_ACCESSIBILITY_LABEL = "accessibilityLabel";
  
      /**
       * The current request.
       */
      @Self
      private SlingHttpServletRequest request;
  
      /**
       * The link manager.
       */
      @Self
      private LinkManager linkManager;
  
      /**
       * The current page.
       */
      @ScriptVariable
      private Page currentPage;
  
      /**
       * The current style.
       */
      @ScriptVariable
      private Style currentStyle;
  
      /**
       * The language manager service.
       */
      @OSGiService
      private LanguageManager languageManager;
  
      /**
       * The relationship manager service.
       */
      @OSGiService
      private LiveRelationshipManager relationshipManager;
  
      /**
       * The accessibility label.
       */
      @nullable
      private String accessibilityLabel;
  
      /**
       * Number indicating how many levels below the navigation root the navigation should start.
       */
      private int structureStart;
  
      /**
       * Number indicating how many levels below the navigation root should be included in the results.
       * Use "-1" for no limit.
       */
      private int structureDepth;
  
      /**
       * The root page from which to build the navigation.
       */
      private Page navigationRootPage;
  
      /**
       * Placeholder for the list of results.
       */
      private List<NavigationItem> items;
  
      /**
       * Initialize the model.
       */
      @PostConstruct
      private void initModel() {
          ValueMap properties = this.resource.getValueMap();
          structureDepth = properties.get(PN_STRUCTURE_DEPTH, currentStyle.get(PN_STRUCTURE_DEPTH, -1));
          boolean collectAllPages = properties.get(PN_COLLECT_ALL_PAGES, currentStyle.get(PN_COLLECT_ALL_PAGES, true));
          if (collectAllPages) {
              structureDepth = -1;
          }
          if (currentStyle.containsKey(PN_STRUCTURE_START) || properties.containsKey(PN_STRUCTURE_START)) {
              //workaround to maintain the content of Navigation component of users in case they update to the current i.e. the `structureStart` version.
              structureStart = properties.get(PN_STRUCTURE_START, currentStyle.get(PN_STRUCTURE_START, 1));
          } else {
              boolean skipNavigationRoot = properties.get(PN_SKIP_NAVIGATION_ROOT, currentStyle.get(PN_SKIP_NAVIGATION_ROOT, true));
              if (skipNavigationRoot) {
                  structureStart = 1;
              } else {
                  structureStart = 0;
              }
          }
      }
  
      /**
       * Get the effective navigation root page.
       *
       * @Return The effective navigation root page.
       */
      private Page getNavigationRoot() {
          if (this.navigationRootPage == null) {
              String navigationRootPath = Optional.ofNullable(this.resource.getValueMap().get(PN_NAVIGATION_ROOT, String.class))
                  .orElseGet(() -> currentStyle.get(PN_NAVIGATION_ROOT, String.class));
              this.navigationRootPage = LocalizationUtils.getLocalPage(navigationRootPath,
                  this.currentPage,
                  this.request.getResourceResolver(),
                  this.languageManager,
                  this.relationshipManager)
                  .orElseGet(() -> currentPage.getPageManager().getPage(navigationRootPath));
          }
          return this.navigationRootPage;
      }
  
      @Override
      public List<NavigationItem> getItems() {
          if (this.items == null) {
              this.items = Optional.ofNullable(this.getNavigationRoot())
                  .map(navigationRoot -> getRootItems(navigationRoot, structureStart))
                  .orElseGet(Stream::empty)
                  .map(item -> this.createNavigationItem(item, getItems(item)))
                  .collect(Collectors.toList());
          }
          return Collections.unmodifiableList(items);
      }
  
      protected NavigationItem newNavigationItem(Page page, boolean active, boolean current, @notnull LinkManager linkManager, int level,
                                                 List<NavigationItem> children, String parentId, Component component) {
          return new NavigationItemImpl(page, active, current, linkManager, level, children, parentId, component);
      }
  
      @Override
      @nullable
      public String getAccessibilityLabel() {
          if (this.accessibilityLabel == null) {
              this.accessibilityLabel = this.resource.getValueMap().get(PN_ACCESSIBILITY_LABEL, String.class);
          }
          return this.accessibilityLabel;
      }
  
      @notnull
      @Override
      public String getExportedType() {
          return this.resource.getResourceType();
      }
  
      /**
       * Builds the navigation tree for a {@code navigationRoot} page.
       *
       * @Param subtreeRoot The page for which to generate the sub-tree.
       * @Return the list of collected navigation trees
       */
      private List<NavigationItem> getItems(@NotNull final Page subtreeRoot) {
          if (this.structureDepth < 0 || subtreeRoot.getDepth() - this.getNavigationRoot().getDepth() < this.structureDepth) {
              Iterator<Page> childIterator = subtreeRoot.listChildren(new PageFilter());
              return StreamSupport.stream(((Iterable<Page>) () -> childIterator).spliterator(), false)
                  .map(item -> this.createNavigationItem(item, getItems(item)))
                  .collect(Collectors.toList());
          }
          return Collections.emptyList();
      }
  
      /**
       * Gets a stream of the top level of pages in the navigation.
       *
       * @Param navigationRoot The navigation root page.
       * @Param structureStart The number of levels under the root page to begin collecting pages.
       * @Return Stream of all descendant pages of navigationRoot that are exactly structureStart levels deeper.
       */
      private Stream<Page> getRootItems(@NotNull final Page navigationRoot, final int structureStart) {
          if (structureStart < 1) {
              return Stream.of(navigationRoot);
          }
          Iterator<Page> childIterator = navigationRoot.listChildren(new PageFilter());
          return StreamSupport.stream(((Iterable<Page>) () -> childIterator).spliterator(), false)
              .flatMap(child -> getRootItems(child, structureStart - 1));
      }
  
      /**
       * Create a navigation item for the given page/children.
       *
       * @Param page The page for which to create a navigation item.
       * @Param children The child navigation items.
       * @Return The newly created navigation item.
       */
      private NavigationItem createNavigationItem(@NotNull final Page page, @notnull final List<NavigationItem> children) {
          int level = page.getDepth() - (this.getNavigationRoot().getDepth() + structureStart);
          boolean current = checkCurrent(page);
          boolean selected = checkSelected(page, current);
          return newNavigationItem(page, selected, current, linkManager, level, children, getId(), component);
      }
  
      /**
       * Checks if the specified page is selected.
       * A page is selected if it is either:
       * <ul>
       *     <li>The current page; or,</li>
       *     <li>A page that redirects to the current page; or,</li>
       *     <li>The current page is a child of the specified page</li>
       * </ul>
       *
       * @Param page The page to check.
       * @Return True if the page is selected, false if not.
       */
      private boolean checkSelected(@NotNull final Page page, boolean current) {
          return current
              || this.currentPage.getPath().startsWith(page.getPath() + "/");
      }
  
      private boolean checkCurrent(@NotNull final Page page) {
          return this.currentPage.equals(page)
                  || currentPageIsRedirectTarget(page);
      }
  
      /**
       * Checks if the specified page redirects to the current page.
       *
       * @Param page The page to check.
       * @Return True if the specified page redirects to the current page.
       */
      private boolean currentPageIsRedirectTarget(@NotNull final Page page) {
          return currentPage.equals(Utils.resolveRedirects(page).getLeft());
      }
}

My code below

// package com.myproject.core.models.impl;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import javax.annotation.PostConstruct;
import javax.mail.Quota.Resource;

import com.adobe.cq.wcm.core.components.internal.Utils;
import com.adobe.cq.wcm.core.components.util.AbstractComponentImpl;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Via;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.wcm.core.components.internal.LocalizationUtils;
import com.adobe.cq.wcm.core.components.commons.link.LinkManager;
import com.adobe.cq.wcm.core.components.models.Navigation;
import com.adobe.cq.wcm.core.components.models.NavigationItem;
import com.day.cq.wcm.api.LanguageManager;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageFilter;
import com.day.cq.wcm.api.components.Component;
import com.day.cq.wcm.api.designer.Style;
import com.day.cq.wcm.msm.api.LiveRelationshipManager;

/**
 * Navigation model implementation.
 */
@Model(adaptables = SlingHttpServletRequest.class,
    adapters = {Navigation.class, ComponentExporter.class},
    resourceType = {NavMenuHeaderImpl.RESOURCE_TYPE})

public class NavMenuHeaderImpl implements Navigation {
     
     /**
      * The resource navigation component resource type.
      */
      public static final String RESOURCE_TYPE = "apps/myproject/components/atom_nav-header";
 
      /**
       * Name of the resource / configuration policy property that defines the accessibility label.
       */
      private static final String PN_ACCESSIBILITY_LABEL = "accessibilityLabel";
  
      /**
       * The current request.
       */
      @Self
      private SlingHttpServletRequest request;
  
      /**
       * The link manager.
       */
      @Self
      private LinkManager linkManager;
  
      /**
       * The current page.
       */
      @ScriptVariable
      private Page currentPage;
  
      /**
       * The current style.
       */
      @ScriptVariable
      private Style currentStyle;
  
      /**
       * The language manager service.
       */
      @OSGiService
      private LanguageManager languageManager;
  
      /**
       * The relationship manager service.
       */
      @OSGiService
      private LiveRelationshipManager relationshipManager;
  
      /**
       * The accessibility label.
       */
      @nullable
      private String accessibilityLabel;
  
      /**
       * Number indicating how many levels below the navigation root the navigation should start.
       */
      private int structureStart;
  
      /**
       * Number indicating how many levels below the navigation root should be included in the results.
       * Use "-1" for no limit.
       */
      private int structureDepth;
  
      /**
       * The root page from which to build the navigation.
       */
      private Page navigationRootPage;
  
      /**
       * Placeholder for the list of results.
       */
      private List<NavigationItem> items;
  
      /**
       * Initialize the model.
       */
      @PostConstruct
      private void initModel() {
          ValueMap properties = this.resource.getValueMap();
          structureDepth = properties.get(PN_STRUCTURE_DEPTH, currentStyle.get(PN_STRUCTURE_DEPTH, -1));
          boolean collectAllPages = properties.get(PN_COLLECT_ALL_PAGES, currentStyle.get(PN_COLLECT_ALL_PAGES, true));
          if (collectAllPages) {
              structureDepth = -1;
          }
          if (currentStyle.containsKey(PN_STRUCTURE_START) || properties.containsKey(PN_STRUCTURE_START)) {
              //workaround to maintain the content of Navigation component of users in case they update to the current i.e. the `structureStart` version.
              structureStart = properties.get(PN_STRUCTURE_START, currentStyle.get(PN_STRUCTURE_START, 1));
          } else {
              boolean skipNavigationRoot = properties.get(PN_SKIP_NAVIGATION_ROOT, currentStyle.get(PN_SKIP_NAVIGATION_ROOT, true));
              if (skipNavigationRoot) {
                  structureStart = 1;
              } else {
                  structureStart = 0;
              }
          }
      }
  
      /**
       * Get the effective navigation root page.
       *
       * @Return The effective navigation root page.
       */
      private Page getNavigationRoot() {
          if (this.navigationRootPage == null) {
              String navigationRootPath = Optional.ofNullable(this.resource.getValueMap().get(PN_NAVIGATION_ROOT, String.class))
                  .orElseGet(() -> currentStyle.get(PN_NAVIGATION_ROOT, String.class));
              this.navigationRootPage = LocalizationUtils.getLocalPage(navigationRootPath,
                  this.currentPage,
                  this.request.getResourceResolver(),
                  this.languageManager,
                  this.relationshipManager)
                  .orElseGet(() -> currentPage.getPageManager().getPage(navigationRootPath));
          }
          return this.navigationRootPage;
      }
  
      @Override
      public List<NavigationItem> getItems() {
          if (this.items == null) {
              this.items = Optional.ofNullable(this.getNavigationRoot())
                  .map(navigationRoot -> getRootItems(navigationRoot, structureStart))
                  .orElseGet(Stream::empty)
                  .map(item -> this.createNavigationItem(item, getItems(item)))
                  .collect(Collectors.toList());
          }
          return Collections.unmodifiableList(items);
      }
  
      protected NavigationItem newNavigationItem(Page page, boolean active, boolean current, @notnull LinkManager linkManager, int level,
                                                 List<NavigationItem> children, String parentId, Component component) {
          return new NavigationItemImpl(page, active, current, linkManager, level, children, parentId, component);
      }
  
      @Override
      @nullable
      public String getAccessibilityLabel() {
          if (this.accessibilityLabel == null) {
              this.accessibilityLabel = this.resource.getValueMap().get(PN_ACCESSIBILITY_LABEL, String.class);
          }
          return this.accessibilityLabel;
      }
  
      @notnull
      @Override
      public String getExportedType() {
          return this.resource.getResourceType();
      }
  
      /**
       * Builds the navigation tree for a {@code navigationRoot} page.
       *
       * @Param subtreeRoot The page for which to generate the sub-tree.
       * @Return the list of collected navigation trees
       */
      private List<NavigationItem> getItems(@NotNull final Page subtreeRoot) {
          if (this.structureDepth < 0 || subtreeRoot.getDepth() - this.getNavigationRoot().getDepth() < this.structureDepth) {
              Iterator<Page> childIterator = subtreeRoot.listChildren(new PageFilter());
              return StreamSupport.stream(((Iterable<Page>) () -> childIterator).spliterator(), false)
                  .map(item -> this.createNavigationItem(item, getItems(item)))
                  .collect(Collectors.toList());
          }
          return Collections.emptyList();
      }
  
      /**
       * Gets a stream of the top level of pages in the navigation.
       *
       * @Param navigationRoot The navigation root page.
       * @Param structureStart The number of levels under the root page to begin collecting pages.
       * @Return Stream of all descendant pages of navigationRoot that are exactly structureStart levels deeper.
       */
      private Stream<Page> getRootItems(@NotNull final Page navigationRoot, final int structureStart) {
          if (structureStart < 1) {
              return Stream.of(navigationRoot);
          }
          Iterator<Page> childIterator = navigationRoot.listChildren(new PageFilter());
          return StreamSupport.stream(((Iterable<Page>) () -> childIterator).spliterator(), false)
              .flatMap(child -> getRootItems(child, structureStart - 1));
      }
  
      /**
       * Create a navigation item for the given page/children.
       *
       * @Param page The page for which to create a navigation item.
       * @Param children The child navigation items.
       * @Return The newly created navigation item.
       */
      private NavigationItem createNavigationItem(@NotNull final Page page, @notnull final List<NavigationItem> children) {
          int level = page.getDepth() - (this.getNavigationRoot().getDepth() + structureStart);
          boolean current = checkCurrent(page);
          boolean selected = checkSelected(page, current);
          return newNavigationItem(page, selected, current, linkManager, level, children, getId(), component);
      }
  
      /**
       * Checks if the specified page is selected.
       * A page is selected if it is either:
       * <ul>
       *     <li>The current page; or,</li>
       *     <li>A page that redirects to the current page; or,</li>
       *     <li>The current page is a child of the specified page</li>
       * </ul>
       *
       * @Param page The page to check.
       * @Return True if the page is selected, false if not.
       */
      private boolean checkSelected(@NotNull final Page page, boolean current) {
          return current
              || this.currentPage.getPath().startsWith(page.getPath() + "/");
      }
  
      private boolean checkCurrent(@NotNull final Page page) {
          return this.currentPage.equals(page)
                  || currentPageIsRedirectTarget(page);
      }
  
      /**
       * Checks if the specified page redirects to the current page.
       *
       * @Param page The page to check.
       * @Return True if the specified page redirects to the current page.
       */
      private boolean currentPageIsRedirectTarget(@NotNull final Page page) {
          return currentPage.equals(Utils.resolveRedirects(page).getLeft());
      }
}

 

Avatar

Correct answer by
Community Advisor

@NathanVieira 

 

You can readily use all the existing code by using Delegation https://experienceleague.adobe.com/docs/experience-manager-core-components/using/developing/customiz...

 

You just need to use @Via annotation. Following would not be required.

LinkManager
LocalizationUtils
Utils 

Aanchal Sikka

Avatar

Community Advisor

I don't see a problem with your code... make sure you are using the correct AEM Cloud SDK (if you are on AEMaaCS), and if you are using classic AEM, make sure you have the correct service pack and/or the AEM WCM Core Components installed as expected.

 

This code works fine for me.

<nav data-sly-use.navigation="com.adobe.cq.wcm.core.components.models.Navigation"
     data-sly-use.template="core/wcm/components/commons/v1/templates.html"
     data-sly-use.groupTemplate="group.html"
     data-sly-call="${groupTemplate.group @ items=navigation.items}"
     data-sly-test.hasContent="${navigation.items.size > 0}"
     id="${navigation.id}"
     class="cmp-navigation"
     itemscope itemtype="http://schema.org/SiteNavigationElement"
	 data-cmp-data-layer="${navigation.data.json}"
     aria-label="${navigation.accessibilityLabel}">
</nav>
<sly data-sly-call="${template.placeholder @ isEmpty=!hasContent, classAppend='cmp-navigation'}"></sly>