Kendo UI for Angular: Customizing the order of Grouping in the Grid

We recently had a scenario where we wanted our data grouped in a Grid. However, we needed a special order applied to the grouping. Alphabetical did not work for us based on a business requirement. In addition, the dataset we were working with was guaranteed to be limited to less than 30 records so paging would be turned off.

For our example, we will be working with a list of products. Here is a sample model used in the list:

{
    ProductID: 1,
    ProductName: 'Chai Tea',
    SupplierID: 1,
    CategoryID: 1,
    QuantityPerUnit: '10 boxes x 20 bags',
    UnitPrice: 1.49,
    UnitsInStock: 39,
    UnitsOnOrder: 0,
    ReorderLevel: 10,
    Discontinued: false,
    Category: {
      CategoryID: 901,
      CategoryName: 'Teas',
      Description: 'Teas',
      Order: 1,
    },
  },

Out of the box, Kendo UI will allow you to easily group a dataset in a Kendo UI Grid. You can also sort it alphabetically ascending or descending. Here is a sample of the Angular code.

export class AppComponent implements OnInit {
  public groups: GroupDescriptor[] = [{ field: 'Category.CategoryName' }];

  public gridView: DataResult;

  public ngOnInit(): void {
    this.loadProducts();
  }

  private loadProducts(): void {
    this.gridView = process(products, { group: this.groups });
  }
}

Here is the Kendo Template. Notice setting groupable to false prevents the user from changing the grouping.

<kendo-grid
          [groupable]="false"
          [data]="gridView"
          [height]="400"
          [group]="groups"
        >
        <kendo-grid-column field="ProductID" title="ID" [width]="80"></kendo-grid-column>
        <kendo-grid-column field="ProductName" title="Name" [width]="300"></kendo-grid-column>
        <kendo-grid-column field="UnitPrice" title="Unit Price" [width]="120"></kendo-grid-column>
        <kendo-grid-column field="Category.CategoryName" title="Category">
            <ng-template kendoGridGroupHeaderTemplate let-value="value">
                {{value}}
            </ng-template>
        </kendo-grid-column>
</kendo-grid>

The above code groups the data for the Grid to display as such:

Notice that the order of the groups is alphabetical. Sorry, adding some ads here to help pay for my WordPress annual subscription. Keep scrolling to the next section to see how to tweak the code.

Advertisements
Advertisements

How to change the order

The SME really wanted the “Teas” group to show first, then “Coffee” and finally “Soft Drinks”. You’re probably asking “Really???” But it really did make sense for the business requirements to order the data in such a way. We also really needed it in a Kendo UI Grid since there were many other features we needed for this feature.

You can easily change the grouping to use the “Order” property in the Category Model.

public groups: GroupDescriptor[] = [{ field: 'Category.Order' }];

But this will display the following which is not user friendly. The Group Descriptor is using the field as the value to display. But we want the value to be the Category Name.

So now we just need to tweak this a bit to get it to work as required. “loadProducts” needs to be changed to update the value to display. The call to “process” will order the data based on the grouping. Next we need loop through the view data, get the Category Name and put it in the group value which is then displayed when bound.

private loadProducts(): void {
    //this.gridView = process(products, { group: this.groups });
    var view = process(products, { group: this.groups });

    view.data.forEach(x => { x.value = x.items[0].Category.CategoryName });

    this.gridView = view;
  }

We need to tweak the Group column in the template so the display is user friendly. The field will be bound to the Category.Order which is the same field we are ordering by.

<kendo-grid-column field="Category.Order" title="Category"> 
      <ng-template kendoGridGroupHeaderTemplate let-value="value"> {{value}}          
      </ng-template> 
</kendo-grid-column>

Now take a look at the grid. The grid is ordered using the Order field but the display shows Category Name. Just how the SMEs wanted it to work! Turns out what seemed like a challenging requirement was pretty simple to implement. Thanks Kendo UI! You saved the day again.

When you populate the “Order” property in your data, you can easily tweak the logic to what your business needs are. For instance, you can order by the number of items per group.

