terug naar het overzicht

Excel-like filters for Kendo UI Grid

door Sander 22-3-2012

In my previous post I showed how to set up the grid so that all data operations occur on the server. In this post, I’ll build on that a little further to enable filtering like Excel does.

Here is the default method of filtering:

old

And this is what I want:

new

Excuse the blurs, that is ‘real’ testdata Smile

First we need to set up the little screens that will replace the default filtering. I used a div with some Razor to create a list of checkboxes:

<div id="customers" class="filterDropdown">
        @foreach (var c in ViewBag.CustomerFilterSource)
        {
            var customer = "Customers_" + c;
            <input type="checkbox" id="@customer" name="CustomerName" value="@c" checked="checked" />
            <label for="@customer">@c</label>
            <br />
        }
        <input type="button" id="applyCustomerFilter" value="Apply" />
    </div>

The data comes from a collection in the ViewBag that is loaded when the page is served.

Next thing is getting rid of the default filter window. It took me a while to figure this one out but it turned out to be really simple. I used jQuery to locate all the little filter buttons in the header and remove their click events and adding my own:

$("#reportGrid").find(".k-grid-filter").unbind("click").click(function () {
            var parent = $(this).parent();
            var filterList;
            switch (parent.attr("data-field")) {
                case "CustomerName":
                    filterList = $("#customers");
                    break;
                case "CertificateNumber":
                    filterList = $("#certificates");
                    break;
                case "ReportDate":
                    filterList = $("#reportdate");
                    break;
                case "ReportType":
                    filterList = $("#reporttypes");
                    break;
            }
 
            var offset = $(this).offset();
            filterList.css('top', offset.top + $(this).height()).css('left', offset.left + $(this).width()).slideDown();
            return false;
        });

The filter buttons are easy to locate as they have a specific CSS class applied to them. In the switch-case I determine which popup I want to show when the button is clicked. In the last three lines I place the popup in the same spot as the filter button so that it rolls out from where the user clicked.

Some code for the Apply buttons: (just showing one here)

$("#reportdate").hide();
        $("#applyReportDateFilter").click(function () {
            setDsFilter(generateDsFilter());
            $("#reportdate").slideUp();
        })

The popup is hidden since we only want to see it when one of the filterbuttons is clicked. The click event takes care of resetting the datasource filters and hiding the popup.

Now we get to the fun part! To keep the existing filter functionality of the grid’s datasource intact, we need to generate the filters ourselves. That way, the controller code from my previous post can be left unchanged to pick up the filters that the grid posts.

The filters on the datasource are contained in a single object, this object has a property called ‘logic’ which specifies if this is an AND or an OR query. The other is an array of objects named ‘filters’ that hold the columns and their filter values. So here is what needs to happen: Gather all the checkboxes that are checked, translate them into a valid JSON filter object and apply the new filter to the datasource.

function generateDsFilter() {
 
        var checkboxContainer = $("#checkboxFilters");
        var flt = { logic: "and", filters: [] };
 
        checkboxContainer.find("input:checked").each(function () {
            var name = $(this).attr("name");
            var val = $(this).attr("value");
 
            flt.filters.push({ field: name, operator: "eq", value: val });
        });
 
        return flt;
    }

The checkbox container you see holds all the windows that contain checkboxes. We need all of them so I put the windows together in a div for easy selecting with jQuery.

‘flt’ is the object that we need to fill with the filters. The logic is set to AND because we want an exclusive filter. We only want to see stuff that is checked. In a loop we pass all the checked checkboxes using jQuery and add them to the filters property of the main filter object.

To apply the filters to the datasource I added this function:

function setDsFilter(customFilter) {
        var grid = $("#reportGrid").data("kendoGrid").dataSource.filter(customFilter);
    }

I put this in a separate function because there is one thing we still need to fix. The datasource has no initial filter. With the filter being empty, no data is loaded.

To make sure the grid has an initial filter we need to set the ‘filter’ property on the datasource that is part of the grid code:

dataSource: {
                type: "json",
                serverPaging: true,
                serverSorting: true,
                serverFiltering: true,
                allowUnsort: true,
                filter: generateDsFilter(),
                pageSize: 25,
                transport: {
                    read: {
                        url: "Export/PagedData",
                        type: "POST",
                        dataType: "json",
                        contentType: "application/json; charset=utf-8"
                    },
                    parameterMap: function (options) {
                        return JSON.stringify(options);
                    }
                },
                schema: { data: "Items", total: "TotalItemCount" }
            }

This ensures that when the grid is first created, all the filters are applied BEFORE the data is loaded, in this case, all the data is loaded because all the checkboxes are checked.

When clicking the Apply button in one of the filter windows (see the code somewhere above) we regenerate the filters and apply them to the datasource. The grid will then call the Controller Action and post the filters to the server for you to handle.

Tags:

ASP.NET MVC | Development | Kendo UI

Kendo UI Grid with server paging filtering and sorting (with MVC3)

door Sander 21-3-2012

I gave the Kendo UI framework a try today. I needed a grid that could do anything (filtering, sorting and paging – the real kind, on the server that is).

Going about my coding business I came across some things that I found hard to solve using the existing documentation, which inspired this blogpost.

As I mentioned, this is about the Kendo UI KendoGrid. The definition of the grid itself is very easy to when you use the “getting started” tutorial and I ended up with something like this:

