devextremeaspnetmvc-401287-concepts-templates.md
This article explains how to implement and apply templates. Templates allow you to customize how the control parts (titles, cells, items, and so on) are rendered.
Use *Template() methods to define templates, for example:
DataGridColumnBuilder.CellTemplate - specifies a template for column cells in a DataGrid control.ListBuilder.ItemTemplate - specifies a template for items in a List control.PopupBuilder.ContentTemplate - specifies a template for a Popup control’s content.A template consists of Razor markup and ERB-style constructs (<% %>) that can use parameters. To define a template, use the @<text> block in a control’s *Template(RazorBlock templateContent) method.
Note
Razor VB : When you use the @<text> block:
enclose the control configuration with @Code/End Code;
end the control configuration with Render().
@(Html.DevExtreme().List()
.DataSource(DataSource)
.ItemTemplate(@<text>
<div><%- Name %></div>
</text>)
)
@Code
Html.DevExtreme().List() _
.DataSource(DataSource) _
.ItemTemplate(Sub()
@<text>
<div><%- Name %></div>
</text>
End Sub) _
.Render()
End Code
The List control is bound to the following data source:
object[] DataSource = new[] {
new { Name = "John" },
new { Name = "Jane" }
};
Dim DataSource = {
New With {.Name = "John"},
New With {.name = "Jane"}
}
You can also use @Html in templates, for example, to nest controls or access standard HTML helpers.
If a template is short and does not use Razor constructs (started with @), you can use a shorthand overload of a *Template method with a String argument:
@(Html.DevExtreme().List()
.DataSource(DataSource)
.ItemTemplate("<div><%- Name %></div>")
)
@(Html.DevExtreme().List() _
.DataSource(DataSource) _
.ItemTemplate("<div><%- Name %></div>")
)
You can define templates outside of a control declaration. This can be useful in the following cases:
There are two ways to declare external templates:
Extract the template markup to a partial Razor file.
Use named templates.
Template syntax allows you to embed JavaScript code within a pair of <% and %> delimiters. There are three types of tags:
HTML-encoded expression tags
Non-HTML-encoded expression tags
Execution tags
Note
ERB-style expressions are executed on a client. You cannot use server code in them.
Template parameters are the *Data or *Info objects that are specific for different templates and are documented in the client API reference. For example:
cellInfo object of a DataGrid column’s cellTemplate provides a predefined set of properties: value, text, columnIndex, and so on.itemData object of a List’s itemTemplate is a list item that comes from items or dataSource.The entire objects can be referenced in a template as obj. Properties of the *Data or *Info object are available directly in a template scope. These properties are called free variables.
The example below shows how to access the Name property.
@(Html.DevExtreme().List()
.DataSource(DataSource)
.ItemTemplate(@<text>
<%- Name %> <!-- Name as a free variable -->
<%- obj.Name %> <!-- Name as a property of obj -->
</text>)
)
@Code
Html.DevExtreme().List() _
.DataSource(DataSource) _
.ItemTemplate(Sub()
@<text>
<%- Name %>
<%- obj.Name %>
</text>
End Sub) _
.Render()
End Code
The List control is bound to the following data source:
object[] DataSource = new[] {
new { Name = "John" },
new { Name = "Jane" }
};
Dim DataSource = {
New With {.Name = "John"},
New With {.Name = "Jane"}
}
You can use obj and free variables in JavaScript code within a template. Refer to Bind a Nested Control to Template Parameters for more information.
You can define templates as JavaScript functions like you do in DevExtreme (refer to the Function section of the template article) and use them to:
Use *Template(new JS(...)) to define a template as a function.
In the example below, the List’s ItemTemplate is declared as the myList_itemTemplate function that renders items content and applies a custom style to the items using jQuery. The JavaScript function approach is used to access the itemIndex and itemElement parameters in the template. These parameters are not available if you use ERB-style constructs (refer to Access Template Parameters).
@(Html.DevExtreme().List()
.DataSource(DataSource)
.ItemTemplate(new JS("myList_itemTemplate"))
)
@(Html.DevExtreme().List() _
.DataSource(DataSource) _
.ItemTemplate(New JS("myList_itemTemplate")) _
)
<script>
function myList_itemTemplate(itemData, itemIndex, itemElement) {
itemElement
.addClass("my-custom-style")
.append(
$("<span>").text("Item index: " + itemIndex + ", Name: " + itemData.Name)
);
}
</script>
You can use DevExtreme controls within templates. For example, the following code nests the DataGrid control in the Popup control:
@(Html.DevExtreme().Popup()
// ...
// Specifies the contents of the Popup control
.ContentTemplate(@<text>
@(Html.DevExtreme().DataGrid<Sale>()
.Columns(columns => {
columns.AddFor(m => m.Region);
columns.AddFor(m => m.City);
columns.AddFor(m => m.Amount);
columns.AddFor(m => m.Date);
})
)
</text>)
)
@Code
Html.DevExtreme().Popup() _
.ContentTemplate(Sub()
@<text>
@(Html.DevExtreme().DataGrid(Of Sale)() _
.DataSource(Function(d) d.WebApi().Controller("GridData")) _
.Columns(Sub(columns)
columns.AddFor(Function(m) m.Region)
columns.AddFor(Function(m) m.City)
columns.AddFor(Function(m) m.Amount)
columns.AddFor(Function(m) m.Date)
End Sub)
)
</text>
End Sub) _
.Render()
End Code
The popup should look like this:
You can find this code in the Demo: Drill Down.
Note
@<text> tags, you cannot define a template within another template. Use external templates for high-level nesting.ItemTemplate or a CellTemplate template, ensure you specify a unique ID in the nested control’s ID option. Alternatively, do not specify this option. In this case, nested control instances get auto-generated IDs.A nested control’s configuration can depend on template parameters.
The example below defines a template for DataGrid cells. A cell contains a button whose configuration depends on the cellTemplate‘s value free variable.
value to the Text method, the Text(JS) overload is used.value to the handleGridButtonClick(cellValue) event handler, the short anonymous function is used. This function captures the value free variable and passes it to the event handler as an argument.Note
If a handler is short, you do not need to extract it to an external function - you can use a short inline function instead, for example:
.OnClick("function() { alert(value); }")
Refer to Handle Events and Define Callbacks for more information.
@(Html.DevExtreme().DataGrid()
// ...
.Columns(columns => {
columns.Add()
.DataField("Name")
.CellTemplate(@<text>
@(Html.DevExtreme().Button()
.Text(new JS("value"))
.OnClick("function() { handleGridButtonClick(value); }")
)
</text>);
})
)
@Code
Html.DevExtreme().DataGrid() _
.Columns(Sub(columns)
columns.Add() _
.DataField("Name") _
.CellTemplate(Sub()
@<text>
@(Html.DevExtreme().Button() _
.Text(New JS("value")) _
.OnClick("function() { handleGridButtonClick(value); }")
)
</text>
End Sub)
End Sub) _
.Render()
End Code
<script>
function handleGridButtonClick(cellValue) {
alert("Cell value:" + cellValue);
}
</script>
A common use-case of binding a nested control to template parameters is the Master-Detail Grid. In the following code, the detail section nests another DataGrid. The data.OrderID free variable is used in the LoadParams of its DataSource.
@(Html.DevExtreme().DataGrid()
.DataSource(d => d.WebApi().Controller("DataGridMasterDetailView").Key("ID"))
.Columns(columns => {
columns.Add().DataField("FirstName");
columns.Add().DataField("LastName");
// ...
})
// Configures the Master-Detail UI
.MasterDetail(md => md
.Enabled(true)
// Specifies the contents of the detail section
.Template(@<text>
@(Html.DevExtreme().DataGrid()
.DataSource(d => d.WebApi()
.Controller("DataGridMasterDetailView")
.LoadAction("TasksDetails")
// Use "data.ID" in LoadParams
.LoadParams(new { id = new JS("data.ID") })
)
)
</text>)
)
)
@Code
Html.DevExtreme().DataGrid() _
.DataSource(Function(d) d.WebApi().Controller("DataGridMasterDetailView").Key("ID")) _
.Columns(Sub(columns)
columns.Add().DataField("FirstName")
columns.Add().DataField("LastName")
End Sub) _
.MasterDetail(Sub(md) md _
.Enabled(True) _
.Template(Sub()
@<text>
@(Html.DevExtreme().DataGrid() _
.DataSource(Function(d) d.WebApi() _
.Controller("DataGridMasterDetailView") _
.LoadAction("TasksDetails") _
.LoadParams(New With { .id = New JS("data.ID") })
)
)
</text>
End Sub)
) _
.Render()
End Code
There are cases when template parameters should be passed through an intermediate control. For example, if you place a detail DataGrid in a TabPanel, this DataGrid has access to Tab template’s parameters, but does not see parameters of a master DataGrid’s detail template. To solve this situation, attach the tabExtras custom option to the Tab, then you can access it in the tab1Template template:
@(Html.DevExtreme().DataGrid()
.KeyExpr("ID")
.DataSource(MasterGridDataSource, key: "ID")
.MasterDetail(m => m
.Enabled(true)
.Template(@<text>
@(Html.DevExtreme().TabPanel()
.Items(items => {
items.Add()
.Title("Tab 1")
.Option("tabExtras", new {
masterKey = new JS("key")
})
.Template(new TemplateName("tab1Template"));
})
)
</text>)
)
)
@using (Html.DevExtreme().NamedTemplate("tab1Template")) {
<!-- Use tabExtras.masterKey to configure a detail grid -->
}
@Code
Html.DevExtreme().DataGrid() _
.KeyExpr("ID") _
.DataSource(MasterGridDataSource, key:= "ID") _
.MasterDetail(Sub(md) md _
.Enabled(True) _
.Template(Sub()
@<text>
@(Html.DevExtreme().TabPanel() _
.Items(Sub(items)
items.Add() _
.Title("Tab 1") _
.Option("tabExtras", New With {.masterKey = New JS("key")}) _
.Template(New TemplateName("tab1Template"))
End Sub)
)
</text>
End Sub)
) _
.Render()
End Code
@Using (Html.DevExtreme().NamedTemplate("tab1Template"))
' Use tabExtras.masterKey to configure a detail grid
End Using
The DataGrid control is bound to the following data source:
object[] MasterGridDataSource = new[] {
new { ID = 1, Name = "John" },
new { ID = 2, Name = "Jane" }
};
Dim MasterGridDataSource = {
New With {.ID = 1, .Name = "John"},
New With {.ID = 2, .Name = "Jane"}
}
Demo: Advanced Master-Detail View
You can use HTML helpers in templates as usual if they render static content. For instance, if you configure grid columns and a column’s cells should display the same link. The code below shows how to use the ActionLink HTML helper to render this link.
@(Html.DevExtreme().DataGrid()
.Columns(columns => {
//...
columns.Add().CellTemplate(@<text>
@Html.ActionLink("Link Text", "Details")
</text>);
})
)
@Code
Html.DevExtreme().DataGrid() _
.Columns(Sub(columns)
columns.Add().CellTemplate(Sub()
@<text>
@Html.ActionLink("Link Text", "Details")
</text>
End Sub)
End Sub) _
.Render()
End Code
If you need to use template parameters, use the following approach:
@(Html.DevExtreme().DataGrid()
.Columns(columns => {
columns.Add().CellTemplate(@<text>
@Html.Raw(
Html.ActionLink("Link Text", "Details", "ControllerName", new { id = "ID_PLACEHOLDER" }, null)
.ToString()
.Replace("ID_PLACEHOLDER", "<%- data.OrderID %>")
)
</text>);
})
)
@Code
Html.DevExtreme().DataGrid() _
.Columns(Sub(columns)
columns.Add().CellTemplate(Sub()
@<text>
@Html.Raw(
Html.ActionLink("Link Text", "Details", "ControllerName", New With {.id = "ID_PLACEHOLDER"}, Nothing) _
.ToString() _
.Replace("ID_PLACEHOLDER", "<%- data.OrderID %>")
)
</text>
End Sub)
End Sub) _
.Render()
End Code
This code demonstrates how to create a template if the links in the grid column depend on the OrderID. The ActionLink has a dynamic route value (id) the data.OrderID template parameter should provide. However, you should use a placeholder because data.OrderID cannot be passed to id (the ActionLink is generated on the server and data.OrderID receives a value only on the client).
The resulting expression:
<a href="/ControllerName/Details/"<%- data.OrderID %>">Link Text</a>
Note
You can use HTML elements instead of helpers. The examples below demonstrate this.
@(Html.DevExtreme().DataGrid()
.Columns(columns => {
columns.Add().CellTemplate(@<text>
<a href="@Url.Action("ActionName", "ControllerName")/<%- data.OrderID %>">Link Text</a>
</text>);
})
)
@Code
Html.DevExtreme().DataGrid() _
.Columns(Sub(columns)
columns.Add().CellTemplate(Sub()
@<text>
<a href="@Url.Action("ActionName", "ControllerName")/<%- data.OrderID %>">Link Text</a>
</text>
End Sub)
End Sub) _
.Render()
End Code