Views Inheritance
Introduction
CppCMS views are actually classes, and thus you can derive one view from other, override different templates and so on.
This gives a developer a very powerful tool to design HTML templates.
For example, we create a master
view that includes
basic CSS and HTML style, and defines placeholders for different types of content that should be included. Then we derive different views that are responsible for the actual content.
master / | \ news page intro
Of course the content for the derived views should be also be derived from the parent content classes.
Content
Let's create our content.h
for the sample hierarchy above.
First we define our master
content in the content
namespace.
struct master : public cppcms::base_content { std::string title; };
It would include shared information like our web site title.
Then we create a news
content derived from the master:
struct news : public master { std::list<std::string> news_list; };
It would add some set of "news" messages we want to display.
We do the same for page
content with, of course, different data:
struct page : public master { std::string page_title, page_content; };
For the "introduction" page
we will use the same master
content, as we don't need more data.
Views
Master
Let's show our master.tmpl
:
As usual we define the include and the skin name:
<% c++ #include "content.h" %> <% skin myskin %>
Then we define our master
view
<% view master uses content::master %>
We create some generic title that can be overridden.
<% template title() %><%= title %><% end %>
Define the real content we want to display
<% template page_content() %>Override Me<% end %>
And finally we define our main render
function
<% template render() %> <html> <head> <title><% include title() %></title> </head> <body> <h1><% include title() %></h1> <div id="content"> <% include page_content() %> </div> </body> </html> <% end template %>
Note:
- We use
title()
in several places - We put our placeholders in the locations we want, in our
case it is one placeholder
page_content()
Then we finish our master.tmpl
as usual
<% end view %> <% end skin %>
News
Then we create our news.tmpl
. As usual we start and end
it with
<% skin myskin %> ... <% end skin %>
Then we define our view:
<% view news uses content::news extends master %>
Note:
- It
extends master
- i.e. derived frommaster
view - It uses
content::news
. It works becausecontent::news
derived fromcontent::master
.
Then we override the title:
<% template title() %> <% include master::title() %> :: News <% end %>
And define our central content:
<% template page_content() %> <% foreach message in news_list %> <ul> <% item %> <li><%= message %></li> <% end %> </ul> <% end foreach %> <% end template %>
Page
We do the same for page.tmpl
<% skin myskin %> <% view page uses content::page extends master %> <% template title() %> <% include master::title() %> :: <%= page_title %> <% end %> <% template page_content() %> <%= page_content | raw %> <% end template %> <% end view %> <% end skin %>
Introduction
Now we create our final intro
view. However in its case,
we do not add any new content as we don't need it. Therefore we reuse content::master
.
<% skin myskin %> <% view intro uses content::master extends master %> <% template page_content() %> <p><a href='<% url "/page" %>'>The Page</a></p> <p><a href='<% url "/news" %>'>The News</a></p> <% end template %> <% end view %> <% end skin %>
Note: we use <% url ... %>
tag from the templates the same way we used it from the application in this tutorial.
Controller
We create a usual mapping to intro
, news
and page
functions:
dispatcher().assign("",&myapp::intro,this); mapper().assign(""); dispatcher().assign("/news",&myapp::news,this); mapper().assign("news","/news"); dispatcher().assign("/page",&myapp::page,this); mapper().assign("page","/page"); mapper().root("/myapp");
Please note, because there is no direct HTML page
that renders master
content we do not create a mapping
for it. On the other hand we do want to setup the
shared master content for all links.
So we create a small function that initializes the content:
void ini(content::master &c) { c.title = "My Web Site"; }
Then we define our "pages":
void intro() { content::master c; ini(c); render("intro",c); } void page() { content::page c; ini(c); c.page_title = "About"; c.page_content = "<p>A page about this web site</p>"; render("page",c); } void news() { content::news c; ini(c); c.news_list.push_back("This is the latest message!"); c.news_list.push_back("This is the next message."); c.news_list.push_back("This is the last message!"); render("news",c); }
Notes:
- Each one of them renders the view it needs
- Each one of then uses their own content
- Each one of them calls shared
ini()
function to setup themaster
content for all.
Building
Please note, even thought each view has its own file
the order of compilation is important so the page.tmpl
would be compiled after master.tmpl
. So in our
Makefile we write:
cppcms_tmpl_cc master.tmpl page.tmpl news.tmpl intro.tmpl -o my_skin.cpp
It would not work if master.tmpl
were not the
first in the list.