Navigating the Page Hierarchy

Hierarchical navigations are based on the ability of web pages to have subordinate pages, which is the key to hierarchical content organization in general. With Scrivito, a page with this ability is called a parent page, while the pages subordinate to it are its child pages. Whether a page is a parent or a child or both depends on its path. A path like “/en/company/about”, for instance, indicates that the “about” page is a child of its “company” parent which, in turn, is a child of “en.”

As a page is created, duplicated, or copied and pasted in-place, its path is generated automatically. However, generated paths can be made editable or adjusted programatically. This is useful if path components are used as search criteria, e.g. on websites available in different languages.

For rendering a parent's list of children, you can use the scrivito_tag_list helper, which is explained below.

To visualize our case, we use the demo website which is part of every Scrivito account. Here, the top-level navigation initially looks like this:

You can see this yourself if you go to "my account", open the Scrivito Demo, and switch to Edit mode. To extend this default navigation, click the navigation handle of this item and select Add page.

After selecting the page type, you'll see something like this:

You can set the page title using the page properties dialog that can be opened from the page menu at the top right corner of the page. However, we'll leave it as it is because it doesn't affect the way the navigation is generated.

The view code

Whether you put the view code directly into the layout view or into a partial is not important. Choose the place that best fits your level of modularization.

Looking just at the “Blog” navigation item, this is the view code that renders it:

Copy
<%= scrivito_tag_list(:ul, home_page, :toclist, class: "nav navbar-nav") do |list, item| %>
  <% if item.toclist.present? %>
    <!-- Navigation item has subentries -->
  <% else %>
    <%= list.tag(:li, id: "main_nav_page_#{item.id}", data: { path: item.path }) do %>
      <%= link_to(scrivito_path(item)) do %>
        <%= scrivito_field(item, :title).presence || "[UNTITLED]" %>
      <% end %>
    <% end %>
  <% end %>
<% end %>

scrivito_path is a routing helper part of the Scrivito SDK. It assembles the path of the given CMS object. It accepts as its first parameter either a CMS object, a linklist, or a link.

The code above would produce the following HTML markup:

Copy
<ul class="nav navbar-nav">
  <li id="main_nav_page_f81236edb6e1d151" data-path="/blog">
    <a href="/blog">Blog</a>
  </li>
  <!-- Further main navigation items -->
</ul>

The scrivito_tag_list helper

The scrivito_tag_list helper generates a list of CMS objects. In Edit mode, it also cares for the navigation handle and a drop-down menu that opens when the editor clicks the handle.

scrivito_tag_list takes as arguments the name of the container element, in our case :ul for placing the list items inside a ul element. It further takes a CMS object that acts as the parent of the list items (here the home_page object, but Obj.root would work as well), the list type (e.g. a :toclist), and options that will be passed to the container element (e.g. class).

For each list item, i.e. for each child object of the homepage object, the helper yields the list and the current item into the block where list.tag renders the list item. Here, too, the first parameter is the name of the HTML element, :li, which renders an li tag. There are other parameters passed to the li tag as well: the data: {path: item.path} attribute, for example, puts the path of the item into the data-path HTML attribute. This information could be used for highlighting the currently selected menu item by adding class="active" to it (using JavaScript). Whether an item is the currently selected one can be determined by comparing its data-path with the path of the current page.

The list.tag block contains a link to the corresponding CMS object using scrivito_path. The a tag encloses the title of the object if set, otherwise a dummy title.

A sidebar navigation

On the screenshot to the left, which has been taken from this documentation, a sidebar navigation with two navigation handles can be seen, one for each level. On the selected level, two children have been inserted for demonstration purposes.

This is, roughly, the view code used for generating the sidebar navigation:

Copy
<div class="sidebar-nav">
  <h3><%= @obj.parent.headline %></h3>
  <%= scrivito_tag_list(:ul, @obj.parent, :toclist, class: "nav nav-list", id: "sidebar-nav") do |list, item| %>
    <%# siblings %>
    <%= list.tag(:li, data: {path: item.path}) do %>
      <%= link_to(scrivito_path(item)) do %>
        <%= item.headline %>
      <% end -%>
      <%# children of active menu item %>
      <% if item == @obj -%>
        <%= scrivito_tag_list(:ul, item, :toclist) do |list2, item2| %>
          <%= list2.tag(:li, data: {path: item2.path}) do %>
            <%= link_to(scrivito_path(item2)) do %>
              <%= item2.headline %>
            <% end -%>
          <% end -%>
        <% end -%>
      <% end -%>
    <% end -%>
  <% end -%>
</div>

The resulting HTML markup is similar to what has been generated for the top-level navigation above.

Inside the <div> element, the view first renders the headline attribute of the parent object as an h3 heading. Next, the navigation items underneath the parent of the obj, i.e. the obj itself as well as its siblings, are rendered by means of the scrivito_tag_list helper. For the current object, its children are rendered as the second level of the navigation using another scrivito_tag_list.

If your navigation items aren't sortable although you want them to be, take a look at Making Navigations Sortable.