Home

Views and Layouts

Template basics

As we have seen in the previous chapter, every controller will render a view. A view is actually a set of 2 HTML template files: the layout template and the view template. The layout template is a master template in which the view template is rendered:

A simple layout template looks like this:

<html>
<head><title>Page Title</title></head>
<body>
<div class="Content">
{{@View}}
</div>
<div class="Footer">&copy; 2007 Me</div>
</body>

The {{ @View }} macro is replaced by the "content" of the view template. By "content" we mean the portion between <body> and </body>.

This is a simple view template:

<html>
<head><title>View Title</title></head>
<body>
Hello everyone!
</body>

When rendered, the final HTML will look like this:

<html>
<head><title>Page Title</title></head>
<body>
<div class="Content">
Hello everyone!
</div>
<div class="Footer">&copy; 2007 Me</div>
</body>

Templates can contain the following:

  • Variables (expressions)
  • Loops (foreach ... endfor)
  • Conditionals (if ... else ... endif)
  • Control placeholders (discussed in the section about Forms)
  • Includes
  • Components
  • Translations
  • Macros

If you want to specify e.g. scripts or tags between the <head>-tags to include in the final rendered view you can use the following syntax:

<html>
<head>
    <title>View Title</title>
<!-- #STARTCOPY# -->
    <meta name="keywords" content="" />
<!-- #ENDCOPY# -->
</head>
<body>
Hello everyone!
</body>

This can be usefull to include small parts of JavaScript or meta tags that are specific for a view.

Variables (expressions)

Templates are fed with data by the page controller using the ViewData[] collection. Variables from the ViewData[] collection can be used in templates by using the following syntax:

{{expression}}

'expression' is a valid .NET (C#) expression containing one or more variables from the ViewData[] collection.

For example, suppose the ViewData contains the following variables:

ViewData["Age"] = 37;
ViewData["Today"] = new DateTime(2007,8,29);

The following are valid expressions:

  • {{Age}} renders as 37
  • {{Age+1}} renders as 38
  • {{Today.Month}} renders as 8
  • {{Today.AddYears(-Age).ToString("MM/dd/yyyy")}} renders as 08/29/1970

As you can see, it is allowed to call methods on objects, but it is not possible to call methods from just any (static) .NET object. This means that the following is not possible:

{{Math.Min(Age,40)}} will throw an exception because the "Math" class is unknown (it was not added to the ViewData collection)

It is possible to explicitly allow this by adding classes and/or methods to the ViewData collection, which will be discussed later in this section.

Formatting

If you want to format the contents of a variable (or the result of an expression), you can specify a standard .NET format expression inside the tag, separated by a back-quote (`):

{{Expresssion`FormatString}}

For example:

  • {{Today ` dd/MM/yyyy}}
  • {{Price`0.00}}

Loops (foreach)

You can repeat a section of HTML by wrapping it in a foreach...endfor section. The foreach directive is very similar to the C# foreach statement:

<!--{{foreach variableName in collectionExpression}}-->
 ...
<!--{{endfor}}-->

The collectionExpression should be any expression or variable that returns a collection implementing the IEnumerable interface. This includes arrays, ArrayList, List<> collections, results from LINQ expressions, etc.

"variableName" can be used inside the loop to reference the current item in the collection. For example:

<!--{{ foreach employee in employees }}-->
 <tr><td>{{ employee.Name }}</td><td>{{ employee.Age }}</td></tr>
<!--{{ endfor }}-->

For Loop

You can emulate a For Loop using the following syntax:

// A for loop in C#
(for i = 0; i <= SomeValue; i++)
{
 ...
}

Can be implemented in a view like this:

<!--{{ foreach i in [0...SomeValue] }}-->
 ...
<!--{{ endfor }}-->

Pseudo-variables inside loops

Inside a foreach loop, you can access a few helpful pseudo-variables:

variable nameReplaced by
iteratorName@rowThe current row number (starting from 1)
iteratorName@oddTrue for odd rows
iteratorName@evenTrue for even rows
iteratorName@oddeven"odd" for odd rows, "even" for even rows
iteratorName@OddEven"Odd" for odd rows, "Even" for even rows
iteratorName@ODDEVEN"ODD" for odd rows, "EVEN" for evan rows

Examples:

<!--{{ foreach employee in employees }}-->
 <tr class="{{ employee@OddEven }}">
   <td>{{ employee@row }}</td><td>{{ employee.Name }}</td><td>{{ employee.Age }}</td>
 </tr>
<!--{{ endfor }}-->

or (same result, but implemented differently)

<!--{{foreach employee in employees}}-->
 <tr class="{{employee@odd ? "Odd":"Even"}}">
   <td>{{employee@row}}</td><td>{{employee.Name}}</td><td>{{employee.Age}}</td>
 </tr>
<!--{{endfor}}-->

Conditionals (if ... elseif ... else ... endif)

