Efficiently Displaying Hierarchical Data With The jQuery Accordion In Razor Web Pages

A frequent requirement is to display data hierarchically, such as products by category. This article shows how to use LINQ to Objects and the jQuery Accordion to display the result of a database query in such a fashion within Razor Web Pages.

One of the problems that people get into when attempting to present hierarchical data on a web page is to fall into the trap if executing multiple database queries to obtain data. If they want to display a list of products by category, they first obtain all of the categories, and then iterate the categories and retrieve a list of products for each category. The code might look something like this:

@foreach(var category in db.Query("SELECT * FROM Categories")){
    <h3>@category.CategoryName</h3>
    foreach(var product in db.Query("SELECT * FROM Products WHERE CategoryId = @0", category.CategoryId)){
        <div>@product.ProductName</div>
    }
}

This is known as the n + 1 select problem, when 1 select query is executed to get the categories, then n further select queries are executed to get the products for each category. Generally, developers are encouraged to minimise the number of times their application makes calls to a database as this can be a common cause of performance issues. Ideally, you should get your data in one shot and then shape it appropriately. A one shot retrieval of data might result in a structure that looks like this:

Ideally, that should be shaped, so that each category's products are grouped together:

LINQ offers a GroupBy method that does just that - it groups elements of a sequence together based on a specified key. The sequence is the output from the database query, and the key around which items are grouped is the name of the category. The code looks like this:

@{
    var db = Database.Open("Northwind");
    var sql = @"SELECT CategoryName, ProductId, ProductName FROM Categories 
                INNER JOIN Products ON Products.CategoryId = Categories.CategoryId 
                ORDER BY Categories.CategoryId";
    var categories = db.Query(sql).GroupBy(p => p.CategoryName);
}

Here's how that looks to the Visual Studio debugger:

 

Data is grouped around the 8 categories in the Northwind database. Getting this on the web page is a matter of two loops - one that iterates the groups, and another inner loop that iterates the content of the group:

@foreach (var category in categories) {
    <h3>@category.Key</h3>
    <div>
    @foreach (var product in category.OrderBy(p => p.ProductName)) {
        <div>@product.ProductName</div>
    }
    </div>
}

The result is exactly as desired:

However, it is a long list, and might benefit from some UI enhancements like the jQuery accordion. The accordion hides all but one section of structured content until the user is ready to see it. It is available as part of the UI download, and is very easy to deploy to the page. Each section of data needs a header-content pair of elements. The example above has that; the category name appears in an h3 element and the list of products appear in a div. It needs a little modification to work with the accordion - a container and some links:

<div id="accordion">
    @foreach (var category in categories) {
        <h3><a href="#">@category.Key</a></h3>
        <div>
        @foreach (var product in category.OrderBy(p => p.ProductName)) {
            <div>@product.ProductName</div>
        }
        </div>
    }
</div>

You can download the accordion and associated styles or link to one of the CDN hosters, and then specify one command to apply the accordion effect to the div labelled with the id of accordion and its contents:

<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"/>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
<script type="text/javascript">
    $(function(){
        $('#accordion').accordion({ autoHeight: false });
    });
</script>

And finally here's how the result looks:

This purpose of this article was to illustrate how to use the LINQ GroupBy operator to prepare data in such a way that displaying it on a Razor Web Page hierarchically is simple and efficient. Finally, the jQuery Accordion was used to apply some user friendly features to the resulting data.

A GitHub repo is available containing the code as a sample web site

 

Date Posted:
Last Updated:
Posted by:
Total Views to date: 20232

6 Comments

- Keef

Excellent and easy to understand as usual.

- Zeljko

Great article, the only thing I would change in your code is to move the query to the controller and send the prepared object to the view via a customized viewmodel.

- Mike

@Zeljko

In MVC, your suggestion is exactly how it should be done. There are, however, no controllers in Razor Web Pages.

- Zeljko

You're absolutely right. I should have paid more attention to the title of your blog post. Somehow I was under the impression it was an MVC post.

- Maran

Hello Mike,
I can't see the images...

- Mike

@Maran,

Check your firewall rules. They are hosted at livestore.com.
Add your comment

If you have any comments to make about this article, please use this form to do so. Make sure that your comment relates specifically to the article above. More general comments can be posted through the form on the Contact page.

Please note, all comments are moderated, and I end up deleting quite a lot. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Requests to fix your code (post a question to forums.asp.net instead, please)
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam

I do not pass email addresses on to spammers, so a valid one will assist me in responding to you personally if required.

Recent Comments

Lorenzo 3/26/2015 8:21 AM
In response to iTextSharp - Introducing Tables
Hi Mike How can I add padding to all cells in the table? Kind Regards Lorenzo...

Satyabrata Mohapatra 3/25/2015 8:11 AM
In response to How To Send Email In ASP.NET MVC
Great article. Simple and up to the point....

Afzaal Ahmad Zeeshan 3/24/2015 8:17 PM
In response to How To Send Email In ASP.NET MVC
A great way to teach the MVC framework for sending the emails... Also, what I found helpful was the...

Jim H 3/24/2015 2:32 PM
In response to Migrating From Razor Web Pages To ASP.NET MVC 5 - Model Binding And Forms
Thank you. This helps....

wazz 3/22/2015 5:48 AM
In response to Posting Data With jQuery AJAX In ASP.NET Razor Web Pages
great info!!...

rael 3/21/2015 8:53 PM
In response to Getting the identity of the most recently added record
I spent hours trying to figure how to achieve this in C#. This article helped me. Thanks a lot...

Stephen 3/21/2015 8:48 PM
In response to Ajax with Classic ASP using jQuery
This was very helpful, thanks:)...

patrick voes 3/19/2015 10:19 AM
In response to iTextSharp - Introducing Tables
Thank you! very helpfull....

Bigmachini 3/19/2015 6:13 AM
In response to ASP.NET MVC DropDownLists - Multiple Selection and Enum Support
This just made my day, afternoon, evening, night... was looking for a way of doing this without to a...

Bobbyg 3/19/2015 4:14 AM
In response to HTML Helpers For Forms In Razor Web Pages
Nice article. There are bugs in MVC 5 with dropdowns retaining values from other screens and them. I...