Skip to main content

Templates

  • 8 minutes to read

Note

This topic explores component customization. For more information on the DevExpress ASP.NET Core Application Template for Microsoft Visual Studio, refer to the following topic: Bootstrap a New DevExtreme Project with Templates and Layouts.

Templates allow you to customize individual parts of a DevExtreme component. Different DevExtreme components offer different Template methods. View the API page of an individual component for a full list of templates that you can set.

For example, you can customize the following parts of a DataGrid component:

Other popular templates include:

You can use templates to nest DevExtreme components. The image below depicts a DataGrid nested inside a Popup:

DataGrid popup

Table of Contents

Basic Capabilities

Define a Simple Template

Simple templates can include plain HTML, Razor code, and JavaScript.

To define a short template without Razor code, pass a String with HTML to the template definition method.

@(Html.DevExtreme().List()
    .ItemTemplate("<div></div>")
)

Use ERB-style constructs to include template parameters or embed JavaScript code:

@(Html.DevExtreme().List()
    .ItemTemplate("<div><%- obj.Parameter %></div>")
)

To define a multi-line template, pass a @<text> block to the template method:

@(Html.DevExtreme().List()
    .DataSource(DataSource)
    .ItemTemplate(@<text>
        <div><%- Name %></div>
    </text>)
)

Define a Function-based Template

Use the new JS() expression to define a template as a JavaScript function. This technique can be useful in the following cases:

  • You need to render the same template in several different ways, depending on conditional logic.
  • You need to modify the template container.
  • You need to perform logical operations with template parameters.

The myList_itemTemplate function in the example below uses jQuery to apply a custom CSS class to template items. The function can access the itemIndex and itemElement template parameters.

function myList_itemTemplate(itemData, itemIndex, itemElement) {
    itemElement
        .addClass("my-custom-style")
        .append(
            $("<span>").text("Item index: " + itemIndex + ", Name: " + itemData.Name)
        );
}

Store Templates Externally

You can store template definitions outside of the component declaration. This can be useful in the following cases:

  • You want to use the same template in multiple components.
  • You want to use large templates.
  • You need to nest templates.

Store templates in Razor partials

You can store a template in a Razor partial:

@(Html.DevExtreme().Popup()
    .ID("myPopup")
    .ContentTemplate(@<text>
        @(await Html.PartialAsync("_MyPopupContentTemplate"))
    </text>)
)

Use Named Templates

Named Templates are easier to access, but you cannot nest them.

Template names should be unique. You can declare a Named Template in the same Razor file as the component that uses the template. Alternatively, you can store your templates in the layout file.

  1. Use the using(Html.DevExtreme().NamedTemplate(...)) method to create a template definition.
  2. Use the *Template(TemplateName name) method to apply the template.
@(Html.DevExtreme().Popup()
   .ID("myPopup")
   .ContentTemplate(new TemplateName("myPopupContentTemplate"))
)

@using (Html.DevExtreme().NamedTemplate("myPopupContentTemplate")) {
   @(Html.DevExtreme().List()
       .DataSource(ListDataSource)
       .ItemTemplate(@<text>
           <div><%- Name %></div>
       </text>)
   )
}

Embed JavaScript Code, Special Characters, and Raw HTML into Your Template

DevExtreme uses ERB-style delimiters (<% and%>) to indicate template elements that require special evaluation.

Embed JavaScript code

Enclose each line of JavaScript code in ERB-style delimiters (<% and%>). DevExtreme executes this code on the client when it renders the template.

@(Html.DevExtreme().Calendar()
    .CellTemplate(@<text>
        <% if(view !== "month") { %>
            <b><%- text %></b>
        <% } else { %>
            <%- text %>
        <% } %>
    </text>)
)

Encode special characters and access template parameters

Use the <%- %> delimiters to encode special characters as HTML entities and access template data.

The <%- <b>John</b> %> expression returns the &lt;b&gt;John&lt;/b&gt; string.

The <%- obj.prop1 %> expression returns the prop1 template parameter.

Embed raw HTML