$(document).ready(function () {
        $("#reportGrid").kendoGrid({
            dataSource: {
                type: "json",
                serverPaging: true,
                serverSorting: true,
                serverFiltering: true,
                allowUnsort: true,
                pageSize: 10,
                transport: { 
                    read: { 
                        url: "Export/PagedData",
                        type: "POST",
                        dataType: "json",
                        contentType: "application/json; charset=utf-8"
                    },
                    parameterMap: function(options) {
                        return JSON.stringify(options);
                    }
                },
                schema: { data: "Items", total: "TotalItemCount" }
            },
            pageable: true,
            sortable: true,
            filterable: true,
            columns: [
                    { field: "CustomerName", title: "Klant" },
                    { field: "SerialNumber", title: "Serienummer" },
                    { field: "CertificateNumber", title: "Polisnummer" },
                    { field: "ReportDate", title: "Melddatum" },
                    { field: "ReportType", title: "Meldingtype" },
                    { field: "Description", title: "Omschrijving" }
                ],
        });
    });

One point of interest in this code here, the parameterMap in the transport section of the datasource. Without that mapping there, MVC3 will have a hard time reading the parameters that are posted by the grid. The stringify function translates it to something we can use in our controller action.

Lets take a look at that:

public JsonResult PagedData(int skip, int take, int page, int pageSize, List<SortDescription> sort, FilterContainer filter)
        {
            int itemCount = 0;
            var data = _export.GetData(page - 1, pageSize, out itemCount, filter, sort);
 
            return Json(new ExportModel(data, itemCount), JsonRequestBehavior.AllowGet);
        }

The first four parameters are straightforward and let me handle the paging in my query. Note that you need to define the serverXXXXX properties on the grid for these to be posted to the controller on the server! The schema tells the grid the properties to look for when data is read from the server. When paging serverside, you will also need to tell the grid how many items exist on the server so it can calculate the number of pages to show.

After that, things get interesting. Before I came to the solution I now have I spent a fair amount of time searching high and low on how to solve the parsing of array in the querystring. I gave up on that after finding out about the stringify function for the parameterMap in the grid.

If you want some more insight on how to configuring the datasource, look here:

http://www.kendoui.com/documentation/framework/datasource/configuration.aspx

It has everything on configuring a datasource for the Kendo Grid. I was looking for filtering and sorting, which comes down to this:

When filtering, the grid produces and object that contains an array of objects that hold our filter parameters. It holds the columnname, the filter value and the operator used in the filter.

When sorting, an array of objects is produced where each element has an object that contains the column and sort direction for every sort operation.

Translating that into code, we come to this:

public class SortDescription
    {
        public string field { get; set; }
        public string dir { get; set; }
    }
 
public class FilterContainer
    {
        public List<FilterDescription> filters { get; set; }
        public string logic { get; set; }
    }
 
public class FilterDescription
    {
        public string @operator { get; set; }
        public string field { get; set; }
        public string value { get; set; }
    }
If you look at the parameters in the controlleraction, we have a list of sortdescriptions, and an object that contains a list of filters.

The controller is now aware of the filtering and sorting on the grid and all you have left to do is translate the filters and sorting into a query.

Tags:

ASP.NET MVC | Development

Using the ASP.NET MVC 3 Razor view engine in Windows Azure

door Ronald 11-1-2011

Yesterday I was trying to upgrade an ASP.NET MVC 2 web application to ASP.NET MVC 3 RC2. I also wanted to use the new Razor view engine that allows a more compact and less obtrusive way of writing ASP.NET MVC view templates.

There already is an excellent guide of how to upgrade your MVC 2 application to MVC 3 RC2. If you are running your MVC 2 application in Windows Azure, you must take some additional steps, which are described here. Visual Studio 2010 doesn’t (yet) allow you to specify an ASP.NET MVC 3 Web Role, so when running in Azure, you always have to upgrade if you want to use MVC 3.

You should reference some extra DLL’s from your MVC project and make sure that for all these DLL’s you set Copy Local to True. This is necessary because on the virtual machine your application is deployed to these DLL’s are not in the GAC. The DLL’s you have to reference are:

  • Microsoft.Web.Infrastructure
  • System.Web.Helpers
  • System.Web.Mvc
  • System.Web.Razor
  • System.Web.WebPages
  • System.Web.WebPages.Razor
  • WebMatrix.Data (I’m not sure why this one is necessary)

and these are located either somewhere below C:\Program Files (x86)\Microsoft ASP.NET for 64bits or below C:\Program Files\Microsoft ASP.NET for 32bits. So far so good.

Having taken all the required steps (or so I thought) I tried to run my first Razor template in the Windows Azure emulator:

image

No what I expected: The name ‘ViewBag’ does not exist in the current context. I must have forgotten something. After double-checking I had all the required dependencies that all had there Copy Local property set to True, I took a better look at the Compilation Source. My page appeared to extend from System.Web.WebPages.WebPage. This class doesn’t have a ViewBag property so that explains the compilation error.

Back to the upgrade instructions. It appeared I had forgotten one small step. In the Views sub directory there is an additional Web.config file. In an MVC 3 project, it contains a new configuration section: system.web.webPages.razor. In this section, a new page base type is specified: System.Web.Mvc.WebViewPage. After copying this Web.config file from another MVC 3 project, everything worked as expected.

A classic case of RTFM. But if you ever happen to see this compiler error when using the Razor view engine, check your Web.config file.

Tags:

Cloud | Development | ASP.NET MVC