Bootstrap Tree View in ASP.Net MVC

Introduction

This article introduces how to create a parent / child tree view in ASP.NET MVC using Bootstrap and jQuery. It is a practical approach so we create an example in which we will create a parent object. The parent object will have associated child objects.

Using the Code

We create two classes, one is AuthorViewModel and another is BookViewModel. AuthorViewModel is the main class that has an association with the BookViewModel class. In other words each AuthorViewModel class object has a list of BookViewModel class objects. The following is the code snippet for the BookViewModel class.

  1. namespace TreeView.Models   
  2. {  
  3.     public class BookViewModel   
  4.     {  
  5.         public long Id   
  6.         {  
  7.             get;  
  8.             set;  
  9.         }  
  10.         public string Title   
  11.         {  
  12.             get;  
  13.             set;  
  14.         }  
  15.         public bool IsWritten   
  16.         {  
  17.             get;  
  18.             set;  
  19.         }  
  20.     }  
  21. }  
The following is the code snippet for the AuthorViewModel class.
  1. using System.Collections.Generic;  
  2.   
  3. namespace TreeView.Models {  
  4.     public class AuthorViewModel   
  5.     {  
  6.         public AuthorViewModel()   
  7.         {  
  8.             BookViewModel = new List < BookViewModel > ();  
  9.         }  
  10.         public int Id   
  11.         {  
  12.             get;  
  13.             set;  
  14.         }  
  15.         public string Name   
  16.         {  
  17.             get;  
  18.             set;  
  19.         }  
  20.         public bool IsAuthor   
  21.         {  
  22.             get;  
  23.             set;  
  24.         }  
  25.         public IList < BookViewModel > BookViewModel   
  26.         {  
  27.             get;  
  28.             set;  
  29.         }  
  30.     }  
  31. }  
Now we create a controller “HomeController” that has two action methods for both GET and POST requests. The action method's name is “Index”. The Get request action method returns a tree view in the UI whereas the POST request method gets the posted data from the UI. The following is the code snippet for HomeController.
  1. using System.Collections.Generic;  
  2. using System.Linq;  
  3. using System.Web.Mvc;  
  4. using TreeView.Models;  
  5.   
  6. namespace TreeView.Controllers  
  7. {  
  8.     public class HomeController : Controller  
  9.     {  
  10.         [HttpGet]  
  11.         public ActionResult Index()  
  12.         {  
  13.             List<AuthorViewModel> model = new List<AuthorViewModel>();  
  14.   
  15.             AuthorViewModel firstAuthor = new AuthorViewModel  
  16.             {  
  17.                 Id = 1,  
  18.                 Name = "John",  
  19.                 BookViewModel = new List<BookViewModel>{  
  20.                     new BookViewModel{  
  21.                         Id=1,  
  22.                         Title = "JQuery",  
  23.                         IsWritten = false  
  24.                     }, new BookViewModel{  
  25.                         Id=1,  
  26.                         Title = "JavaScript",  
  27.                         IsWritten = false  
  28.                     }  
  29.                 }  
  30.             };  
  31.   
  32.             AuthorViewModel secondAuthor = new AuthorViewModel  
  33.             {  
  34.                 Id = 2,  
  35.                 Name = "Deo",  
  36.                 BookViewModel = new List<BookViewModel>{  
  37.                     new BookViewModel{  
  38.                         Id=3,  
  39.                         Title = "C#",  
  40.                         IsWritten = false  
  41.                     }, new BookViewModel{  
  42.                         Id=4,  
  43.                         Title = "Entity Framework",  
  44.                         IsWritten = false  
  45.                     }  
  46.                 }  
  47.             };  
  48.             model.Add(firstAuthor);  
  49.             model.Add(secondAuthor);  
  50.             return View("Index", model);  
  51.         }  
  52.   
  53.         [HttpPost]  
  54.         public ActionResult Index(List<AuthorViewModel> model)  
  55.         {  
  56.             List<AuthorViewModel> selectedAuthors = model.Where(a => a.IsAuthor).ToList();  
  57.             List<BookViewModel> selectedBooks = model.Where(a => a.IsAuthor)  
  58.                                                 .SelectMany(a => a.BookViewModel.Where(b => b.IsWritten)).ToList();  
  59.             return View();  
  60.         }  
  61.     }  
  62. }  