Warning

Raw HTML evaluation can make your application vulnerable to cross-site scripting (XSS).

Use the <%= %> delimiters to evaluate raw HTML.

Advanced Capabilities

Access Template Data

Each template offers access to objects with template-related data, otherwise known as template parameters. The names of these objects end with the word Data or Info. The client-side API reference details available parameters for each template. For example:

You can use <%- %> delimiters to reference template data inside the template.

<%- obj %> // Returns the parameter in its entirety.
<%- obj.prop1 %> // Returns a single property of the parameter.
<%- prop1 %> // Parameter properties are available as standalone variables within the scope of the template.

For example, if you define the following Data Source:

object[] DataSource = new[] {
    new { Name = "John" },
    new { Name = "Jane" }
};

You can access the Name parameter as follows:

@(Html.DevExtreme().List()
    .DataSource(DataSource)
    .ItemTemplate(@<text>
        <%- Name %>     <!-- As a free variable -->
        <%- obj.Name %> <!-- As a property -->
    </text>)
)

You can use JavaScript code to process template data.

Use Templates to Nest DevExtreme Components

You can use templates to place DevExtreme components inside other DevExtreme components.

Note

  • You cannot nest multiple templates in a single template definition. Use external templates instead.
  • Set unique IDs for components that you place inside templates.

The following example from the Drill Down PivotGrid demo embeds a DataGrid into a Popup:

@(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 result appears as follows:

DataGrid popup

Use Template Parameters to Pass Component Configuration Data

You can use template parameters to pass configuration data to a nested control.

The cell template in the example below includes a button that receives its value from a template parameter.

  • The new JS expression evaluates the content of the value parameter.
  • A short anonymous function passes the value parameter to the handleGridButtonClick(cellValue) event handler.
@(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>

Note

You can pass a short inline handler function directly to the handler method:

.OnClick("function() { alert(value); }")

Refer to the Events and Callbacks guide for more information.

Master-Detail Grids commonly require component nesting. In the following example, the detail section of a DataGrid includes a different DataGrid. The LoadParams option of the detail grid’s DataSource receives its value from the data.OrderID parameter.

Example: Master-Detail Grid

View Full Demo: Master-Detail View

@(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>)
    )
)

Multi-Level Nesting

You can create multiple levels of nested DevExtreme components. To make template data from higher-level components accessible further down the DOM tree, propagate this data through intermediary components.

As an example, the TabPanel in the Advanced Master-Detail View demo contains a DataGrid. This grid uses another template to render its detail section.

To pass data from the TabPanel to the grid’s detail section, we need to first pass it to the grid as a custom option (tabExtras):

@(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  -->
}

Use HTML Helpers in Templates

Templates can include Razor HTML helpers that render static content. The code below uses the ActionLink HTML helper to render the same link across different template instances:

@(Html.DevExtreme().DataGrid()
    .Columns(columns => {
        //...
        columns.Add().CellTemplate(@<text>
            @Html.ActionLink("Link Text", "Details")
        </text>);
    })
)

Configure grid columns

Process Template Parameters with HTML Helpers

Create a custom HtmlHelper extension to access template parameters in an HTML Helper.

The example below demonstrates how to use a custom HTML Helper to render a link with the OrderID parameter.

using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
using System.IO;
using System.Text.Encodings.Web;

 public static class TemplatedHtmlHelperExtensions {
    public static IHtmlContent TemplatedHtmlHelper(this IHtmlHelper html, IHtmlContent template, Func<string, string> replacementFunc) {
        using(var writer = new StringWriter()) {
            template.WriteTo(writer, HtmlEncoder.Default);
            return html.Raw(replacementFunc(writer.ToString()));
        }
    }
}

You can generate the same URL with HTML and a reference to the template parameter:

@(Html.DevExtreme().DataGrid()
    .Columns(columns => {
        columns.Add().CellTemplate(@<text>
            <a href="@Url.Action("ActionName", "ControllerName")/<%- data.OrderID %>">Link Text</a>
        </text>);
    })
)