Building a Widget for Subpage Navigations

Building a Widget for Subpage Navigations

In this tutorial, we are going to extend the Scrivito Example Application so that editors can place navigations for subpages on any page.

For this, we’ll develop a widget that utilizes the hierarchical relationship between CMS objects that accounts for the top-down structure a website built with Scrivito may have.

With Scrivito, web pages can be organized in a reversed-tree-like structure, meaning that pages can act as parents (or nodes) to which child pages can be added (each of which may again be a node). The page at the very top, the origin of all descendants, is the root page. The unique position each CMS page object has in the hierarchy is identified by the value of its respective _path attribute. In a path like “/company/staff”, the root page is the first ”/”; “company” would be one of the root’s child pages and, at the same time, the parent of “staff”.

You actually don’t need to take care of paths because Scrivito maintains them for you. As an editor, you can create subpages using the main menu at the top right. And if a navigation is rendered using the built-in Scrivito.ChildListTag component, then subordinate pages can be created using the menu behind the blue handle of the navigation, like shown on the screenshot.

We want the starting point of our navigation to be configurable so that it can be used to list the child pages of any page on your website, not just the ones underneath the page containing the navigation widget. Additionally, the widget should render an “up-by-one-level” link and support adding a title.

We’ll also show you how to apply custom styles to the navigation, as an example of how to adapt the look of a Scrivito application in general.

First things first: provide the model class

Every CMS object or widget type needs a model class that specifies the attributes of its instances, so let’s create one. In a Scrivito app, the files a widget type is made up of are located in a folder named after the widget, underneath the “src/Widgets” folder. We’ll name the widget ChildNavigationWidget and provide it with two attributes to cover its features outlined above:

As you can see, the model has an attribute named parentPage. It is meant for customizing the starting point of the navigation. Its type is reference, which is exactly the attribute type needed for pointing to CMS objects. It lets editors set the attribute value conveniently using the Content Browser. The second attribute is navTitle, a string for storing the navigation title. That’s all at this point. If you like the idea behind this widget type and want to extend it later on, you’ll surely require one or two further attributes.

Make the attributes editable

Before elaborating on the component for rendering the instances of our widget, let’s quickly provide the configuration required for setting the value of the parentPage attribute via the properties dialog of actual widgets of this type. From the imported icon (which we didn’t change) you can see that we used the LinkListWidget as a template for our ChildNavigationWidget:

Next to the properties for displaying the widget type in the widget selection dialog (title, description, thumbnail), only parentPage is configured here, i.e. added to the widget instances’ properties dialog. For the other attribute, navTitle, no configuration is required because we are going to make it in-place editable.

In general, it’s a good idea to place only those attributes on a properties dialog that cannot be edited directly on the page. This helps preventing properties dialogs from overflowing.

The “infrastructure” of our widget type is now complete, so let’s turn to the rendering part.

Render the widget instances

The ChildNavigationWidget component is meant to do the following:

  1. Render the navTitle attribute.
  2. Render an “up-one-level” link.
  3. Render links to the children of the parentPage object.

Editors should not have to specify a parentPage but can do so optionally. If none is given, the page containing the widget (i.e. the current page) is used as the parentPage.

There is some logic required to render the “up-one-level” link, so it’s delegated to a separate component, ParentLink. To enable this component to access content, we connect it to Scrivito using Scrivito.connect. Add the following code to the “ChildNavigationWidgetComponent.js” file:

The ParentLink component receives the widget’s parentPage object as a prop. It renders the link only if neither the passed-in object nor its parent object is null. This precautionary measure is advisable because Scrivito loads content asynchronously, meaning that the objects might or might not be present when the component is invoked.

Also, if the passed-in CMS object is the current page, the parent of this object is linked so that the link, which is rendered using Scrivito.LinkTag, doesn’t point to the page already being displayed.

The links pointing to the children of the parent object (the actual navigation) are rendered using the built-in Scrivito.ChildListTag component (see above for how it’s invoked). As a default, Scrivito.ChildListTag wraps each link in an <li> element, but you can customize this behavior by specifying your own rendering function via the renderChild prop. We make use of this option here to pass some CSS class names to the individual <li> elements. This is what our rendering function looks like (add this code to the “ChildNavigationWidgetComponent.js” file, too):

With some additional logic we are highlighting the navigation link that points to the current page.

This can only apply to one of the navigation items if the widget’s parentPage and the parent of the current page (containing the widget) are identical.

As for the code parts, the widget is now complete! Test it out!

Aligning the widget’s styling with your website

The ChildNavigationWidget component applies the (not yet existing) childNav CSS class to its wrapper <div> element as it renders the navigation.

To provide a childNav CSS class, just save it to a .scss file in the “src/assets/stylesheets” folder of your project directory. Then import the file in the “index.scss” file as described in Extending the Example App Styles.

The childNav CSS class for styling the navigation widget could look like the one given here (we named the file “_childnav.scss”).

After saving and importing the file, the class should be instantly effective. To make the linked list items look even better, you could replace the child dummy class in the renderChild function with a built-in icon class: fa fa-caret-right.

See the screenshot below for what the new childNav CSS class effects. Remember to place the navigation widget on a page with children (the homepage or the “Pages & Widgets” page of the Scrivito Example Application), or set the parent page accordingly. Otherwise, the navigation won’t contain any items.

That’s it! You now have a widget for placing a children-based navigation on any page! As with many new page and widget types, this one has potential, too: It displays the navigation independently of whether the parentPage has children or not. Also, it would be nice for editors to be able to specify the horizontal alignment of the navigation, for example. Both extensions shouldn’t be too difficult to implement...