The preceding code shows how books are associated with an author in the GET action method and how to get a selected tree node (authors and books) in the POST request. 

Bootstrap CSS is already added to the application but we write a new custom CSS for the tree view design. The following is the code snippet for the tree CSS.

  1. .tree li {  
  2.     margin: 0px 0;    
  3.     list-style-type: none;  
  4.     position: relative;  
  5.     padding: 20px 5px 0px 5px;  
  6. }  
  7.   
  8. .tree li::before{  
  9.     content: '';  
  10.     position: absolute;   
  11.     top: 0;  
  12.     width: 1px;   
  13.     height: 100%;  
  14.     right: auto;   
  15.     left: -20px;  
  16.     border-left: 1px solid #ccc;  
  17.     bottom: 50px;  
  18. }  
  19. .tree li::after{  
  20.     content: '';  
  21.     position: absolute;   
  22.     top: 30px;   
  23.     width: 25px;   
  24.     height: 20px;  
  25.     right: auto;   
  26.     left: -20px;  
  27.     border-top: 1px solid #ccc;  
  28. }  
  29. .tree li a{  
  30.     display: inline-block;  
  31.     border: 1px solid #ccc;  
  32.     padding: 5px 10px;  
  33.     text-decoration: none;  
  34.     color: #666;      
  35.     font-family: 'Open Sans',sans-serif;  
  36.     font-size: 14px;  
  37.     font-weight :600;  
  38.     border-radius: 5px;  
  39.     -webkit-border-radius: 5px;  
  40.     -moz-border-radius: 5px;  
  41. }  
  42.   
  43. /*Remove connectors before root*/  
  44. .tree > ul > li::before, .tree > ul > li::after{  
  45.     border: 0;  
  46. }  
  47. /*Remove connectors after last child*/  
  48. .tree li:last-child::before{   
  49.       height: 30px;  
  50. }  
  51.   
  52. /*Time for some hover effects*/  
  53. /*We will apply the hover effect the the lineage of the element also*/  
  54. .tree li a:hover, .tree li a:hover+ul li a {  
  55.     background: #dd4814; color: #ffffff; border: 1px solid #dd4814;  
  56. }  
  57. /*Connector styles on hover*/  
  58. .tree li a:hover+ul li::after,   
  59. .tree li a:hover+ul li::before,   
  60. .tree li a:hover+ul::before,   
  61. .tree li a:hover+ul ul::before{  
  62.     border-color:  #dd4814;  
  63. }  
  64. .tree-checkbox{  
  65.     margin :4px !important;  
  66. }  
  67.   
  68.    
  69. .tree:before {  
  70.     border-left:  1px solid #ccc;  
  71.     bottom: 16px;  
  72.     content: "";  
  73.     display: block;  
  74.     left: 0;  
  75.     position: absolute;  
  76.     top: -21px;  
  77.     width: 1px;  
  78.     z-index: 1;  
  79. }  
  80.   
  81. .tree ul:after {  
  82.     border-top: 1px solid #ccc;  
  83.     content: "";  
  84.     height: 20px;  
  85.     left: -29px;  
  86.     position: absolute;  
  87.     right: auto;  
  88.     top: 37px;  
  89.     width: 34px;  
  90. }  
  91. *:before, *:after {  
  92.     box-sizing: border-box;  
  93. }  
  94. *:before, *:after {  
  95.     box-sizing: border-box;  
  96. }  
  97. .tree {  
  98.     overflow: auto;  
  99.     padding-left: 0px;  
  100.     position: relative;  
  101. }  
