Layout Pages, Content Pages, Partials and Sections

The Response.ExpandTemplate() and Response.ExpandScript() methods can render Partial and Layout pages. Partial pages allow creating nested pages of partial content that is pulled into the parent page. Layout pages allow you to create the base page layout in a single page that can be reused for many or all content pages. Layout pages then contain a Content region into which the content page is rendered as well as optional sections that can be used to render additional content from the content page into the Layout page. A content page references a Layout page and the layout page then renders the content page into it.

Scripts and Templates: Same Syntax

Scripts and Templates use the same basic syntax to describe Layout, Sections and Partials using specific tags that can be embedded into a script or template.

For the following examples I'll use a script page (.wcs), but the syntax related to these concepts is identical in both scripts and templates with the exception that templates can't process code blocks.

For most applications we recommend script pages over templates. Templates work well for one off pages where you know you're only displaying data and not executing code.

Finicky Markup Syntax for Page Directives

The markup syntax for various page directives are finicky and have to be specified with exact syntax, double quote characters and spacing.

Here's a list of the build-time directives:

For Content Pages:

  • <% Layout="~/somepage.wcs" %>
  • <%= RenderPartial("~/partialPage.wcs") %>
  • <%= RenderSection("mysection") %>

For Layout Pages:

  • <%= RenderContent("~/somepage.wcs") %>
  • <%= section "sectionName" %>
  • <% endsection %>

These directives are parsed out of the document at build or parse time and due to the way they are parsed, the syntax has to be exact.

Content Page

Content pages are the pages that contain your main page content. The top level page you render as a script or template is always a content page. Content pages can either be self contained which means it contains a full HTML document, or it can be an HTML fragment is merged into a Layout Page that you specify in the header of the content page. Content pages are always the entry point for any script or template you render.

When you create CustomerList.wcs or CustomerDetail.wcs the page that is referenced first and starts template/script execution is always the content page.

Content Pages can also reference Partial Pages which are essentially child pages that are imported into the current page. This is useful for breaking up complex pages into more manageable, smaller page fragments that are easier to work with.

Here's a self contained Content Page:

<html>
<head>
   <title>< %= pcTitle %>
</head>
<body>
   Hello < %= pcName %>, the time is <%= TIME() %>.
   <hr>
   <% for x=1 to 10 %>
       <%= "output" + TRANSFORM(x) %>
   <% endfor %>
</body>
</hml>

And here's a more complex Content Page that references a Layout page and pulls in content from a Partial page:

<% Layout="~/LayoutPage.wcs" %> 
<% section="headers" %>
     <title>My 3nd Awesome Web Page - <%= Time() %> </title>
     <link href="css/MyPage.css" rel="stylesheet" />
<% endsection %>  
 
<div>           
<h3>This is my CONTENT PAGE</h3>
<p>   
  Time is: <%= DateTime() %> Yup it is!   
</p>
  
<h3>Partial Content below (10x)</h3>  
<hr />  
<% for x = 1 to 5 %>     
    <%= RenderPartial("~/PartialPage.wcs") %>  
<% endfor %>
<hr />
    
           
</div>               
<% section="bottomcontent" %>
    Bottom Content:<br />
    <%= Version() %>
<% endsection %>
<% section="scripts" %>    
    <script src="bower_components/jquery/jquery.js"></script>
<% endsection %>

This page contains a Layout section, a Partial reference and a couple of Sections that allow injecting content into a Layout page from the Content Page.

Layout Pages

To reference a layout page from a Content page use the Layout directive at the top of the page with a location for the Layout Page:

<% Layout="~/Views/LayoutPage.wcs" %>

Layout pages are top level pages that typically hold a common page frame - things like the page head (html,head,body tags) as well as the HTML for the banner, top level menu, as well as a footer. Most pages in a site use a common layout and a Layout page can provide most of that common layout on a single page.

An embedded <%= RenderContent() %> tag is then used to embed the actual Content page into the layout.

Here's a simple layout page:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>    
    <%= IIF(vartype(pcTitle) = "C",pcTitle,"") %>    
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link href="bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />
    <%= RenderSection("headers") %>
</head>
<body>    
    <%= RenderPartial("~/Views/Common/LoginTag.wcs") %>
   
    <h1>HEADER FROM LAYOUT PAGE</h1>         
    
    Content goes here:
    <%= RenderContent() %>    
    
    <hr />   
    LAYOUT FOOTER 
             
    <%= RenderSection("scripts") %>           
