Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC 4 generating a treeview with recursive partial view

I have a partial view in an MVC 4 project, which is strongly typed. It takes an IEnumerable collection of a table of a database. In that table there are IDs, Names, and ParentIDs for storing hierarchical connection between records. The view that calls the partial view is also strongly typed, it takes the whole database as the model, and passes the Categories table to the partial view, as an enumerable collection:

@Html.Partial("_TreeCategories", @Model.Categories.ToList())

And in the partial view, I want to take the root nodes first, so I can extend the whole tree in a recursive way. In the database table, all records are considered as root nodes with a ParentID == null.
So generally, my way to do this would look like:

@model IEnumerable<TreeCollections.OpenAccess.Category>

@if (Model.ToList().Count >= 0)
    {
    @if (Model.ToList()[0].Parent_id == null)
    {
        <text><ul id="navigation"></text>
    }

    @foreach (var node in @Model)
    {
        <li><a href="[email protected]">@node.Name</a>
            @foreach (var subNode in @Model.Where(s => s.Parent_id == node.Id))
            {
                @Html.Partial("_TreeCategories", subNode)
            }
        </li>
    }
    @if (Model.ToList()[0].Parent_id == null)
    {
        </ul>
    }
}

So I check if the first element's ParentID of the Model is null, and if it is, then it should create a < ul> tag with the id "navigation", so the jquery plugin can recognise that it is meant to be a treeview control. Then it creates a list tag with a recursive call within. The recursively called partial view takes the children of the node as the model. And lastly if we arrived to the end of the partial view's rendering, and we are at the "root level", it should write a closing < ul> tag
There are some problems, however. First, at the end, that closing unordered list tag is wrong, VS can't find the matching start tag for that. Second, I don't know why, but at the top, I can put the starter < ul> tag in between tags, and I can't do it at the closing tag below. But I'm not sure about these < ul > tags either, I feel those are wrong too.

Please, help me, I'm stuck with this for days now.

like image 757
user2082422 Avatar asked Jul 09 '13 16:07

user2082422


2 Answers

man, you got some wonk going on here. i feel your pain on getting stuck.

see if this floats your boat.

you need a seed value to keep track of what you are looking for in the listing when you do recursion on the same list. it's better to do a parent children mapping in the class, but meh this was fun to do given your structure and should do the trick.

Models

namespace trash.Models
{
    public class Category
    {
        public int ID { get; set; }
        public int? Parent_ID { get; set; }
        public string Name {get; set;}
    }

    public class SeededCategories
    {
        public int? Seed { get; set; }
        public IList<Category> Categories { get; set; }
    }
}

Controller (NOTE: you start the recursion chain by setting the Seed property to null which will pick up all the null parents)

namespace trash.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            IList<trash.Models.Category> categories = new List<trash.Models.Category>();
            categories.Add(new trash.Models.Category { ID = 1, Parent_ID = null, Name = "Top1" });
            categories.Add(new trash.Models.Category { ID = 2, Parent_ID = null, Name = "Top2" });
            categories.Add(new trash.Models.Category { ID = 3, Parent_ID = 1, Name = "Top1Ring1" });
            categories.Add(new trash.Models.Category { ID = 4, Parent_ID = 1, Name = "Top1Ring2" });

            trash.Models.SeededCategories model = new Models.SeededCategories { Seed = null, Categories = categories };
            return View(model);
        }
    }
}

Views Index

@model trash.Models.SeededCategories

Here's a list
@Html.Partial("_TreeCategories", Model)

Partial (your _TreeCategories. NOTE: set the Seed to the current node ID and volia recursion)

@model trash.Models.SeededCategories

@if (Model.Categories.Where(s => s.Parent_ID == Model.Seed).Any())
{
    <ul>
        @foreach (var node in Model.Categories)
        {
            if (node.Parent_ID == Model.Seed)
            {
                trash.Models.SeededCategories inner = new trash.Models.SeededCategories { Seed = node.ID, Categories = Model.Categories };
            <li><a href="[email protected]">@node.Name</a>
                @Html.Partial("_TreeCategories", inner)
            </li>
            }
        }
    </ul>
}
like image 200
hubson bropa Avatar answered Nov 03 '22 16:11

hubson bropa


You can try Shield UI's recursive TreeView for ASP.NET MVC.

It allows you to specify all the TreeView items using a RecursiveDataSource object, which can be setup to retrieve the data for a tree item from a remote endpoint or a local source "lazily", whenever the item is being expanded.

The RecursiveDataSource is a wrapper around a JavaScript DS widget, which introduces the need for some JS code, as well as updating your server code that will provide the data (either implementing a web service, or place the data in a JS variable in your view).

like image 30
Vladimir Georgiev Avatar answered Nov 03 '22 14:11

Vladimir Georgiev