A section of HTML can be shown conditionally by using the <!--{{if}}-->...<!--{{endif}--> tags. These tags have to be wrapped inside a HTML comment (just like loop directives):

<!--{{if condition}}-->
...
<!--{{elseif condition}}-->
...
<!--{{else}}-->
...
<!--{{endif}}--> 

The condition can be any .NET expression. The expression does not have to return a boolean value. Other data types will be evaluated like this:

  • string expressions: evaluates to TRUE if the string is not null and length > 0. Other cases (null or zero length) evaluate as FALSE
  • numeric expressions: evaluates to TRUE for values not equal to 0 (zero).
  • collections (enumerable objects): evaluates to TRUE if the collection contains elements, otherwise FALSE
  • objects: evaluates to FALSE if the object reference is null, otherwise TRUE

The {{else}} and {{elseif}} directives are optional.

Including other templates

You can include other templates in two ways:

Rendered

{{ render templateName , @LocalVar1=..., @LocalVar2=... }}
or
<!--{{ render templateName , @LocalVar1=..., @LocalVar2=... }}-->

This will include another template and render it using the normal template parser. All ViewData variables are available in the called template, but not temporary variables like the name of the iterator in a {{foreach}} loop. If you want to pass these variables to the sub-template, you should add local variables when calling the template.

"As is"

It's also possible to include a template without running the the template renderer. The template will be rendered "as is". Only the body part will be included (the part between <body> and </body>):

{{ INCLUDE templateName }}
or
<!--{{ INCLUDE templateName }}-->

Template name

The name of the template should be a legal C# expression returning a string, so you can use variables and string literals (or any expression you like),

Examples:

{{ render "subTemplate.inc" }}
{{ render "group/" + UserGroup + "/index.htm" }}
{{ render "breadCrumb.htm", @Crumbs = crumbList }}

The template name can contain path information, but it should be relative to the directory of the current template. It should not be an absolute path. Parent directory ("..") is supported though. The filename should include the extension (this allows you to choose a different extension for sub-templates)

Using temporary variables

It's possible to assign values to temporary variables inside a template. You simply create an assignment expression to create a variable. From that point on the variable will be available in your template.

For example, to keep a running total in a table:

<!--{{ runningTotal = 0 }}-->
<table>
<!--{{ foreach item in Items }}-->
   <!--{{ runningTotal = runningTotal + item.Price }}-->
   <tr><td>{{item.Name}}</td><td>{{item.Price}}</td><td>{{runningTotal}}</td></tr>
<!--{{ endfor }}-->
</table>

Note the use of the "commented" expression tag. This prevents the result of the assignment being rendered, because in C# the assignment operator returns the result of the assignment.

Built-in variables

There are a few built-in variables you can use in your templates:

  • {{@Url}} is a reference to the current URL. It should be used to post back form data (by specifying it in the action attribute of the form tag)
  • {{@Title}} is the title of the view template (the part between <title> and </title>). It can only be used in the layout template

The {{@Url}} variable is actually an object representing the current URL which can be manipulated by a number of "chainable" methods:

  • Add(parameterName,value): adds a parameter to the URL
  • Replace(parameterName,value): replaces the specified parameter with the new value. If the parameter does not yet exist, it will be added
  • Remove(parameterName): removes the specified parameter from the URL
  • RemoveAll(): removes all parameters from the URL

These method calls can be chained together. For example, if {{@Url}} contains "http://www.vicimvcdemo.com/test.aspx?id=5&action=navigate", you can do this:

{{ @Url.Replace("id","4").Remove("action").Add("name","test") }}

The resulting URL will be: "http://www.vicimvcdemo.com/test.aspx?id=4&name=test"

Using .NET objects, classes and methods in view templates

It is possible to use .NET classes and objects in view templates. By default, none of the .NET classes can be used. You can enable this by adding classes and/or objects to the ViewData[] colection.

Adding objects

Adding an object to ViewData is very simple. Just assign it like you would any other variable:

ViewData["Context"] = currentContext; // currentContext is an instance of an application specific class

Example template:

The current user is {{Context.User.Name}}

Adding classes/types

To add a .NET class to ViewData, you can use the ContextFactory factory class (which is part of Vici Parser, included with Vici MVC):

ViewData.AddType("DateTime", typeof(DateTime));

Example template:

Current time is {{ DateTime.Now.ToString("dd/MM/yyyy") }}

<!--{{ if DateTime.Now > new DateTime(2008,2,14) }}-->
 You're too late, you missed valentine's day...
<!--{{ endif }}-->

Note the call to the DateTime constructor. When a type is available in your view template, you can create new objects of this type in expressions

Adding methods

You can also create your own "global" methods that you can call from the view template.

Methods can be defined using delegates or by the ContextFactory class:

ViewData["square"] = delegate(double x) { return x*x; };
ViewData["max"] = ContextFactory.CreateFunction(typeof(Math), "Max");

Example template:

The square of 6 is {{ square(6.0) }} <br/>
The highest of the numbers 5 and 9 is {{ max(5,9) }}

Relative path references

To support HTML design software properly, you are allowed to use relative references to images and other files from your templates. Vici MVC will automatically translate the references to the correct path.

For example, let's assume your templates are located in "/templates" and your images in "/images". In your template file, you can reference an image like this:

<img src="../images/image.gif" />

If this template is used in the URL "http://www.vicimvcdemo.com/public/products/list", the image reference will be changed by Vici MVC to:

<img src="../../images/image.gif" />

Or, if you use the same template in "http://www.vicimvcdemo.com/welcome", the image reference will be:

<img src="images/image.gif" />

To reference an absolute path (relative to the web application root), you should use the standard ASP.NET root folder token: "~/", for example:

<img src="~/images/image.gif" />

This is not supported by most HTML design software, but if you write HTML by hand, this may the preferred way of referencing other resources.