ViewModels

A view model is an object that can be rendered in a view - a Razor template. The view model represents an entity such as a page or a paragraph and has been optimized for rendering in front end.

Since view models are objects with strongly typed properties you will be able to use intellisense in e.g. Visual Studio and easily find the values available to you (Figure 1.1)

Figure 1.1 Intellisense

Some of the key advantages to using ViewModels are:

  • Workflow / Intellisense
    Regular razor templates use a key/value collection where the view model templates are served a typed object with properties. The view model will make it possible to get full intellisense when working with templates in Visual Studio. You no longer have to rely as much on documentation or DwTemplateTags/@TemplateTags() to find out what information is available.
  • Feedback
    A regular razor templates will fail silently if you misspell a property, since it is a simple lookup like this: @GetValue(“DwPageName”). In contrast, the rendering engine will give you immediate feedback (an error message) if you misspell the name of a view model property.
  • Performance
    View model templates are easier to optimize, particularly regarding the parsing and rendering process. Therefore you should see some performance improvements when using view model templates, compared to other types of templates.

There are two different base ViewModels available to you:

  • PageViewModel
  • ParagraphViewModel

They inherit a number of other viewmodels, such as the CartViewModel used for rendering shopping cart information. Both of these base ViewModels are described in more detail below.

In order to use a view model the template must inherit from ViewModelTemplate and should specify a model that is valid in the context where the template is used, e.g. page or paragraph. The type of view model should be specified at the top of the template:

@inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel>

In this example, we are specifying the view model for a page view model template

You can optionally specify some information about your template which will be displayed in layout selectors in the administration.

@inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel> @Title("My view model template") @Description("Description of my view model template")

This will show up in the layout selector in the backend (Figure 2.3).

Figure 2.3 A view model template with a title and a description

Both view models (PageViewModel and ParagraphViewModel) work with the following standard template methods and features:

Property

Used in

Comments

@ContentPlaceholder()

Master templates

 

@MasterPageFile(“master.cshtml”)

Layout templates

 

@Include(…)

Everywhere

 

@IncludeFile(…)

Everywhere

 

@GetPageIdByNavigationTag("TagName")

Everywhere

Returns the first area page with the specified navigationtag. If no page is found, 0 is returned

@RenderItem(…)

Everywhere

 

@RenderItemList(…)

Everywhere

 

@RenderItemCreationForm(…)

Everywhere

 

@RenderNavigation(…)

Everywhere

Must start with a @

@SnippetStart(…)

@SnippetEnd(…)

@RenderSnippet(…)

Everywhere

 

@Translate(…)

Everywhere

 

When rendering a page layout you must use the PageViewModel:

@inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel>

Content placeholders are defined in the following manner:

@* A content placeholder *@ @Model.Placeholder("footer") @*A content placeholder with a title and a description *@ @Model.Placeholder("main", "Main content", "default:true;sort:1")

A placeholder can take the following parameters:

Parameter

Required

Comments

Id

Yes

Unique id of a layout container.

Title

No

Friendly name of the layout container which is displayed in the administration (defaults to the value of Id).

Settings

No

Additional settings that specifies how the content should be rendered.

 

The placeholder settings available to you are:

Property

Description

Default value

Possible values

Default

Whether new content should be placed in this container by default (page edit).

False

True or False

Sort

Controls the sorting in administration (page edit).

0

1-99

Template

The template that should be used for items in this container.

 

Any valid paragraph template. Must be placed in

/Templates/Paragraph,

/Templates/Designs/Paragraph or

/Templates/Designs/DesignName/Paragraph

A very simple page layout template could look like this:

@inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel> @Title("My view model template") @Description("Description of my view model template") <!DOCTYPE html> <html> <head> <title>@Model.Title</title> <meta name="description" content="@Model.Description" /> <meta name="keywords" content="@Model.Keywords" /> </head> <body> <h1>@Model.Name</h1> <div> @Model.Placeholder("main") </div> </body> </html>

You can explore the PageViewModel further in the API documentation - or simply browse the example file below:

