Menu app

We will generate the menu directly from your filesystem tree:

Let's create a menu app at /apps/menu and set its type to app-1 to to turn it into an application. Then create a child named .Inline inside it that will be executed for inlining it within the page.

We'll have to modify our page layout to embed the menu. We will inline it with .inline(false) to avoid wrapping it in a div that would identify the block as the /apps/menu file on the filesystem. This will prevent end-users from accidentally removing the menu.

<?
    var css = response.addCssModule(source.select('../style.css'));
?>
<!DOCTYPE html>
<html>
<head>
    <title><?= f.title() ?></title>
</head>
<body>
    <div class="<?= css.wrapper ?>">
        <div class="<?= css.menu ?>">
            <?== source.select('/apps/menu').inline(false) ?>
        </div>
        <div class="<?= css.main ?>">
            <?== f.query(':2').inline() ?>
        </div>
    </div>
</body>
</html>

Menu code

Let's use the sjs-4 JavaScript engine on the /apps/menu/.Inline file as our menu will contain more JS code than HTML.

First, let's create a renderMenu() function to render a single menu entry:

function renderMenu(page) {
    var s = '';
    s += '<a href="' + page.path() + '">';
        s += util.htmlEncode(page.title());
    s += '</a>';
    return s;
}

We can use it to render the menu entry for the root file which we can get by f.root(). Then we can return it in the response file by setting response.body(p).

function renderMenu(page) {
    var s = '';
    s += '<a href="' + page.path() + '">';
        s += util.htmlEncode(page.title());
    s += '</a>';
    return s;
}
 
var p = renderMenu(f.root());
response.body(p);

Note that this is not the same response file we have used in the .Request method of the page app.

When a content is inlined (rendered), Boomla creates a new sandboxed environment for executing it with its own response file. The application must write that  response file's body to return a response to its callee. The callee in turn has to embed the returned results into the response it generates.

Boomla also starts a new transaction every time a content is inlined so that it can automatically roll back any actions in case the application crashes unexpectedly.

To render the entire page tree of the website, we have to call the renderMenu() function recursively. As pages are always stored in bucket 1, we can use page.query(':1') to find them.

function renderMenu(page) {
    var s = '';

    s += '<a href="' + page.path() + '">';
        s += util.htmlEncode(page.title());
    s += '</a>';
 
    page.query(':1').each(function(subpage) {
        s += renderMenu(subpage);
    });

    return s;
}
 
var p = renderMenu(f.root());
response.body(p);

CSS

Let's create a dedicated CSS file for our menu at /apps/menu/menu.css, again with type css-2.

We will highlight the active page by adding an active class to it. We can check all pages if they match the context file which point to the currently visited page (the page in context). You can compare two files via context.id() === page.id().

Also, let's indent each level by wrapping the entries in a class named subpages. We will support 4 levels of subpages, increase it if necessary.

Here is an example CSS:

.a {
    display: block;
    padding: 5px 10px;
    color: #333333;
    text-decoration: none;
}
.a:hover,
.a.active {
    background: #DDDDDD;
}
.subpages .a {
    padding-left: 25px;
}
.subpages .subpages .a {
    padding-left: 40px;
}
.subpages .subpages .subpages .a {
    padding-left: 55px;
}
.subpages .subpages .subpages .subpages .a {
    padding-left: 70px;
}

Then we can import and use it as before.

var css = response.addCssModule(source.select('../menu.css'));
 
function renderMenu(page) {
    var s = '';
 
    var classNames = css.a;
    if (context.id() === page.id()) {
        classNames += ' ' + css.active;
    }

    s += '<a class="' + classNames + '" href="' + page.path() + '">';
        s += util.htmlEncode(page.title());
    s += '</a>';
 
    s += '<div class="' + css.subpages + '">';
        page.query(':1').each(function(subpage) {
            s += renderMenu(subpage);
        });
    s += '</div>';

    return s;
}
 
var p = renderMenu(f.root());
response.body(p);

Warning: make sure to set the title property of your page instance files otherwise the menu will be blank.

Time to test drive it in your browser!

See how to do it (silent video)

Next

At this point, we have a multi-page website, with a centrally managed page layout and contents nicely stored in separate files. We can edit the website via the IDE and create new pages and contents as files on the filesystem.

Let's take this to another level by making both the menu and the text contents on the page editable via the built-in rich text editor of Boomla.

Subscribe to our newsletter!