Here, in this article, we're going to extend
JS Tree from JS to MVC and we'll render the HTML tags from the database using custom development in the model.
Basically, we'll create a sample DB that contains main/sub categories with self referencing; then we'll add support for Razor like (@HTML.TreeView); and after, we'll render the HTML tags (<ul> <li>) based on our relations in the database.
Let's get started.
We will proceed with the following sections.
- Getting Started
- Creating the (Code First) model
- Creating TreeView Component
- Representing Data
Getting Started
In this step, we're going to create the MVC application using Visual Studio 2017 with the following steps.
Step1Open Visual Studio 2017 and click on “New Project”.
Figure 1 Start Page of VS 2017Step 2
Select "Web" tab from the left panel and select the “ASP.NET Web Application”.
Figure 2 Creating Web Application in VS 2017
Step 3Select “MVC project template” from the ASP.NET Template Wizard to create MVC app.
Figure 3 MVC Project TemplateThis will create an MVC application. After some changes in default layout page, run the application.
Figure 4 Runing the Project
Creating the (Code First) Model
Now, let's create our Model that handles many to many relationships with self referencing (Categories and unlimited sub categories).
Step 1
Right click on Models folder and select Add->Class and name it (Category).
Figure 5 Adding category model
Step 2
Now, Category Model will contain the follwing peroperties.
Id // Category ID
Name // Category name
Description //Category description
PID // Parent id
- using System;
- using System.Collections.Generic;
- using System.ComponentModel.DataAnnotations.Schema;
- using System.Linq;
- using System.Web;
-
- namespace TreeViewMVC.Models
- {
- public class Category
- {
-
- public int ID { get; set; }
-
-
- public string Name { get; set; }
-
-
- public string Description { get; set; }
-
-
- public int? Pid { get; set; }
- [ForeignKey("Pid")]
- public virtual Category Parent { get; set; }
- public virtual ICollection<Category> Childs { get; set; }
- }
- }
Figure 6 Adding model proprtiesStep 3 Creating Database contextRight click on the project name from Solution Explorer window and choose Add-> class, and name it "context".
Figure 7 Creating ContextStep 4
Now, we have to install Entity Framework to generate our database.
From the top bar in Visual Studio, click on Tools->Nuget Package Manager -> Manage nuGet packges for sulotion. From the "Browse" tab at the top, select Entity Framwork, then from the right pane, select your project and click on "Install" button.
Figure 8 Installing EntityFramework
Step 5
Now, we have to include the Model of (Category) that we created recently along with Entity Framework, and map our Category Model.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Data.Entity;
- using TreeViewMVC.Models;
-
- namespace TreeViewMVC
- {
- public class context: DbContext
- {
- public context()
- //Your connection string in Web.Config File
- :base("ConnectionString")
- {
-
- }
-
- DbSet<Category> Categories { get; set; }
- }
- }
Step 6
Now, let's create our Database and add migrations to it with some example data in seed. From the top bar in VS, click on Tools -> NuGet Package Manager -> Package Manager console.
Figure 9 Opening Nuget package manager consoleNow, let's enable migrations for our Project using
Enable-Migrations command.
After excuting this, EF will create a migration in the root of our project, now, we have to add the migration ( let's call it "init").
Figure 10 Adding init migration
Then, let's add some dummy data into seed. We'll be creating categories and sub categories as the follwing,
- Main Cat1 -> Sub Main Cat1 -> Sub Sub
- Main Cat2
- Main Cat3 -> Sub Main Cat3
From Solution Explorer pane, open up the configuration.cs class in the Migrations folder.
- namespace TreeViewMVC.Migrations
- {
- using System;
- using System.Data.Entity;
- using System.Data.Entity.Migrations;
- using System.Linq;
- using TreeViewMVC.Models;
-
- internal sealed class Configuration : DbMigrationsConfiguration<TreeViewMVC.context>
- {
- public Configuration()
- {
- AutomaticMigrationsEnabled = false;
- }
-
-
-
-
-
- protected override void Seed(TreeViewMVC.context context)
- {
-
- context.Categories.AddOrUpdate(c=>c.Name,
- new Category { ID =1 , Name="Main Cat1" , Pid = null , Description = "Main Cat1" },
- new Category { ID =2 , Name="Sub Main Cat1" , Pid = 1 , Description ="Sub Main Cat1" },
- new Category { ID =3 , Name="Sub Sub" , Pid = 2 , Description ="Sub Sub" },
- new Category { ID =4 , Name="Main Cat2" , Pid = null , Description ="Main Cat2" },
- new Category { ID =5 , Name="Main Cat3" , Pid = null , Description ="Main Cat3" },
- new Category { ID =6 , Name="Sub Main Cat3" , Pid = null , Description ="Sub Main Cat3" }
- );
- }
- }
- }
Now, let's create the Database along with dummy data that we added in seed.
Go to Tools -> NuGet Package Manager -> Package Manager console and type "Update-Database".
Figure 11 Creating the database from Code First using EFLet's see what we got in the database:
Figure 12 Our Database after executing Update-Database
Creating TreeView class
Step 1
Now, we have to develop the TreeView class "component"
From soulotion explorer window, right click on Models folder ->add ->class and let's name it "Tree"
Figure 13 Adding class "Tree" for creating TreeView component
Step 2
Here we go with our component,
The Tree class will create an HTML tree from a recursive collection of items and handle the (X) model in our case "Category" model and generate <ul> <li> items based on relations we added in the seed.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System.Web;
- using System.Web.Mvc;
- using System.Web.UI;
- using System.Web.WebPages;
-
- namespace TreeViewMVC.Models
- {
- public static class TreeViewHelper
- {
-
-
-
- public static TreeView<T> TreeView<T>(this HtmlHelper html, IEnumerable<T> items)
- {
- return new TreeView<T>(html, items);
- }
- }
-
-
-
-
- public class TreeView<T> : IHtmlString
- {
- private readonly HtmlHelper _html;
- private readonly IEnumerable<T> _items = Enumerable.Empty<T>();
- private Func<T, string> _displayProperty = item => item.ToString();
- private Func<T, IEnumerable<T>> _childrenProperty;
- private string _emptyContent = "No children";
- private IDictionary<string, object> _htmlAttributes = new Dictionary<string, object>();
- private IDictionary<string, object> _childHtmlAttributes = new Dictionary<string, object>();
- private Func<T, HelperResult> _itemTemplate;
-
- public TreeView(HtmlHelper html, IEnumerable<T> items)
- {
- if (html == null) throw new ArgumentNullException("html");
- _html = html;
- _items = items;
-
- _itemTemplate = item => new HelperResult(writer => writer.Write(_displayProperty(item)));
- }
-
-
-
-
- public TreeView<T> ItemText(Func<T, string> selector)
- {
- if (selector == null) throw new ArgumentNullException("selector");
- _displayProperty = selector;
- return this;
- }
-
-
-
-
-
- public TreeView<T> ItemTemplate(Func<T, HelperResult> itemTemplate)
- {
- if (itemTemplate == null) throw new ArgumentNullException("itemTemplate");
- _itemTemplate = itemTemplate;
- return this;
- }
-
-
-
-
-
- public TreeView<T> Children(Func<T, IEnumerable<T>> selector)
- {
-
- _childrenProperty = selector;
- return this;
- }
-
-
-
-
- public TreeView<T> EmptyContent(string emptyContent)
- {
- if (emptyContent == null) throw new ArgumentNullException("emptyContent");
- _emptyContent = emptyContent;
- return this;
- }
-
-
-
-
- public TreeView<T> HtmlAttributes(object htmlAttributes)
- {
- HtmlAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
- return this;
- }
-
-
-
-
- public TreeView<T> HtmlAttributes(IDictionary<string, object> htmlAttributes)
- {
- if (htmlAttributes == null) throw new ArgumentNullException("htmlAttributes");
- _htmlAttributes = htmlAttributes;
- return this;
- }
-
-
-
-
- public TreeView<T> ChildrenHtmlAttributes(object htmlAttributes)
- {
- ChildrenHtmlAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
- return this;
- }
-
-
-
-
- public TreeView<T> ChildrenHtmlAttributes(IDictionary<string, object> htmlAttributes)
- {
- if (htmlAttributes == null) throw new ArgumentNullException("htmlAttributes");
- _childHtmlAttributes = htmlAttributes;
- return this;
- }
-
- public string ToHtmlString()
- {
- return ToString();
- }
-
- public void Render()
- {
- var writer = _html.ViewContext.Writer;
- using (var textWriter = new HtmlTextWriter(writer))
- {
- textWriter.Write(ToString());
- }
- }
-
- private void ValidateSettings()
- {
- if (_childrenProperty == null)
- {
- return;
- }
- }
-
-
- public override string ToString()
- {
- ValidateSettings();
- var listItems = new List<T>();
- if (_items != null)
- {
- listItems = _items.ToList();
- }
-
-
- var ul = new TagBuilder("ul");
- ul.MergeAttributes(_htmlAttributes);
- var li = new TagBuilder("li")
- {
- InnerHtml = _emptyContent
- };
- li.MergeAttribute("id", "-1");
-
- if (listItems.Count > 0)
- {
- var innerUl = new TagBuilder("ul");
- innerUl.MergeAttributes(_childHtmlAttributes);
-
- foreach (var item in listItems)
- {
- BuildNestedTag(innerUl, item, _childrenProperty);
- }
- li.InnerHtml += innerUl.ToString();
- }
- ul.InnerHtml += li.ToString();
-
- return ul.ToString();
- }
-
- private void AppendChildren(TagBuilder parentTag, T parentItem, Func<T, IEnumerable<T>> childrenProperty)
- {
-
- if (childrenProperty == null)
- {
- return;
- }
- var children = childrenProperty(parentItem).ToList();
- if (!children.Any())
- {
- return;
- }
-
- var innerUl = new TagBuilder("ul");
- innerUl.MergeAttributes(_childHtmlAttributes);
-
- foreach (var item in children)
- {
- BuildNestedTag(innerUl, item, childrenProperty);
- }
-
- parentTag.InnerHtml += innerUl.ToString();
- }
-
- private void BuildNestedTag(TagBuilder parentTag, T parentItem, Func<T, IEnumerable<T>> childrenProperty)
- {
- var li = GetLi(parentItem);
- parentTag.InnerHtml += li.ToString(TagRenderMode.StartTag);
- AppendChildren(li, parentItem, childrenProperty);
- parentTag.InnerHtml += li.InnerHtml + li.ToString(TagRenderMode.EndTag);
- }
-
- private TagBuilder GetLi(T item)
- {
- var li = new TagBuilder("li")
- {
- InnerHtml = _itemTemplate(item).ToHtmlString()
- };
- Type myType = item.GetType();
- IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
- foreach (PropertyInfo prop in props)
- {
- if (prop.Name.ToLower() == "id")
- li.MergeAttribute("id", prop.GetValue(item, null).ToString());
-
- if (prop.Name.ToLower() == "sortorder")
- li.MergeAttribute("priority", prop.GetValue(item, null).ToString());
- }
- return li;
- }
- }
- }
Represent Data
Now, we're done with TreeView class and our Model, let's start on View.
Step 1We have to include
jstree, which is jquery plugin, that provides interactive trees.
From the top menu, click on Tools -> NuGet Package Manager -> Manage NuGet packages for solution.
Choose "Browse" tab, then type jsTree,
Then, hit on Install button to download JSTree and include it in your project.
Step 2
From Solution Explorer window, expand the Views folder and right click on Home folder to add an Empty View, and name it "TreeView".
Figure 14 Adding an empty view to our project
Now, let's add some code to the our View to display the categories.
- @model IEnumerable<TreeViewMVC.Models.Category>
- @using System.Web.UI.WebControls
- @using TreeViewMVC.Models;
-
- <h2>TreeView</h2>
- <link href="~/Content/jsTree/themes/default/style.min.css" rel="stylesheet" />
- <div class="form-body">
- <div id="jstree">
- @(Html.TreeView(Model)
- .EmptyContent("root")
- .Children(m => m.Childs)
- .HtmlAttributes(new { id = "tree" })
- .ChildrenHtmlAttributes(new { @class = "subItem" })
- .ItemText(m => m.Name)
- .ItemTemplate(
- @<text>
- <a href="@item.Description" desc="@item.Description">@item.Name</a>
- </text>)
- )
- </div>
- </div>
- @section scripts
- {
- <script src="~/Scripts/jsTree3/jstree.min.js"></script>
- <script>
- $(function () {
- var selectedData;
- $('#jstree').jstree({
- "core": {
- "multiple": true,
- "check_callback": false,
- 'themes': {
- "responsive": true,
- 'variant': 'larg',
- 'stripes': false,
- 'dots': false
- }
- },
- "types": {
- "default": {
- "icon": "fa fa-folder icon-state-warning icon-lg"
- },
- "file": {
- "icon": "fa fa-file icon-state-warning icon-lg"
- }
- },
- "plugins": ["dnd", "state", "types", "sort", "checkbox"]
- });
- });
- </script>
- }
Now, we have to define the View "TreeView" in our Controller "Home controller".
- public ActionResult TreeView()
- {
- var db = new context();
- return View(db.Categories.Where(x => !x.Pid.HasValue).ToList());
- }
Result