Note: The jQuery version of Kendo UI does allow for a “compare” function on the sort. See

It would be nice if Telerik would be consistent between the two versions of the framework.

Conclusion

Telerik’s Kendo UI components are rather powerful tools. They can easily speed up development once you get the hang of them.

Here is a working sample of the code:

References:

KendoUI: Understanding ToDataSourceResult

Using server filtering is rather easy with Kendo UI MVC, unless your new to it.  Then that first couple hours is pretty frustrating untill you find the right article, information and samples.  I have very few gripes with Telerik and Kendo UI but they do have a problem with the Kendo UI Demos.  They do not include enough sample code from controllers and web services.  So hopefully this will help out those newbies to KendoUI.

When would I use this?

Server-side Filtering, Paging, and Sorting with the Grid or ListView.

How to use it?

When would I use this?  Server-side Filtering with the Grid or ListView.

First thing you want to do is add the following the following using statement to you Controller or API Controller:

using Kendo.Mvc.Extensions;

Now, you can easily take advantage of ToDataSourceResult extension method to convert any IQueryable or IEnumerable to a DataSourceResult object.  It will help you page, filter, sort or group your data using the information provided by the DataSourceRequestObject.

Your AJAX action will take one parameter:  DataSourceRequest.

public ActionResult GetAllOrders([DataSourceRequest] DataSourceRequest request)

Now in the method, you just call ToDataSourceResult on your IQueryable or IEnumerator.  Pretty simple, although you have to be careful when dealing with large volumes of data o make sure your implementation does not return all the data before applying filter, sorting, and paging.

How is ToDataSourceResult being implemented under the hood by Telerik?  C# Extension Methods.  If you need a refresher, check it out here:

If you want to see the implementation, then use JustDecompile and run it against Kendo.MVC.dll.

Which Kendo UI Widgets

This is meant to be used with the following controls:

  • Grid
  • ListView

Other controls like ComboBox and AutoComplete expect just a simple array and will not work with DataSourceResult.

Does this only work with a ORM?

No, it does not.  Note:  Any IQueryable or IEnumerable so LINQ, List, Dictionary, etc…

Let’s dive into a simple example

In my sample, I have a Domain POCO Class called Order.

public class Order
{
 public int Id { get; set; }
 public string LastName { get; set; }

 public decimal TotalAmount { get; set; }

 public decimal TotalAmountUsd { get; set; }

 public string OrderCurrencyCode { get; set; }

 public string OrderCurrencyCultureCode { get; set; }

 public DateTime OrderDate { get; set; }
}

Then I have a Domain Class that contains a list of Orders.  This has data stubbed out for now.

public class Orders
{
   private readonly List orders = new List();
   private int nextId = -1;

   public Orders()
   {
     Add(new Order() { LastName = "Federer", TotalAmount = 1023.23m, TotalAmountUsd = 400.23m, OrderCurrencyCode = "SEK", OrderCurrencyCultureCode = "sv-SE", OrderDate = DateTime.UtcNow.AddDays(-1) });
     // Add lots of data here........
     Add(new Order() { LastName = "Aikemo", TotalAmount = 1023.23m, TotalAmountUsd = 400.23m, OrderCurrencyCode = "JPY", OrderCurrencyCultureCode = "ja-JP", OrderDate = DateTime.UtcNow.AddDays(-8) });
 }

 public Order Add(Order item)
 {
   if (item == null)
   {
     throw new ArgumentNullException("item");
   }
   item.Id = nextId++;
   orders.Add(item);
   return item;
 }

 public List GetAll()
 {
  return orders;
 }
}

Then, I have a ViewModel which looks just like my Order class.
public class OrderViewModel
{
 public int Id { get; set; }
 public string LastName { get; set; }

 public decimal TotalAmount { get; set; }

 public decimal TotalAmountUsd { get; set; }

 public string OrderCurrencyCode { get; set; }

 public string OrderCurrencyCultureCode { get; set; }

 public DateTime OrderDate { get; set; }
}

In the controller, I have the following Action (Note:  My controller is importing  Kendo.Mvc.Extensions):

