Optgroup with the Kendo UI Combobox

Kendo UI is a great framework to use, sometimes what you are looking for is not out of the box, but it’s not really difficult to make it work as you want to.

Kendo provides a combobox and a dropdown control, the following article uses the combobox for the example but applies for both controls.

What I want to achieve is to build a combobox with an optgroup.

Here is the raw html sample

<select>
 <optgroup label="Group 1">
 <option value="Value0">Option N°0</option>
 <option value="Value1">Option N°1</option>
 <!-- ... -->
 </optgroup>
 <optgroup label="Group 2">
 <option value="Value0">Option N°0</option>
 <option value="Value1">Option N°1</option>
 <!-- ... -->
 </optgroup>
 <optgroup label="Group 3">
 <option value="Value0">Option N°0</option>
 <option value="Value1">Option N°1</option>
 <!-- ... -->
 </optgroup>
 </select>

Using kendo, you can define a combox using the MVC wrappers this way

@(Html.Kendo().ComboBox()
 .Name("democombo").HtmlAttributes(new { style = "width:300px;" })
 .Filter("contains")
 .Placeholder("Select an item ...")
 .Items(x =>
 {
 // add some values
 x.Add().Text("Group 1");
 for (int i = 0; i < 10; i++)
 {
 x.Add().Value("value" + i).Text("Option N°" + i);
 }
 x.Add().Text("Group 2");
 for (int i = 0; i < 10; i++)
 {
 x.Add().Value("value" + i).Text("Option N°" + i);
 }
 //....

}))

it will look like this :

kendo combobox

There is a nice style and a search functionality, but since there is no way to specify that we want an optgroup, you cannot spot the difference between a simple item and a “group” item

Fortunately, it’s not that hard to make it work like if this option was existing.

Kendo ships with a template engine that will help

if we change a bit our code to make a clever user of the template :


@(Html.Kendo().ComboBox()
 .Name("democombo").HtmlAttributes(new { style = "width:300px;" })
 .Filter("contains")
 .Placeholder("Select an item ...")
 .Items(x =>
 {
x.Add().Text("Group 1").Value("optgroup");// notice that we give a value
 // ...
}).TemplateId("scriptTemplate"))

the template

// we use the value
<script id="scriptTemplate" type="text/x-kendo-template">
 # if ( Value=="optgroup") { #
 <div class="optgroup">#=Text#</div>
 # } else { #
 <div class="option">#=Text#</div> # } #
</script>

and the css


.k-list .k-item .optgroup {
 background: #fff;
 color: #000;
 font-weight: bold;
 }
 .k-list .k-item .option {
 padding-left: 15px;
 }

we are getting this result

kendo ui template

It’s nice but there is still two problems: the style on hover change, and the item is clickable

we can make use of the kendo combobox events to resolve that :


@(Html.Kendo().ComboBox()
 .Name("democombo").HtmlAttributes(new { style = "width:300px;" })
 .Filter("contains")
 .Placeholder("Select an item ...")
 .Items(x =>
 {
x.Add().Text("Group 1").Value("optgroup");
// ...
}).TemplateId("scriptTemplate").Events(events =>
 {
 events.DataBound(@<text>function(e){
// disable the click, and add a special class on the parent (which is the real item)
       $(".optgroup").parent().click(false).addClass("optgrouplistitem");
 }</text>);>
 }))

Some CSS to override the normal kendo “hover” style


.k-popup .k-list .k-item.optgrouplistitem.k-state-hover{
 border: none;
 color: #000;
 border-radius: 0;
 background: none;
 padding: 1px 5px 1px 5px;
 box-shadow: none;
 }

and we end with this

kendo optgroup

Complete code

<style type="text/css">
 .k-list .k-item .optgroup {
 background: #fff;
 color: #000;
 font-weight: bold;
 }

 .k-popup .k-list .k-item.optgrouplistitem.k-state-hover {
 border: none;
 color: #000;
 border-radius: 0;
 background: none;
 padding: 1px 5px 1px 5px;
 box-shadow: none;
 }