</body>  
</html>

This page contains a Content area, two sections and one partial.

Content Section
<%= RenderContent() %>

RenderContent() is the most important tag on a Layout Page: It's where the Content Page's content gets embedded. The content rendered is the initial page you requested as part of the render operation so CustomerList.wcs or CustomerDetail.wcs for example. The template engine processes both the layout page and the content page and merges the content into the layout page at runtime to produce a single HTML document.

Sections

A Layout page can define Sections, which allow Content pages to inject content back up into the Layout page.

If you're new to Layout pages, this may seem like a strang concept but content pages are contained within a Layout page at specific location namely where the Layout page calls <% RenderContent() %>. A content page cannot direct affect any content in the layout page except where the result of <%= RenderContent() %> is merged in. If the Content Page needs to add content at the top or bottom of the page, there's no direct way of doing that.

Sections allow a way around this by providing injection points in the Layout Page where a Content Page can inject content back up into the Layout page.

The default layout page template has two pre-defined sections:

  • headers - Defined at the bottom of the <head> section
    Use this to inject additional page headers into the page. For example if you need to add meta tags or custom social media headers. You can also use this to add CSS Stylesheets or scripts that need to load BEFORE the stock script libraries

  • scripts - Defined at the bottom of the page after script library definitions

These two sections let you perform common tasks of adding CSS and JavaScript to a content page and let them be rendered at the appropriate location in the document to work properly. Scripts have to be at the end of the document (generally before the </body> tag so they fire after the Document has been defined for example.

You can add your own sections into a layout page to create additional insertion points.

To define a section in a Layout Page use this syntax by providing a section label:

<%= RenderSection("scripts") %>

This creates an insertion point where section text from a Content Page can be rendered.

To declare section content that gets injected into the Layout Page from a Content Page:

<% section="scripts" %>
<script src="scripts/mypage.js"></script>
<script>
   // your javascript code here
</script>
<% endsection %>

This injects the result of the scripts section in the Content Page into the <%= RenderSection("scripts") %> location of the Layout page.

Section Content Expressions are Evaluated

Any expressions defined inside of a Section are always evaluated rather than executed, even if running inside of a Script page. This means you can't use Codeblocks inside of sections nor access LOCAL variables. Any variables referenced from scripts must be PRIVATE or PUBLIC passed down via the FoxPro call stack.

If you need to do more complex processing, add a <%= RenderPartial() %> to the section and add your more complex code to that for normal processing of the content.

Partial Pages

Partial pages are nested, child pages that are rendered from a content or layout page.

To reference a partial page embed the following:

<%= RenderPartial("~/Views/Common/LoginTag.wcs") %>

RenderPartial() embeds the content of another page into the current Content Page or Layout Page. You pass a Web site relative path starting with the ~ which signifies the site root path, followed by the path to the page you want to pull into the current page. The partial page is then executed as a script or template and the content injected into the page replacing the <%= RenderPartial() %> expression.

We've already seen partial pages called in the other examples. Partial pages are typically HTML fragments, not complete pages. A good example of a partial page might be the Login/Account Status section at the top of the page. You can create the partial once and re-use it in multiple pages.

<div class="login-widget">
    <% if poUser.IsAuthenticated %>
       <a href="profile.wcs"><%= poUser.Username %></a>
    <% else %>
       <a href="login.wcs">Login</a>
    <% endif %>
</div>

To use the partial you then simply reference the page where the script is stored from another script.

<%= RenderPartial("~/LoginWidget.wcs") %>

Partials are great for breaking up complex pages into smaller more manageable sub-pages as well as providing re-usability. For example, if you have a complex list display you might want to break the individual rows or even individual columns out to be rendered from a partial to make the HTML more readable.

Putting it all together

Building applications that use one or more Layout pages, with a number of Sections, and then using Content pages to create the actual content for pages in small self contained blocks, make HTML creation much more manageable. You don't have to copy and paste large blocks of common HTML into every page or render partial scripts into each page. Rather you let the Layout page handle the 'top level' layout of the page, with a few holes left to fill for the main RenderContent() and with Section directives to allow any content pages to fill in other common areas of the page.

Partials are useful to create reusable page components, or to break up large complex pages into smaller more manageable chunks.

Using all of these in combination makes it much easier to create complex applications and it's an effective workflow that minimizes repeated markup.


© West Wind Technologies, 1996-2019 • Updated: 01/05/19
Comment or report problem with topic