[HttpPost]
public ActionResult GetAllOrders([DataSourceRequest] DataSourceRequest request)
 {
   Orders orders = new Orders();
   List orderList = orders.GetAll();
   DataSourceResult result = orderList.ToDataSourceResult(request, 
     order => new OrderViewModel 
     { 
       Id = order.Id, LastName = order.LastName,
       OrderCurrencyCode = order.OrderCurrencyCode, OrderCurrencyCultureCode = order.OrderCurrencyCultureCode,
       TotalAmount = order.TotalAmount, TotalAmountUsd = order.TotalAmountUsd,
       OrderDate = order.OrderDate
     }
   );

   return Json(result);
 }

The call to ToDataSourceResult is taking the request information and using that to filter, sort, etc the results and transform them into the ViewModel.  Pretty simple and straightforward.

Finally, in your Razor view you can call your AJAX method which will easily page, filter, sort, etc…

@(Html.Kendo().Grid()
 .Name("grid")
 .HtmlAttributes(new { style = "width:700px" })
 .Columns(columns =>
 {
 columns.Bound(p => p.Id).Title("Order Id");
 columns.Bound(p => p.LastName).Title("Customer");
 columns.Bound(p => p.TotalAmount).Title("Total Amount");
 columns.Bound(p => p.TotalAmountUsd).Title("Total Amount (USD)");
 columns.Bound(p => p.OrderDate).Title("Ordered On");
 })
 .Pageable()
 .Sortable()
 .Scrollable()
 .Filterable()
 .DataSource(dataSource => dataSource
 .Ajax()
 .PageSize(5)
 .Read(read => read.Action("GetAllOrders", "Orders"))
 )
)

Now the user can sort, page, filter, …

Grid Filtering
Grid Filtering

KendoUI Resources

Here are a couple helpful resources from Telerik:

 

Conclusion

This is pretty simple once you get the basics down so good luck coding up your solutions with it.

KendoUI: Formatting Dates, Numbers, Currency and Custom Formats

Ever need to format text in JavaScript?  Ok, dumb question.  Everyone has needed that functionality.  Well, if you use KendoUI, Telerik provides a pretty awesome framework for this.  There is a toString method which is documented here.

This method takes three parameters:

  • value –> the Date or Number to be formatted.
  • format –> string to format.
    • n –>  Format as a Number.  If you include a number after the n, then this will denote the number of decimal places.
    • p –> Format as a Percent
    • c –> format as the Currency including the symbol
    • date format like ‘yyyy/MM/dd’
    • Custom Formatter like ’00##’
  • culture –> the name of the culture which should be used to format the value.  If this parameter is not supplied, then the default culture is used

Now, this is pretty AWESOME.  Lets say you need to display a value in multiple currencies and formatted for each currency.

In your page, you need to register each KendoUI culture script.  For this example, let’s reference the following:

<script src="http://cdn.kendostatic.com/2013.2.716/js/cultures/kendo.culture.de-DE.min.js"></script>
<script src="http://cdn.kendostatic.com/2013.2.716/js/cultures/kendo.culture.sv-SE.min.js"></script>
<script src="http://cdn.kendostatic.com/2013.2.716/js/cultures/kendo.culture.en-US.min.js"></script>

You also need a reference to kendo.core.js.

Now, that you are referencing the cultures you need,  it is pretty easy to format your dates and string in JavaScript.

For instance:

kendo.toString(1234.23, ‘c’, ‘de-DE’)  –>   1.234,23 €

kendo.toString(1234.23, ‘c’, ‘sv-SE’)  –> 1.234,23 kr

kendo.toString(1234.23, ‘c’, ‘en-US’)  –> $1,234.23

This is pretty slick.  You can format the currency based on a culture which comes in very handy in some business application where you might need to display multiple currencies on the same page.

Other places to use this:

  • In a ClientTemplate on a column on a Grid so you could format one column for Swiss Franc then have another column for US Dollars.
  • For formatting date time when displaying multiple dates for more than one culture
  • Allowing the user to pick a culture and dynamically update the formats of the numbers, dates, etc without reposting the page.