RAZOR
@inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel> @Title("My view model template") @Description("Description of my view model template") <!DOCTYPE html> <html> <head> <title>@Model.Title</title> <meta name="description" content="@Model.Description" /> <meta name="keywords" content="@Model.Keywords" /> </head> <body> <h1>@Model.Name</h1> <div> <table> <tr> <th>Model.ID</th> <td>@Model.ID</td> </tr> <tr> <th>Model.Name</th> <td>@Model.Name</td> </tr> <tr> <th>Pageview.ID</th> <td>@Pageview.ID</td> </tr> </table> </div> <h1>Path</h1> <p>Used e.g. for rendering a breadcrumb navigation:</p> <ol> @foreach (var path in Model.Path) { if (Model.Path.IndexOf(path) == 0) { <a href='/Default.aspx?ID=@path.ID'>@path.Name</a> } else { string divider = ">"; @divider <a href='/Default.aspx?ID=@path.ID'>@path.Name</a> } } </ol> <h2>TopPage</h2> <p>Returns the ID and name of the top page for this page.</p> <table> <tr> <th>Model.TopPage.ID</th> <td>@Model.TopPage.ID</td> </tr> <tr> <th>Model.TopPage.Name</th> <td>@Model.TopPage.Name</td> </tr> </table> <h2>Placeholders</h2> <p>Defining content placeholders:</p> <table> <tr> <th>Model.Placeholder("left", "Left content")</th> <td>@Model.Placeholder("left", "Left content")</td> </tr> <tr> <th>Model.Placeholder("Main", "Main content", "default:true;sort:1")</th> <td>@Model.Placeholder("Main", "Main content", "default:true;sort:1")</td> </tr> <tr> <th>Model.Placeholder("footer", "Footer")</th> <td>@Model.Placeholder("footer", "Footer")</td> </tr> </table> <h2>Includes</h2> <table> <tr> <th>Include("/includes/Part1.cshtml")</th> <td>@Include("/includes/Part1.cshtml")</td> </tr> <tr> <th>IncludeFile("/includes/Part2.cshtml")</th> <td>@IncludeFile("/includes/Part2.cshtml")</td> </tr> </table> <h2>Snippets</h2> <p>Snippets are used for rendering markup from any template in any other template.</p> <div style="color:green"> @RenderSnippet("thesnippet") </div> <div style="color:red"> @SnippetStart("thesnippet") <p> This text should be green</p> @SnippetEnd("thesnippet") </div> <h2>Page item</h2> @if (Model.Item != null) { <p>ItemId: @Model.ItemId</p> <p>ItemType: @Model.ItemType</p> <table> @foreach (var field in Model.Item.Fields) { <tr> <th>@field.SystemName</th> <td>@field.GetValue()</td> </tr> } </table> } else { <p>This is not an item-based page</p> } <h2>Page property item</h2> @if (Model.PropertyItem != null) { <table> <tr> <th>Model.PropertyItemId</th> <td>@Model.PropertyItemId</td> </tr> <tr> <th>Model.PropertyItemType</th> <td>@Model.PropertyItemType</td> </tr> </table> <table> @foreach (var field in Model.PropertyItem.Fields) { <tr> <th>@field.SystemName</th> <td>@field.GetValue()</td> </tr> } </table> } else { <p>This page does not have any item-based page properties</p> } <h2>Area item</h2> <p>Model.Area.ItemId: @Model.Area.ItemId</p> <p>Model.Area.ItemType: @Model.Area.ItemType</p> @if (Model.Area.Item != null) { <table> @foreach (var field in Model.Area.Item.Fields) { <tr> <th>@field.SystemName</th> <td>@field.GetValue()</td> </tr> } </table> } else { <p>This website does not use item-based website properties</p> } <h2> Items </h2> <table> <tr> <th>RenderItem(...)</th> <td>@RenderItem(new { ItemType = "MyCustomItem", SourceItemEntry = 2, ItemFields = "*", ListPageSize = 10, ListSourceType = "SelfArea", DetailsTemplate = "ItemPublisher/Details/Details.html" })</td> </tr> <tr> <th>RenderItemList(...)</th> <td>@RenderItemList(new { ItemType = "MyCustomItem", ItemFieldsList = "*", ListPageSize = 10, ListSourceType = "SelfArea", ListTemplate = "ItemPublisher/List/List.html" })</td> </tr> <tr> <th>RenderItemCreationForm(...)</th> <td>@RenderItemCreationForm(new { ItemType = "MyCustomItem" })</td> </tr> </table> <h2>Page languages</h2> <ul> @foreach (var lang in Model.Languages) { <li><a href="/Default.aspx?ID=@lang.Page.ID">@lang.Page.Name</a> (@lang.Culture)</li> } </ul> <h2>Website (Area) languages</h2> <ul> @foreach (var lang in Model.Area.Languages) { <li><a href="/Default.aspx?ID=@lang.FirstActivePage.ID">@lang.Name</a> (@lang.Culture)</li> } </ul> <h2>User</h2> @if (Model.CurrentUser == null) { <table> <tr> <th>Model.CurrentUser.Address</th> <td>@Model.CurrentUser.Address</td> </tr> <tr> <th>Model.CurrentUser.Address2</th> <td>@Model.CurrentUser.Address2</td> </tr> <tr> <th>Model.CurrentUser.City</th> <td>@Model.CurrentUser.City</td> </tr> <tr> <th>Model.CurrentUser.Company</th> <td>@Model.CurrentUser.Company</td> </tr> <tr> <th>Model.CurrentUser.Country</th> <td>@Model.CurrentUser.Country</td> </tr> <tr> <th>Model.CurrentUser.CustomerNumber</th> <td>@Model.CurrentUser.CustomerNumber</td> </tr> <tr> <th>Model.CurrentUser.Department</th> <td>@Model.CurrentUser.Department</td> </tr> <tr> <th>Model.CurrentUser.Email</th> <td>@Model.CurrentUser.Email</td> </tr> <tr> <th>Model.CurrentUser.FirstName</th> <td>@Model.CurrentUser.FirstName</td> </tr> <tr> <th>Model.CurrentUser.HouseNumber</th> <td>@Model.CurrentUser.HouseNumber</td> </tr> <tr> <th>Model.CurrentUser.ID</th> <td>@Model.CurrentUser.ID</td> </tr> <tr> <th>Model.CurrentUser.Image</th> <td>@Model.CurrentUser.Image</td> </tr> <tr> <th>Model.CurrentUser.JobTitle</th> <td>@Model.CurrentUser.JobTitle</td> </tr> <tr> <th>Model.CurrentUser.LastName</th> <td>@Model.CurrentUser.LastName</td> </tr> <tr> <th>Model.CurrentUser.MiddleName</th> <td>@Model.CurrentUser.MiddleName</td> </tr> <tr> <th>Model.CurrentUser.Name</th> <td>@Model.CurrentUser.Name</td> </tr> <tr> <th>Model.CurrentUser.Phone</th> <td>@Model.CurrentUser.Phone</td> </tr> <tr> <th>Model.CurrentUser.PhoneBusiness</th> <td>@Model.CurrentUser.PhoneBusiness</td> </tr> <tr> <th>Model.CurrentUser.PhoneMobile</th> <td>@Model.CurrentUser.PhoneMobile</td> </tr> <tr> <th>Model.CurrentUser.PhonePrivate</th> <td>@Model.CurrentUser.PhonePrivate</td> </tr> <tr> <th>Model.CurrentUser.State</th> <td>@Model.CurrentUser.State</td> </tr> <tr> <th>Model.CurrentUser.Title</th> <td>@Model.CurrentUser.Title</td> </tr> <tr> <th>Model.CurrentUser.UserName</th> <td>@Model.CurrentUser.UserName</td> </tr> <tr> <th>Model.CurrentUser.Zip</th> <td>@Model.CurrentUser.Zip</td> </tr> </table> } else { <p>No user is currently logged in</p> } <h1>Cart model</h1> <table> <tr> <th>Model.Cart.ID</th> <td>@Model.Cart.ID</td> </tr> <tr> <th>Model.Cart.IsEmpty</th> <td>@Model.Cart.IsEmpty</td> </tr> </table> @if (@Model.Cart.IsEmpty == false) { <table> <tr> <th>Model.Cart.ProductsCount</th> <td>@Model.Cart.ProductsCount</td> <td></td> </tr> <tr> <th>Model.Cart.OrderlinesCount</th> <td>@Model.Cart.OrderlinesCount</td> <td></td> </tr> <tr> <th>Model.Cart.TotalDiscountWithoutVAT</th> <td>@Model.Cart.TotalDiscount.PriceWithoutVat.Formatted</td> <td>@Model.Cart.TotalDiscount.PriceWithoutVat.Value</td> </tr> <tr> <th>Model.Cart.TotalDiscountWithVAT</th> <td>@Model.Cart.TotalDiscount.PriceWithVat.Formatted</td> <td>@Model.Cart.TotalDiscount.PriceWithVat.Value</td> </tr> <tr> <th>Model.Cart.TotalPrice w/o Discounts without VAT</th> <td>@Model.Cart.TotalPriceWithoutDiscounts.PriceWithoutVat.Formatted</td> <td>@Model.Cart.TotalPriceWithoutDiscounts.PriceWithoutVat.Value</td> </tr> <tr> <th>Model.Cart.TotalPrice w/o Discounts with VAT</th> <td>@Model.Cart.TotalPriceWithoutDiscounts.PriceWithVat.Formatted</td> <td>@Model.Cart.TotalPriceWithoutDiscounts.PriceWithVat.Value</td> </tr> <tr> <th>Model.Cart.TotalPrice with VAT</th> <td>@Model.Cart.TotalPrice.PriceWithVat.Formatted</td> <td>@Model.Cart.TotalPrice.PriceWithVat.Value</td> </tr> <tr> <th>Model.Cart.TotalPrice without VAT</th> <td>@Model.Cart.TotalPrice.PriceWithoutVat.Formatted</td> <td>@Model.Cart.TotalPrice.PriceWithoutVat.Value</td> </tr> </table> } else { <p>This cart is empty</p> } </body> </html>

