• Home
  • Why Erstwhile?
  • Getting Started
  • Learn Erstwhile
  • API Docs
  • Home
  • Why Erstwhile?
  • Getting Started
  • Learn Erstwhile
  • API Docs
  • Learning Erstwhile
  • File Structure
  • Compilation Process
  • Routing
  • Request Flow
  • Components and ErML
    • How Components are Compiled
    • Themes
    • Layouts
  • Scoped Variables
  • Modals
  • Models
    • Using an Erstwhile-aware Backend
    • Creating Your Own Description.json
    • Using Models from the Controller

Layouts

7 views 0

Written by mike
August 8, 2023

Layouts are specialized components that are subclasses of ErstwhileLayout. They are meant to serve as frames for your action templates, and typically include things like navigation, sidebars, headers, footers, and things like that. Quite often they will use Scoped Variables in the session scope to show things like user names or photos, or render navigation that’s present across multiple pages.

Fat Layouts

Erstwhile believes in getting things done in as little code as possible. It’s a fact that HTML is a fairly verbose markup language, particularly when it comes to things like navigation where you often need to use additional tags to move things around and position them correctly across different breakpoints. Here is where the concept of Fat Layouts comes in.

Using Mazer’s <FullLayout /> as an example, the code is pretty weighty:

<%
  function renderSidebarMenuItem( menuItem, submenu = false ) {
    %>
    <li class="<%= (submenu ? "submenu-item" : "sidebar-item") %> <%= (menuItem.id && menuItem.id == args.sidebarMenuActive ? "active" : "") %> <%= (menuItem.children ? "has-sub" : "") %>" <%= (menuItem.id ? `id="sidebar-menu-${menuItem.id}"` : "") %>>
      <a href="<%= (menuItem.link ? menuItem.link : "#") %>" class="<%= (submenu ? "submenu-link" : "sidebar-link") %>">
        <% if(menuItem.biIcon) { %><i class="<%= `bi bi-${menuItem.biIcon}` %>"></i><% } %>
        <span><%= (menuItem.label ? menuItem.label : "Item") %></span>
      </a>
      <% if(menuItem.children) { %>
        <ul class="submenu">
          <% for(let j in menuItem.children) {
            renderSidebarMenuItem(menuItem.children[j], true);
          } %>
        </ul>
      <% } %>
    </li>
    <%
  }

%>
<div id="app">
  <div id="sidebar">
    <div class="sidebar-wrapper active">
      <div class="sidebar-header position-relative">
        <div class="d-flex justify-content-between align-items-center">
          <div class="logo">
            <a href="/"
              ><img
                src="<%- (args.logo ? args.logo : "https://placeholder.pics/svg/500x200") %>"
                alt="Logo"
                srcset=""
            /></a>
          </div>
          
          <div class="sidebar-toggler x">
            <a href="#" class="sidebar-hide d-xl-none d-block"
              ><i class="bi bi-x bi-middle"></i
            ></a>
          </div>
        </div>
      </div>
      <div class="sidebar-menu">
        <ul class="menu">
          <% if(args.sidebarMenu && args.sidebarMenu.length > 0) { %>
            <% for(let i in args.sidebarMenu) {
              if(args.sidebarMenu[i].label) { %><li class="sidebar-title"><%= args.sidebarMenu[i].label %></li><% } %>
            <% 
              for(let j = 0; j < args.sidebarMenu[i].items.length; j++) { 
                renderSidebarMenuItem(args.sidebarMenu[i].items[j]);
              } 
            %>
          <% }} %>
        </ul>
      </div>
    </div>
  </div>
  <div id="main">
    <header class="mb-3">
      <a href="#" class="burger-btn d-block d-xl-none">
        <i class="bi bi-justify fs-3"></i>
      </a>
    </header>

    <div class="page-heading">
      <div class="page-title">
        <div class="row">
          <div class="col-12 col-md-6 order-md-1 order-last">
            <h3 id="page-title"><%- args.pageTitle %></h3>
            <p id="page-intro" class="text-subtitle text-muted">
              <%- args.pageIntro %>
            </p>
          </div>
          <div class="col-12 col-md-6 order-md-2 order-first">
              <nav
                aria-label="breadcrumb"
                class="breadcrumb-header float-start float-lg-end"
                id="breadcrumb-container"
              ><% if(args.breadcrumbs) { %>
                <ol class="breadcrumb">
                  <% for(let i in args.breadcrumbs) { %>
                    <li class="breadcrumb-item <%= (i == args.breadcrumbs.length - 1 ? 'active' : "") %>" <%= (i == args.breadcrumbs.length - 1 ? 'aria-current="page"' : "") %>>
                      <% if(args.breadcrumbs[i].link) { %>
                        <a href="<%- args.breadcrumbs[i].link %>"><%- args.breadcrumbs[i].label %></a>
                      <% } else { %>
                        <%- args.breadcrumbs[i].label %>  
                      <% } %>
                    </li>
                  <% } %>
                </ol>
                <% } %>
              </nav>
            
          </div>
        </div>
      </div>
      <pagecontent />
    </div>

    <footer>
      <div class="footer clearfix mb-0 text-muted">
        <div class="float-start">
          <% if(args.copyrightYear || args.copyrightName) {%>
            <p><%- args.copyrightYear %> &copy; <%- args.copyrightName %></p>
          <% } %>
          
        </div>
        <div class="float-end">
          <% if (args.footerMessage) { %>
            <p>
              <%- args.footerMessage %>
            </p>
          <% } %>
          
        </div>
      </div>
    </footer>
  </div>
</div>

We call this a Fat Layout because it contains a bunch of code you don’t want to have to look at every day. You want to figure it out once and then stick it in a library somewhere to not have to think about again.

By sticking it in an Erstwhile component, the application can have a layout (under /app/views/layouts/full.ejs, for example) that looks like this:

<FullLayout 
  logo="/assets/images/todos-logo.png" 
  sidebarMenu="@.session.sidebarMenu"
  sidebarMenuActive="@.page.sidebarMenuActive"
  breadcrumbs="@.page.breadcrumbs"
  copyrightYear="<%= (new Date()).getFullYear() %>"
  copyrightName="<a class='external' target='_blank' href='https://www.restlessdev.com'>RestlessDev</a>"
  footerMessage="Made with <span class='text-danger'><i class='bi bi-heart-fill icon-mid'></i></span> in New Orleans"
  pageTitle="@.page.title"
  pageIntro="@.page.intro"
  appName="Todos" 
  meta="@.page.meta"
  >
  <pagecontent />
</FullLayout>

Your application is now only concerned with the data that needs to get to the layout and not all of the unsightly markup. It’s a cabbage soup diet for your code.

Was this helpful?

Yes  No
Related Articles
  • Using Models from the Controller
  • Creating Your Own Description.json
  • Using an Erstwhile-aware Backend
  • Models
  • Modals
  • Scoped Variables

Didn't find your answer? Contact Us

Previously
Themes
Up Next
Scoped Variables

loader

Join our Mailing List     

  • Privacy Policy
  • Terms of Use
  • © 2025 RestlessDev. Made in New Orleans.

Welcome to Erstwhile

Thank you for checking out Erstwhile. We’re just getting started, so if you’d like to keep up with our progress please register now and we’ll let you know when new releases come out.

loader

Email Address*

Name