Now we create an Index view that renders in the browser and shows the tree view for the author and book. The following is the code snippet for the Index view.
  1. @model List  
  2. <TreeView.Models.AuthorViewModel>  
  3. @section head{  
  4. @Styles.Render("~/Content/css/tree.css")  
  5. }  
  6.   
  7.   
  8.     <div class="panel panel-primary">  
  9.         <div class="panel-heading panel-head">Author Book Tree View</div>  
  10.         <div id="frm-author" class="panel-body">  
  11. @using (Html.BeginForm())  
  12. {  
  13.   
  14.             <div class="tree">  
  15. @for (int i = 0; i < Model.Count(); i++)  
  16. {  
  17.   
  18.                 <ul>  
  19.                     <li>  
  20.                         <a href="#">  
  21. @Html.CheckBoxFor(model => model[i].IsAuthor, new { @class = "tree-checkbox parent", @id = @Model[i].Id })  
  22.   
  23.                             <label for=@i>  
  24.                                 <strong>Author:</strong>  
  25. @Html.DisplayFor(model => model[i].Name)  
  26.   
  27.                             </label>  
  28.                         </a>  
  29.                         <ul>  
  30. @for (int j = 0; j < Model[i].BookViewModel.Count(); j++)  
  31. {  
  32. int k = 1 + j;  
  33. @Html.HiddenFor(model => model[i].BookViewModel[j].Id)  
  34.   
  35.                             <li>  
  36.                                 <a href="#">  
  37. @Html.CheckBoxFor(model => model[i].BookViewModel[j].IsWritten, new { @class = "tree-checkbox node-item", @iid = i + "" + j })  
  38.   
  39.                                     <label for=@i@j>  
  40.                                         <strong>Book @(k):</strong> @Html.DisplayFor(model => model[i].BookViewModel[j].Title)  
  41.                                     </label>  
  42.                                 </a>  
  43.                             </li>  
  44.   
  45. }  
  46.   
  47.                         </ul>  
  48.                     </li>  
  49.                 </ul>  
  50. }  
  51.   
  52.             </div>  
  53.             <div class="form-group">  
  54.                 <div class="col-lg-9"></div>  
  55.                 <div class="col-lg-3">  
  56.                     <button class="btn btn-success" id="btnSubmit" type="submit">  
  57. Submit  
  58. </button>  
  59.                 </div>  
  60.             </div>  
  61. }  
  62.   
  63.         </div>  
  64.     </div>  
  65.   
  66. @section scripts{  
  67. @Scripts.Render("~/Scripts/tree.js")  
  68. }  
Thereafter we create an important part of this example. We create the JavaScript file tree.js with the following code.
  1. (function($)   
  2. {  
  3.     function Tree() {  
  4.         var $this = this;  
  5.   
  6.         function treeNodeClick()   
  7.         {  
  8.             $(document).on('click''.tree li a input[type="checkbox"]', function() {  
  9.                 $(this).closest('li').find('ul input[type="checkbox"]').prop('checked', $(this).is(':checked'));  
  10.             }).on('click''.node-item', function() {  
  11.                 var parentNode = $(this).parents('.tree ul');  
  12.                 if ($(this).is(':checked')) {  
  13.                     parentNode.find('li a .parent').prop('checked'true);  
  14.                 } else {  
  15.                     var elements = parentNode.find('ul input[type="checkbox"]:checked');  
  16.                     if (elements.length == 0) {  
  17.                         parentNode.find('li a .parent').prop('checked'false);  
  18.                     }  
  19.                 }  
  20.             });  
  21.         };  
  22.   
  23.         $this.init = function() {  
  24.             treeNodeClick();  
  25.         }  
  26.     }  
  27.     $(function() {  
  28.         var self = new Tree();  
  29.         self.init();  
  30.     })  
  31. }(jQuery))  
As in the preceding JavaScript code, the create tree view has the following features.
  1. When we select an author parent node then all the associated books will be selected.
  2. When a book is selected the associated parent author will be selected automatically.
  3. When all child book nodes are selected for a parent author node then the parent node will be selected.
  4. When the parent node is unselected then the child books will be automatically unselected.

Figure 1 shows the parent child (author-book) tree view.

Figure 1: Author Book Tree View