Telerik blogs
kendo_grid_header

Kendo UI's Grid component supports several methods of data binding. The two that I most commonly use are Local and Ajax (Ajax being the more common as of late). One pain point I've always had with this however is that the AJAX binding doesn't store state when filtering, paginating, sorting, etc. Compare this to the "local" binding - it stores everything in the querystring which makes it easy to both browse back to your list, or to share that specific result set with someone else.

True, the grid does offer a persist state feature, but that stores everything about the configuration of a grid, such as column configurations, which is more than you'd want to store in the querystring.

In this post, I want to discuss a potential solution that I worked on that offers the benefits of AJAX data loading, while remembering what the user's current criteria is.

Please note that my examples are all using UI for ASP.NET MVC, which is powered by Kendo UI. However, the same technique could be applied to Kendo UI directly.

Creating an Example

Let's assume we have a Grid bound to the following model:

public class UserModel
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public bool IsAdmin { get; set; }
    public bool IsActive { get; set; }
}

Let's also assume that we have a view model, named UserListViewModel with the same properties but they are all nullable. This will be our view's model, as defined with the @model syntax. Now, let's quickly take a look at our Grid definition:

@(Html.Kendo().Grid()
      .Name("users-grid")
      .DataSource(ds => ds.Ajax()
          .Read("GetUsers", "Users")
          .Filter(filter => filter.GetInitialFilters(Model))
          .Sort(s => s.Add(sb => sb.LastName))
          .PageSize(20))
      .Sortable()
      .Pageable()
      .Columns(cols =>
      {
          cols.Bound(x => x.LastName);
          cols.Bound(x => x.FirstName);
          cols.Bound(x => x.Email);
          cols.Bound(x => x.IsAdmin);
          cols.Bound(x => x.IsActive);
          cols.Template(t => t.Id).Title("").ClientTemplate(string.Format("Edit", Url.Action("Edit"))).Width("7%");              
       }))

This is a pretty simple grid definition, with only one "non-standard" thing:

.Filter(filter => filter.GetInitialFilters(Model))

This is what we use to pre-filter the grid results with the values set in the View Model (these values are set in the Index action method on the controller). The GetInitialFilters() method is an extension method on DataSourceFilterDescriptorFactory<UserModel> and looks like this:

public static DataSourceFilterDescriptorFactory<UserModel> GetInitialFilters(
    this DataSourceFilterDescriptorFactory<UserModel> filter, UserListViewModel model)
{            
    if (!model.FirstName.IsNullOrEmpty())
    {
        filter.Add(x => x.FirstName).Contains(model.FirstName);
    }

    if (!model.LastName.IsNullOrEmpty())
    {
        filter.Add(x => x.LastName).Contains(model.LastName);
    }

    if (!model.Email.IsNullOrEmpty())
    {
        filter.Add(x => x.Email).Contains(model.Email);
    }

    // Additional filters redacted for brevity
    return filter;
}

Note that some of the code has been redacted for brevity, we are essentially checking what UserListViewModel's properties' values are (if they have one) and setting the filters accordingly.

Getting the Selected Filters

So, now that we can pre-populate the grid with filters from the querystring, let's look at how to get our current selected filters into the querystring without doing a page refresh. While the Kendo grid does have a filtering UI, for some grids with more fields, I find it to be a better user experience to create a search form above the grid. While I'm not going to focus on that form here, it's a GET form, but the submit is intercepted with jQuery as shown below:

$(document).ready(function() {
    var $grdProjects = $('#projects-grid').data('kendoGrid'),
        $btnFilterProjects = $('#btn-filter-projects');

    $btnFilterProjects.on('click', function(e) {
        e.preventDefault();

        // Creates JSON object with form values (keys are field names)
        var params = getFilterParams();

        var filters = [];
        if (params.lastName) {
            filters.push({ field: 'LastName', operator: 'contains', value: params.lastName });
        }
        // Additional properties redacted

        // Sets the grid's datasource to the filters defined above 
        $grdProjects.dataSource.filter(filters);

        var filterQueryString = '?' + collab.helpers.objectToString(params);
        window.history.pushState(params, null, filterQueryString);
        });
});

While most of this JavaScript is pretty self explanatory, let's look at some key parts.

To start, we're getting the filter parameters as a JSON object. From there, if each parameter ("lastName") has a value, we are building the filters array that the Kendo Datasource needs and then setting the grid's datasource with that array. Finally, we're getting a comma-separated string of the filter parameters and using the window.history.pushState() method to put the values in the querystring for easy linking.

Conclusion

While I love Kendo's Grid (and many of the other widgets), this makes the Grid much more user friendly and linkable to specific search criteria for linking.

Related resources:


Telerik Blogging Ninja
About the Author

The Telerik Team

 

Comments

Comments are disabled in preview mode.