When rendering a paragraph you must use the ParagraphViewModel. 

@inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel>

This view model gives you access to all basic properties of the current paragraph – ParagraphText, ParagraphImage and ParagraphModule:

<img src="@Model.Image" alt="@Model.ImageAlt" /> <div> @Model.Text </div> <div> @Model.GetModuleOutput() </div>

The module output is a string value.

A very simple paragraph template could look like this:

@inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> <div> <h1>@Model.Header</h1> <div> @Model.Text </div> <div> <img src="@Model.Image" alt="@Model.ImageAlt" /> </div> <div> @Model.GetModuleOutput() </div> </div>

Some view models will expose an item property which will give you an instance of ItemViewModel, e.g.

  • PageViewModel.Item (Item based pages)
  • PageViewModel.PropertyItem (Page Properties items)
  • PageViewModel.Area.Item (Website Properties items)
  • ParagraphViewModel.Item (Item based paragraphs)

The ItemViewModel.Fields gives you access to a collection of item fields and some methods for retrieving the field values. The following methods are available:

Method

Return type

Use with item field type

GetBoolean

Boolean

Check box

GetDateTime

DateTime

Date, Date and time

GetDouble

Double

Decimal number

GetInt32

Int32

Integer number

GetInt64

Int64

 

GetString

String

