aspnetcore-401029-devextreme-based-controls-concepts-templates.md
Note
This tutorial and the Concepts section apply only to UI Controls (DevExtreme). To get started with other controls, refer to the following help topics:
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:
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.
@(Html.DevExtreme().List()
.DataSource(DataSource)
.ItemTemplate(@<text>
<div><%- Name %></div>
</text>)
)
object[] DataSource = new[] {
new { Name = "John" },
new { 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 (that start with @), you can use a shorthand overload of a *Template method with a String argument:
@(Html.DevExtreme().List()
.DataSource(DataSource)
.ItemTemplate("<div><%- Name %></div>")
)
Note
DevExtreme *Template(RazorBlock templateContent) methods, like Splitter ItemTemplate(RazorBlock), are not compatible with native ASP.NET Core/MVC’s @Render* expressions (for example, @RenderBody).
To incorporate these expressions, use named templates :
<body>
@(Html.DevExtreme().Splitter()
.Items(items => {
items.Add().Template(@<text> Static nav content </text>);
items.Add().Template(new TemplateName("my-render-body-template"));
})
)
@RenderBody()
</body>
@using (Html.DevExtreme().NamedTemplate("my-render-body-template")) {
<div>Employee Info</div>
<div>@(Html.DevExtreme().TextBox().Value("First Name"))</div>
<div>@(Html.DevExtreme().TextBox().Value("Second Name"))</div>
}
You can define templates outside of a control declaration. This approach can be useful in the following scenarios:
Use one of the following approaches 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. It supports three tag types:
HTML-encoded expression tags
Non-HTML-encoded expression tags
Execution tags
Important
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 Data Grid column’s cellTemplate provides a predefined set of properties: value, text, columnIndex, and so on.itemData object of a List’s itemTemplate allows you to access the data linked to the current list item. The object returns an entry from items or a data record from dataSource.You can reference entire objects in a template as obj. Properties of the *Data or *Info object are available directly in the template scope. These properties are called free variables.
The following code snippet accesses 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>)
)
object[] DataSource = new[] {
new { Name = "John" },
new { 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. Refer to new JS() Expression for more information.
In the following code snippet, the List’s ItemTemplate is declared as the myList_itemTemplate function. The function renders item content and applies a custom style using jQuery. This approach allows you 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"))
)
<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-based controls within templates. For example, the following code nests the Data Grid 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>)
)
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 are assigned auto generated IDs.A nested control’s configuration can depend on template parameters.
The following code snippet defines a template for Data Grid cells. A cell contains a button. The button configuration depends on the value variable from the cellTemplate.
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>);
})
)
<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 Data Grid. 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>)
)
)
In some scenarios, template parameters should be passed through an intermediate control. For example, if you place a detail Data Grid in a Tab Panel, the detail grid has access to the Tab template parameters but does not have access to the parameters of a master Data Grid detail template. To solve this issue, attach the tabExtras custom option to the Tab, and then 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 -->
}
object[] MasterGridDataSource = new[] {
new { ID = 1, Name = "John" },
new { ID = 2, Name = "Jane" }
};
Demo: Advanced Master-Detail View
You can use HTML helpers in templates as usual if they render static content. For example, if you want every cell in a Data Grid column to display the same link, the following code snippet uses the ActionLink HTML helper to render the link.
@(Html.DevExtreme().DataGrid()
.Columns(columns => {
//...
columns.Add().CellTemplate(@<text>
@Html.ActionLink("Link Text", "Details")
</text>);
})
)
If you need to use template parameters, use the approach with a custom HtmlHelper extension. The steps below demonstrate an example of how to create a template if the links in the grid column depend on the OrderID.
In the project’s root, create the TemplatedHtmlHelperExtensions.cs file that declares an HtmlHelper extension:
Use the declared extension in a Razor file (.cshtml):
You can use HTML elements instead of helpers.
@(Html.DevExtreme().DataGrid()
.Columns(columns => {
columns.Add().CellTemplate(@<text>
<a href="@Url.Action("ActionName", "ControllerName")/<%- data.OrderID %>">Link Text</a>
</text>);
})
)