Customize Widget Element Appearance - MVVM Approach

You may often encounter the need to customize widget appearance. In the most simple case, widget elements (title, content, collection item) have a simple textual presentation. But in most cases, widget elements require a more complex structure. Moreover, an element appearance may depend on values held in a data model associated with the current view. In this topic, you will learn how to customize the appearance of such widget elements.

If a widget contains elements available for customization, it includes the corresponding options that end with "Template". For instance, the dxPopover widget enables you to customize its content and title. For this purpose, it includes the contentTemplate and titleTemplate options.

Similar article is available for jQuery approach.

Read below to learn how to customize the required element appearance.

Use a Default Template

A default template for a widget is defined out-of-the-box. Default templates are based on a predefined set of fields in the widget. For example, any widget has a field that allows you to modify textual representation of the widget.

  • text
    Specifies the text inserted into the widget element.

Different widgets can have other additional fields (for example, icon field).

Collection widgets have default templates for item representation.They are based on a predefined set of fields in the widget data source, requiring you to provide the required set of fields for your widget's data source, so that widget items are displayed using a default template. The following are the fields that are used in a default item template of any widget.

  • disabled
    Specifies whether or not the list item is disabled.
  • visible
    Specifies whether or not the list item is visible.
  • html
    Specifies the html code inserted into the item element.
  • template
    Specifies an item template that should be used to render this item only.
  • text
    Specifies the text inserted into the item element.

However, additional fields can be required for item templates of certain widgets. For instance, a default template for the dxList widget is based on the set of fields above. However, the key field is also required to specify the group to which an item is related. To learn what set of fields is required for a specific widget, refer to the Default Item Template reference section of this widget.

In certain scenarios, it is enough to specify a simple array as a widget data source. In this instance, a default item template contains text binding associated with the current array value.

Implement a Custom Template

To customize widget element appearance, you can use a built-in template engine.

In the examples presented below, the data model has the following structure.

Angular Approach
JavaScript
function Controller($scope) {
    $scope.popoverTitle = "Popover Title";
}
Knockout Approach
JavaScript
viewModel = {
    popoverTitle: "Popover Title"
}

To specify widget element structure, create an HTML container element within the widget container element and apply the data-options attribute set to dxTemplate. Then, insert the required structure in the newly added dxTemplate element.

Angular Approach
HTML
<div dx-popover="{ showTitle: true }">
    <div data-options="dxTemplate:{ name:'title' }">
        <h1>{{popoverTitle}}</h1>
    </div>
</div>
Knockout Approach
HTML
<div data-bind="dxPopover:{ showTitle: true }">
    <div data-options="dxTemplate:{ name:'title' }">
        <h1 data-bind="text: popoverTitle"></h1>
    </div>
</div>

A binding context of a template is the bound view or application model. So, you can bind template elements to the scope or view model's fields directly (see the code above). To access another binding context within a template, use the [Knockout binding variables] ](http://knockoutjs.com/documentation/binding-context.html) or Angular binding variables, depending on the approach you use.

The default name of the template element usually corresponds to the required element name (e.g., 'title' for a title element,'content' for a content element, 'item' for an item element). If you use a different template name, assign it to the appropriate option (titleTemplate for a title element, contentTemplate for a content element and so on).

Angular Approach
HTML
<div dx-popover="{ showTitle: true, titleTemplate:'popoverTitle', contentTemplate: 'popoverContent' }">
    <div data-options="dxTemplate:{ name:'popoverTitle' }">
        ...
    </div>
    <div data-options="dxTemplate:{ name:'popoverContent' }">
        ...
    </div>
</div>
Knockout Approach
HTML
<div data-bind="dxPopover:{ showTitle: true, titleTemplate:'popoverTitle', contentTemplate: 'popoverContent' }">
    <div data-options="dxTemplate:{ name:'popoverTitle' }">
        ...
    </div>
    <div data-options="dxTemplate:{ name:'popoverContent' }">
        ...
    </div>
</div>

You can assign the template name directly to the option, or assign a function that returns the required template name.

JavaScript
var popoverOptions = {
    contentTemplate: function() { 
        return 'popoverContent'; 
    }
}

Provide Several Custom Templates

You may have several templates defined within a widget. To apply the required one in a specific context, assign a function to the required "Template" option. The return value of this function must be a string representing the name of the required template.

Angular Approach
JavaScript
function Controller($scope) {
    $scope.popoverTitle = "Popover Title";
    $scope.highlightTitle = false;
    $scope.getTemplate = function() {
        return highlightTitle ? "highlightedTitle" : "standardTitle";
    }
}
HTML
<div dx-popover="{ showTitle: true, titleTemplate: getTemplate }">
    <div data-options="dxTemplate:{ name:'standardTitle' }">
        <h1>{{popoverTitle}}</h1>
    </div>
    <div data-options="dxTemplate:{ name:'highlightedTitle' }">
        <h1 style="color: red;">{{popoverTitle}}</h1>
    </div>
</div>
Knockout Approach
JavaScript
viewModel = {
    popoverTitle: "Popover Title",
    highlightTitle: false,
    getTemplate: function() {
        return highlightTitle ? "highlightedTitle" : "standardTitle";
    }
}
HTML
<div data-bind="dxPopover:{ showTitle: true, titleTemplate: getTemplate }">
    <div data-options="dxTemplate:{ name:'standardTitle' }">
        <h1 data-bind="text: popoverTitle"></h1>
    </div>
    <div data-options="dxTemplate:{ name:'highlightedTitle' }">
        <h1 style="color: red;" data-bind="text: popoverTitle"></h1>
    </div>
