Building Navigations

Let's take a closer look at a navigation from our sample site to see how we would go about building it. For our example, we'll use the ancestor navigation used to generate breadcrumb links.

Note: The Path attribute value on the Category element is omitted from page XML (and DSS Preview) by design. Be aware of this if you had planned to use the Path value in view implementations (e.g., for navigations).

No Path Attribute Value on Category Element

Ancestor Navigation

In our sample site, the ancestor navigation is part of the header content and shows a visitor's location and depth in the Site Tree.

Ancestor Navigation Heading

Ancestor Navigation

Site Tree

The navigation is rendered by a partial view, AncestorNavigation, on _SectionLayout.


The HTML cut-up for the navigation is straightforward:

"div" tags wrapping an unordered list, with two hard-coded, non-functional links and some styling attributes.

HTML Cutup for Navigation

In order to make this cutup into a functional, dynamic navigation, we need to replace the hardcoded values (Home, Newsroom, and Keynote Speakers) with XML data from the model. We also need to implement logic for leaving folders out of the navigation and for looping through ancestor pages to generate links. All together, the AncestorNavigation view will look something like this:

@model Ingeniux.Runtime.ICMSNavigationElement
@Using Ingeniux.Runtime
    if (Model != Null)
        <div class="links">


        <ul         @{ var links = Model.pages.Where(ele => ele.Schema != "Folder").ToArray();
            var current = links.LastOrDefault();
            foreach (var link in links)           
                    String navName = !String.IsNullOrEmpty(link.GetAttributeValue("navName")) ?
                    link.GetAttributeValue("NavName") : link.LinkName;          <ul>            
            @if (link.url != current.URL)           
          @_Helpers.RenderLink(Url.ProcessUrl(link.Url), navName);      
            <span class="separator"></span>
else { <span>@navName</span> } </li> } } </ul> </div> } }

For a model, we're using Ingeniux.Runtime.ICMSNavigationElement. This is an interface that retrieves pages to be used as link elements in a navigation. At the top of the view, we check to see if the model is null. Then, if it's not -- that is, if the navigation element exists -- we proceed to generate the ancestor navigation. The links variable builds the array of pages that we'll loop through.

var links = Model.Pages
     .Where(ele => ele.Schema != "Folder")

It also filters out folders, because in most cases we don't want folders to appear in our navigations. We use another variable, current, to create a special display for the active link in the navigation.

var current = links.LastOrDefault();

The LastOrDefault method returns the last element in a sequence, which, in an ancestor navigation, is the page that the visitor is on.

Having declared these variables, we use a foreach function to loop through the list and generate navigation links. We declare a navName variable to create a name for each link. For links that are not current (that is, not linked to the current page), we use the RenderLink helper to generate URLs and link them to navName. For the current page, we don't actually need a link, so we just display the navName string.

All of this may seem a little bit complicated, but the basic idea behind it is simple: loop through a collection of pages and, for each page, render a link. The following simplified code represents the basic logic behind our ancestor navigation.

Simplified Ancestor Navigation

This logic underlies most other CMS site navigations as well.