Text, Rich text, Color

GetFile

FileViewModel

File

GetFiles

IList<FileViewModel>

Folder

GetGeolocation

GeolocationViewModel

Geolocation

GetItem

ItemViewModel

Item type, Link to item

GetItems

IList<ItemViewModel>

Item relation list

GetUsers

IList<UserViewModel>

User

GetValue

object

Any

Examples:

@* Rendering all fields of an item *@ <div> @if (Model.Item != null) { <table> @foreach (var field in Model.Item.Fields) { <tr> <th>@field.Name</th> <td>@field.GetValue()</td> </tr> } </table> } </div> @* Rendering a specific field of an item *@ <div> @{ var myField = Model.Item.GetField("MyField"); if (myField != null) { <p>@myField.Name</p> <p>@myField.GetValue()</p> } } </div> @* Rendering a specific field value of an item *@ <input type="checkbox" checked="@Model.Item.GetBoolean("Checkbox")" />

GetValue() returns the field value as an untyped object.

You can use GetValue if you just want to render the value and don’t want to change the format in any way.

However, if you want to format the value, e.g. render a date in a specific format, then you would need to get the typed value and then apply your formatting.

Note that if the field value is a complex type, like the FileViewModel you get from a file field, then the output from GetValue will just render as the type name, e.g. “Dynamicweb.Frontend.FileViewModel”.

You can either use one of the helper methods for converting the value to the right type (recommended) or you can use GetValue and then cast the value yourself.

Example: Rendering a Date field using GetValue or GetDateTime

Method

Example

Result

GetValue

<div>@Model.Item.GetValue("Date")</div>

<div>01-11-2016 14:35:09</div>

GetDateTime

<div>@Model.Item.GetDateTime("Date").ToString("s")</div>

<div>2016-11-01T14:35:09</div>

GetDateTime  @Model.Item.GetDateTime("Date").Year <div>2016</div>

 

Example: Rendering a File field using GetValue or GetFile

Method

Example

Result

GetValue

<div>@Model.Item.GetValue("File")</div>

<div>Dynamicweb.Frontend.FileViewModel</div>

GetFile

<div>@Model.Item.GetFile("File").Path</div>

<div>/Files/Images/logo.png</div>

You can use the GetField method on ItemViewModel in order to get additional information about a specific field.

Example: Rendering information about a specific field:

<div> @{ var dateField = Model.Item.GetField("Date"); if (dateField != null) { <p>@dateField.Name</p> <p>@dateField.SystemName</p> <p>@dateField.GetDateTime()</p> } } </div>

You use the GetItem() method if you want to retrieve values from an item with a field of the type Item type. For example, you have a shared item type called Font which contains all sort of font settings. This itemt ype is used in your website settings – so to retrieve the value from the FontFamily field you do the following:

Model.Area.Item.GetItem("Font").GetString("FontFamily”);

You use the GetFile() method whenever you’ve used an item field of the type File – this is the only way to retrieve the path to the file.

Item.GetFile("Image").Path

GetFile() can return the file extension, the name, and the path to the file selected – if you use any other method, you will fail silently.

You use the GetItems() method if you have used an item field of the type Item relation list – this is the only way to retrieve values from item relation lists.

@foreach (var i in Model.Item.GetItems(“GalleryImages”)) { i.GetString(“Image”); }