</div>

In the same manner, you can provide an individual template for an item of a collection widget. To set the required template for a widget item, specify the template field within the data source object that represents this item.

JavaScript
tileViewData = [
    {
        name: 'Alabama',
        area: 135765,
        population: 4822023,
        capital: 'Montgomery',
        //template to this certain item
        template: 'specific'
    },
    {
        name: 'Alaska',
        area: 1717854,
        population: 731449,
        capital: 'Juneau'
    },
    ...
];
Angular Approach
HTML
<div dx-tile-view = "{ dataSource: tileViewData, itemTemplate:'myItem' }">
    <div data-options="dxTemplate:{name:'myItem'}">
        ...
    </div>
    <div data-options="dxTemplate:{name:'specific'}">
        ...
    </div>
</div>
Knockout Approach
HTML
<div data-bind="dxTileView:{dataSource: tileViewData, itemTemplate:'myItem'}">
    <div data-options="dxTemplate:{name:'myItem'}">
        ...
    </div>
    <div data-options="dxTemplate:{name:'specific'}">
        ...
    </div>
</div>

If you need to define individual markup for collection widget items, which are not bound to a data source, you may use the dxItem markup component.

Angular Approach
HTML
<div dx-list="listOptions">
    <div data-options="dxItem: item1Options">
        <!--First item markup-->
    </div>
    <div data-options="dxItem: item2Options">
        <!--Second item markup-->
    </div>
    <div data-options="dxItem: item3Options">
        <!--Third item markup-->
    </div>
    . . .
</div>
Knockout Approach
HTML
<div data-bind="dxList: listOptions">
    <div data-options="dxItem: item1Options">
        <!--First item markup-->
    </div>
    <div data-options="dxItem: item2Options">
        <!--Second item markup-->
    </div>
    <div data-options="dxItem: item3Options">
        <!--Third item markup-->
    </div>
    . . .
</div>

Provide a Common Template

When you have several widgets with similar elements, you may need to define a common custom template for them. For this purpose, factor out a template into a separate element, as demonstrated below.

Angular Approach
JavaScript
function Controller($scope) {
    $scope.name = "John Doe";
    $scope.city = "Glendale";
    $scope.phone = "(626) 555-9248";
}
HTML
<script id="person-template">
    <h3>{{name}}</h3>
    <p>City: <span>{{address}}</span></p>
    <p>Phone: <span>{{phone}}</span></p>
</script>
Knockout Approach
JavaScript
var viewModel = {
    name: "John Doe",
    city: "Glendale",
    phone: "(626) 555-9248"
}
HTML
<script id="person-template">
    <h3 data-bind="text: name"></h3>
    <p>City: <span data-bind="text: address"></span></p>
    <p>Phone: <span data-bind="text: phone"></span></p>
</script>

To use the common template for the rendering of a certain element, assign one of the following values to the appropriate "Template" option of the widget.

  • Assign a jQuery object of the template's container.

    Angular Approach
    HTML
    <div dx-popover="{ contentTemplate: $('#person-template') }">
    </div>
    Knockout Approach
    HTML
    <div data-bind="dxPopover:{ contentTemplate: $('#person-template') }">
    </div>
  • Assign a DOM Node of the template's container.

    Knockout Approach
    HTML
    <div data-bind="dxPopover:{ contentTemplate: document.getElementById('person-template') }">
    </div>
    Angular Approach
    HTML
    <div dx-popover="{ contentTemplate: document.getElementById('person-template') }">
    </div>
  • Assign a function that returns the jQuery object or a DOM Node of the template's container.

    Angular Approach
    HTML
    <div dx-popover="{ contentTemplate: function() { return document.getElementById('person-template'); } }">
    </div>
    Knockout Approach
    HTML
    <div data-bind="dxPopover:{ contentTemplate: function() { return document.getElementById('person-template'); } }">
    </div>

To use the common template for the item rendering of a collection widget, assign a jQuery object, a DOM node of the template element, or a function returning any of them to the itemTemplate option.

Customize Rendered Markup Element

When a customizable widget element is rendered, the widget fires the corresponding event, which enables you to modify the rendered element for your requirements. You can assign the function which handles the required event to the onElementRendered option, where Element is a name of the rendered widget element. For instance, pass the event handling function to the onTitleRendered option to handle the event that occurs when the widget title is rendered. If a widget contains customizable content, the option holding a function called when widget content is rendered is called onContentReady.

JavaScript
var popoverOptions = {
    onTitleRendered: titleRenderedHandler
}

The object passed to the handler function enables you to access the rendered markup element and the data model associated with the current view.

JavaScript
var myTitle = "Popover Title";
var titleRenderedHandler = function(e) {
    // Use the 'e' object to access the widget markup element, rendered element and data model
    e.titleElement.append("<h1>" + myTitle + "</h1>");
}

Refer to the required event description for detailed information on the supported fields of the parameter object.

Note that rendered (for example onItemRendered, onGroupRendered) event handlers for collection items have other parameters. For more information, refer to the Customize Collection Item Appearance article.

See Also

Use an Alternative Template Engine

DevExtreme enables you to use an alternative template engine to customize the appearance of widget elements. Pass the name of the required template engine to the DevExpress.ui.setTemplateEngine(name) method to define which engine to use. DevExtreme supports the following template engines out of the box.

If DevExtreme does not support your required template engine, call the DevExpress.ui.setTemplateEngine(options) method to define a custom template engine. The options argument should be an object containing the following fields.

  • compile
    Holds a function that compiles a template.

  • render
    Holds a function that renders a template.