FlexMenuBuilder

Introduction

Purpose

In Lift we create menus for our application defining Sitemap and MenuItems. These classes give us a great power to combine and create the required menu but it has some limitation related with the generated Html. If you choose to use frameworks like Bootstrap, Foundation, etc. we need to set css classes or use a combination of tags to get a modern html menu. To specify a way to construct the html using those libraries was not a Sitemap requirement. This task will be accomplished by using FlexMenuBuilder.

Design Overview

To provide a way to introduce and build the menu using the specific front-end framework, the trait FlexMenuBuilder will be defined.
FlexMenuBuilder: in order to construct a menu with the required configuration, this trait will read the defined Sitemap in Lift’s bootstrap and apply the configuration specified in a subclass of this trait. This will be done with some common methods and properties – used by front-end frameworks – that are defined in FlexMenuBuilder.
Besides FlexMenuBuilder we will need to define a subclass that will creates the final menu.

Architectural Design

Chosen Architecture

The chosen architecture for this component is a trait that must be extended with the required configuration.
The idea of usage is similar to Menu.builder, its construction must be placed in the main template. FlexMenuBuilder will call render method to show the menu in every page loaded – is recommended to use it as an object- that is created using the defined Sitemap.
The new trait comes with some definitions that can and should be overriden

Discussion of Alternative Designs

Since the render method need to run on every page to create the current menu, it can be modified to use memoization or dynamic programming to avoid repeated computations and provide a method to reload when necessary.

Component Interface Definition


package net.liftweb.sitemap
trait FlexMenuBuilder  {
  type StructBuildItem = AnyRef {
  def buildItem(kids : List[MenuItem], current : Boolean, path : Boolean) : Box[MenuItem]
}
  def linkToSelf : Boolean
  def expandAll : Boolean
  protected def expandAny : Boolean
  protected def buildItemMenu[A](loc : Loc[A], currLoc : Box[Loc[_]], expandAll : Boolean) : List[MenuItem]
  def toRender : Seq[MenuItem]
  protected def emptyGroup : NodeSeq
  protected def emptyMenu : NodeSeq
  protected def emptyPlaceholder : NodeSeq
  protected def updateForPath(nodes : Elem, path : Boolean) : Elem
  protected def updateForCurrent(nodes : Elem, current : Boolean) : Elem
  protected def buildInnerTag(contents : NodeSeq, path : Boolean, current : Boolean) : Elem
  protected def renderPlaceholder(item : MenuItem, renderInner : Function1[Seq[MenuItem], NodeSeq]) : Elem
  protected def renderSelfLinked(item : MenuItem, renderInner : Function1[Seq[MenuItem], NodeSeq]) : Elem
  protected def renderSelfNotLinked(item : MenuItem, renderInner : Function1[Seq[MenuItem], NodeSeq]) : Elem
  protected def renderSelf(item : MenuItem) : NodeSeq 
  protected def renderLink(uri : NodeSeq, text : NodeSeq, path : Boolean, current : Boolean) : NodeSeq
  protected def renderItemInPath(item : MenuItem, renderInner : Function1[Seq[MenuItem], NodeSeq]) : Elem
  protected def renderItem(item : MenuItem, renderInner : Function1[Seq[MenuItem], NodeSeq]) : Elem
  protected def renderOuterTag(inner : NodeSeq, top : Boolean) : NodeSeq
  protected def renderWhat(expandAll : Boolean) : Seq[MenuItem]
  def render : NodeSeq
}

Description of FlexMenuBuilder Trait

Working example

Getting it from Github

> git clone https://github.com/antidata/FlexMenuBuilderExample.git

Running the example

> sh sbt.sh
> container:start

then load the page http://localhost:8080

Snippet

The snippet created to extend the new trait is the object in MyMenu.scala.
The reference to the snippet is in /templates-hidden/default.html

<span data-lift="MyMenu.builder"></span>

FlexMenuBuilder source code

https://github.com/lift/framework/blob/master/web/webkit/src/main/scala/net/liftweb/sitemap/FlexMenuBuilder.scala