.k-list .k-item .option {
 padding-left: 15px;
 }
</style>

 

<script id="scriptTemplate" type="text/x-kendo-template">
 # if ( Value=="optgroup") { #
 <div class="optgroup">#=Text#</div>
 # } else { #
 <div class="option">#=Text#</div> # } #
</script>

 

@(Html.Kendo().ComboBox()
 .Name("democombo").HtmlAttributes(new { style = "width:300px;" })
 .Filter("contains")
 .Placeholder("Select an item ...")
 .Items(x =>
 {
 x.Add().Text("Group 1").Value("optgroup");
 for (int i = 0; i < 10; i++)
 {
 x.Add().Value("value" + i).Text("Option N°" + i);
 }
 x.Add().Text("Group 2").Value("optgroup");
 for (int i = 0; i < 10; i++)
 {
 x.Add().Value("value" + i).Text("Option N°" + i);
 }
 x.Add().Text("Group 3").Value("optgroup");
 for (int i = 0; i < 10; i++)
 {
 x.Add().Value("value" + i).Text("Option N°" + i);
 }

}).TemplateId("scriptTemplate").Events(events =>
 {

events.DataBound(@<text>function(e){
 $(".optgroup").parent().click(false).addClass("optgrouplistitem");
 }</text>);
 }))

Incoming search terms:

  • kendo combobox
  • kendoComboBox template tree
  • kendoDropDownList and optgroup
  • kendo select group items
  • kendocombobox
  • optgroup filter
  • kendo ui model for combobox
  • Kendo() DropDownList TemplateId
  • kendo optgroup
  • filter the kendo drop down list with 2 or 3 drop donlist

6 comments

  1. Rulin says:

    Great article. But filtering mouse click is not enough . It needs implement keyboard related functions such as filtering, navigation, etc.

  2. admin says:

    Hi,

    Although I suppose it’s possible with a bit of javascript, I think a treeview will be better for this situation.

    I found these links that might be helpful:

    Telerik forum : http://www.telerik.com/forums/treeview-in-dropdownlist
    Telerik forum : http://www.telerik.com/forums/treeview-inside-dropdownlist
    aspnetwiki : http://www.aspnetwiki.com/page:kendoui-ext-api-dropdowntreeview

    if it fits your requirement, you can then apply a css to mimic an optgroup

  3. seb says:

    HI Sam, great job !
    Is there a way to make the group expandable/collapsible ?

  4. Jonas says:

    Thanks Sam!
    It’s working fine.

  5. admin says:

    Hi, thanks for your comment!

    Here is the first solution I can think of :

    Imagine your service returns you a list of items having subitems

    so if you have a class “ListItem”:

    public class ListItem
    {
        public string Text { get; set; }
        public string ID { get; set; }
        public IList<ListItem> SubItems { get; set; }
    }
    

    your service returns a “IList

    The first level of list items are going to be your groups, and the subitems your real items

    in your controller you will retrieve that list of “ListItem” and fill your model with it(or you will receive something else and twist it to end with something similar)

    let’s say your model is “MyModel”

    public class MyModel
    {
     // ...
     public IList<ListItem> Items{get;set;}
     // ...
    }
    

    in your view, for the dropdownlist, you will add the items this way:

    //...
    .Items(x =>
    {
    	foreach (var item in Model.Items)
    	{
    		x.Add().Text(item.Text).Value("optgroup");
    		foreach (var subitem in item.SubItems)
    		{
    			x.Add().Text(subitem.Text).Value(subitem.ID);
    		}
    	}
    }) 
    // ...
    

    (I’m using notepad to write you the example, it’s not tested so it can contains some typo)

  6. Jonas says:

    Ho Sam,
    Great job Sam. This is exactly what I’m looking for right now.
    I tried without success to make the “.Items(x =>” area dynamic. I mean based on the data I receive from a call to a wcf service. The values “Group 1″ and the number of groups in my case are dynamic.
    Do you have any clue ??

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>