Templates: Flow Control (v 1.x)
Conditions
Syntax
Begin or continue conditional statement:
( 'if' | 'elif' ) [ 'not' ] [ 'empty' ] ( VARIABLE | 'rtl' ) ( 'if' | 'elif' ) '(' any-c++-expression ')'
Else block:
'else'
Each if/else block should be ended with end
statement. For example:
<% if not empty username %> <h1>Hello <% username %></h1> <% else %> <h1>Hello Visitor</h1> <% end %> <% if ( content.n % 2==0 ) %> <% n %> is odd. <% elif ( content.n % 3 ==0) % > <% n %> can be divided by 3 without reminder. <% end %>
Description
This is ordinary if
/else if
statement. You can specify following conditions:
- By specifying variable or property you check whether it is true.
- By specifying
empty
keyword you may check if specific STL container is empty by calling its memberempty()
. - You can negate the result using
not
- You can also check whether current locale referred to Right-To-Left language like Hebrew, Arabic or Persian by specifying
rtl
keyword.
This condition actually checks in dictionary if string "LTR" is translated as "RTL". - You can specify arbitrary C++ conditional expression withing round brackets
()
. Note: Do not forget to refer to content variables usingcontent.
prefix.
Foreach block
Syntax
Major loop:
'foreach' NAME ['as' IDENTIFIER ] [ 'rowid' IDENTIFIER [ 'from' NUMBER ] ] [ 'reverse' ] 'in' VARIABLE
In case of empty collection:
'empty'
Specify central body:
'item' 'separator'
For example:
<% foreach student in students %> <ul> <% item %> <li><% student.id %>, <% student.name %></li> <% end %> </ul> <% empty %> <h2>No students</h2> <% end %>
You can specify delimiter for elements between separator
and item
block:
<% foreach student in students %> <% separator %>, <% item %><% student.name %><% end %> <% end %>
Would generate a list like: "Ron, John, Moshe"
Description
foreach
loop creates a for loop that iterates any STL collection. The given name is a reference to
the type returned by iterator. For example, if students is std::list<student_t>
then student
in above
example is defined as student_t &student
.
reverse
- orders to use reverse iterator (rbegin,rend)rowid
- defines a special integer index that can be used for enumerating rows and optionalfrom
defines initial index value (default 0)
For example
<% foreach line rowid num from 1 in code %> <% item %><%= num %>:<%=line%><% end %> <% end %>
Note: reverse
and rowid
are supported starting from CppCMS 0.99.10
Type detection
Template system uses one of following method for automatic type detection:
- C++0x
auto
- C++0x
decltype
- GCC's style
typeof
or__typeof__
At least one of them should be supported by the compiler in order to provide correct template generation without
using as
keyword.
But some compilers, most noticeable is MSVC8, MSVC9 still not support automatic type detection, thus, specific iterator type should be explicitly specified.
For example:
<% foreach student as students_type::iterator in students %>
Would give you correct solution for MSVC compiler and student
would a variable of type students_type::iterator::value_type
Body
empty
statement is equivalent to "else" so:
<% foreach a in b %> <ul> <% item %><% a %><% end %> </ul> <% empty %> nothing <% end %>
Is generated into code like:
if(!content.b.empty()) { out()<<"<ul>"; for(auto a_it=content.b.begin();a_it!=content.b.end();++a_it) { auto &a=*a_it; out()<<a; } out()<<"</ul>"; }else{ out()<<"nothing"; }
You must provide <% item %>...<% end %>
block for any foreach
statement.
If separator
is used it should be added between separator
and item
blocks.
Custom form rendering
CppCMS forms system provides several options for rendering form widgets, but sometimes custom rendering
may be needed. In this case, foreach
can be used to iterate over cppcms::form
as over ordinary collation.
For example, rendering with "new line" separator:
<% foreach w in form %> <% separator %><br/><% item %> <% if w.has_message() %><% w.message() %>:<% end %> <% form input w %> <% if not w.valid() %>:<% w.error_message() %><% end %> <% end %> <% end %>
Caching Elements
Syntax
The cached block begins with cache statement
'cache' ( VARIABLE | STRING ) [ 'for' NUMBER ] ['on' 'miss' VARIABLE() ] [ 'no' 'triggers' ] [ 'no' 'recording' ]
and ends with end
statement.
For example
<% cache "sidebar" for 10 %> ... <% end cache %>
Description
This block allows to cache parts of rendered
templates. The first parameter ( VARIABLE | STRING )
is mandatory and defines the key that is used to
retrieve the frame from the cache. It should be
either constant predefined string or some variable
stored in the context.
for NUMBER
- defines the number of seconds this block is stored.on miss VARIABLE()
defines the callback function that should be called if cache miss occurs. It allows for example to populate the context with some more data from the database.no triggers
- says that the cache key should not be added as trigger to the general cache. By default the key is added to the current triggers set, ifno triggers
defined it does not happen.For example:
<% cache "foo" %> <% cache "bar" %> <% end %> <% end %>
The
bar
trigger is added tofoo
and to all higher level object as dependent trigger. Thus callingrise("bar")
would invalidate "foo" frame.However if you write:
<% cache "foo" %> <% cache "bar" no triggers %> <% end %> <% end %>
Then rising "bar" would not invalidate "foo".
no recording
prevents from this object to record all triggers that where added during frame generation.For example:
<% cache "foo" on miss load_data() %> ... <% end %> void load_data(some_content &c) { c.foo = .. // load some data from S cache().add_trigger("my_data"); }
If
load_data()
is called it would add a trigger "my_data" to the cache and thus risingmy_data
would invalidate "foo".If
no recording
is provided<% cache "foo" on miss load_data() no recording %>
Then the rising
my_data
would not invalidate "foo".
Adding Cache Triggers
Strictly speaking this is not flow block but it
is strongly connected to the cache
block described
above.
trigger ( STRING | VARIABLE )
Says to add trigger to the current context effectively
calling cache().add_trigger(value)
For example:
<% cache "foo" %> ... <% trigger "bar" %> <% end cache %>
Now rising bar
as trigger would invalidate foo
object.
It is useful for some complex relations where you want to add triggers to cached frames explicitly.