Sharing Common Layout

In this section, we will use the master/content feature of PRADO to share common layout among pages. Common layout refers to the area that is the same or largely the same for a set of pages. For example, in our blog system, all pages will share the same header, footer and side-bar containing shortcut links. A straightforward implementation is to repeat the common layout in every page. However, this approach is prone to error and is hard to maintain. The master/content feature allows us to treat the common layout as a control which centralizes the logic and presentation of the common layout for every page.

Info: It is also possible to share common layout via template inclusion, which is like PHP file inclusion. The drawback of template inclusion is that it is not self-contained and does not carry a class to contain the logic for the common layout.

Creating Master Control

We now create the master control MainLayout to represent the common layout shared by our blog pages. The MainLayout control is a template control extending from TTemplateControl. It requires a template file MainLayout.tpl and a class file MainLayout.php located under the same directory. To facilitate maintenance, we create a new directory protected/layouts to hold them.

For the moment, MainLayout only contains a simple header and a footer, as shown in the following. In future, we will add a side-bar to it. Readers are also encouraged to enhance the layout with other features.

<html>
<com:THead />
<body>
<com:TForm>
<div id="page">

<div id="header">
<h1>My PRADO Blog</h1>
</div>

<div id="main">
<com:TContentPlaceHolder ID="Main" />
</div>

<div id="footer">
<%= PRADO::poweredByPrado() %>
</div>

</div>
</com:TForm>
</body>
</html>

The above shows the content in the template file MainLayout.tpl. Three new tags are used:

  • <com:TContentPlaceHolder> represents TContentPlaceHolder control. It reserves the place in the template where content will be placed at. Here, the content comes from the pages who use this master control.
  • <com:THead> represents THead control which represents the <head> tag in HTML. It allows PRADO to manipulate the <head> tag as a component (e.g., setting page titles, adding custom CSS styles.)
  • <%= %> is an expression tag. It displays the evaluation result of the enclosed expression at the place where it appears.

The class file MainLayout.php is very simple:

<?php
class MainLayout extends TTemplateControl
{
}
Info: The file extension name for page templates is .page, while for non-page templates it is .tpl. This is to differentiate pages from other controls. They both use the same template syntax. For pages, their class files are optional (default to TPage), while for non-page controls, their class files are mandatory. Similar to Java, the name of a class file must be the same as the class name. Be careful about the case-sensitivity on Linux/Unix systems.

Using Master Control

To use the newly created master control, we will modify Home.page and Contact.page. In particular, we need to remove the header and footer from them because the master control will be responsible for displaying them; and we need to tell the two pages that they should use MainLayout as their master.

The following shows the content in Contact.page after the change:

<%@ MasterClass="Application.layouts.MainLayout" Title="My Blog - Contact" %>

<com:TContent ID="Main">

<h1>Contact</h1>
<p>Please fill out the following form to let me know your feedback on my blog. Thanks!</p>

...textbox and validator for user's name...

...textbox and validators for user's email...

...textbox and validator for user's feedback content...

<com:TButton Text="Submit" OnClick="submitButtonClicked" />

</com:TContent>

Content enclosed within the <com:TContent> tag will be inserted into the place that is reserved by <com:TContentPlaceHolder> in the master template.

Info: It is possible to have multiple TContentPlaceHolder's in a master template and multiple TContent's in a content template. They are matched to each other by their ID values. It is also possible to make a content template the master of another content template by placing a TContentPlaceHolder in the former. This is called nested master.

Besides <com:TContent>, we also see another new tag <%@ %> in the above, which is called template control tag. It contains name-value pairs which are used to initialize the corresponding properties for the template owner, namely, the Contact page.

By setting MasterClass property as Application.layouts.MainLayout, we instruct the Contact page to use MainLayout as its master. Here, we are using the namespace format to refer to the MainLayout class.

Info: Namespace format is widely used in PRADO programming. It is used together with path aliases. PRADO defines two path aliases: System refers to the framework directory of the PRADO installation, and Application refers to the protected directory. The namespace Application.layouts.MainLayout can thus be translated as protected/layouts/MainLayout which is exactly the file name (without the extension .php) for the MainLayout class.

Alternative Ways of Specifying Master

There are several additional ways to specify the master class for a page.

We can specify master in code like the following to enable dynamic change of layout:

<?php
class Contact extends TPage
{
    public function onPreInit($param)
    {
        parent::onPreInit($param);
        $this->MasterClass='Path.To.NewLayout';
    }

    // ...
}

In the above, we specify MasterClass in the onPreInit() method which is inherited from TPage. The method is invoked by PRADO right after the page instance is created. We thus can dynamically determine the layout to use when the page is requested. For example, when the page is requested by a registered user we use layout A, and layout B is used if a guest user is requesting the page.

We can also specify master in application configuration or page configuration. The following shows the updated application configuration for our blog system:

<?xml version="1.0" encoding="utf-8"?>
<application id="blog" mode="Debug">
  <!-- configuration for available services -->
  <services>
    <service id="page" class="TPageService" DefaultPage="Home">
      <!-- initial properties set for all pages -->
      <pages MasterClass="Application.layouts.MainLayout" />
    </service>
  </services>
</application>

By doing so, we save the trouble of specifying master in every page template. If we decide to use a different master for the pages, we only need to change the application configuration. For this reason, in our blog system we will use this approach to specify master.

Info: There is an order determining which master is acutally applied when it is specified in multiple places. In particular, onPreInit() takes precedence over page template over application/page configuration. Therefore, if we specify MainLayout in the application/page configuration and we specify SpecialLayout in Contact.page, the effective master would be the latter.