jsTree tri-state checkboxes with ASP.NET MVC revisited

My original jsTree checkboxes post is still my most popluar, yet it is 18 months old and no longer works with the latest version of jsTree (pre 1.0). Since that post is so popular I thought I should update it.

My first surprise was how much the jsTree guys changed their API between releases – I was only going from version 0.9.9 to version 1.0, but  I had to practically start from scratch.

So, without further ado:

jsTree is a jQuery plugin for creating a treeviews, and jsTree’s checkbox plugin allows you to create a treeview with tri-state checkboxes, like so:

Notice how “Origination” appears half-checked because only some of its children are checked.

Getting started

For this demo I am using ASP.NET MVC 3 and jsTree pre 1.0 fixed. Let’s start with a new “ASP.NET MVC 3 Web Application”, choose Internet Application, name it jsTreeDemo, and add the required jsTree files to our solution:

In the View, create a div which you want to become a treeview. I’ll name mine demoTree. Also add references to the jsTree script, and add a new index.js file for our custom javascript.

Views/Home/Index.cshtml

@{
 ViewBag.Title = "Home Page";
}

<h2>@ViewBag.Message</h2>

<div id="demoTree">

</div>
<script type="text/javascript">
treeModel = [{"data":"Confirm Application","attr":{"id":"10"},"children":null},{"data":"Things","attr":{"id":"20"},"children":[{"data":"Thing 1","attr":{"id":"21"},"children":null},{"data":"Thing 2","attr":{"id":"22"},"children":null},{"data":"Thing 3","attr":{"id":"23"},"children":null},{"data":"Thing 4","attr":{"id":"24"},"children":[{"data":"Thing 4.1","attr":{"id":"241"},"children":null},{"data":"Thing 4.2","attr":{"id":"242"},"children":null},{"data":"Thing 4.3","attr":{"id":"243"},"children":null}]}]},{"data":"Colors","attr":{"id":"40"},"children":[{"data":"Red","attr":{"id":"41"},"children":null},{"data":"Green","attr":{"id":"42"},"children":null},{"data":"Blue","attr":{"id":"43"},"children":null},{"data":"Yellow","attr":{"id":"44"},"children":null}]}];
</script>
<script src="@Url.Content("~/Scripts/jquery.jstree.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/index.js")" type="text/javascript"></script>

Scripts/index.js

/// <reference path="jquery-1.7.1-vsdoc.js" />

$(function () {
   $("#demoTree").jstree({
      json_data : {
         data : treeModel
      },
      plugins : ["themes", "json_data", "ui", "checkbox"]
 });
});

Briefly, jstree has a number of ways of poplulating the tree with data – in my previous jsTree post I was populating the tree via AJAX but for now the tree is populated via hardcoded JSON (using the built-in "json_data" plugin). Don’t forget to specify the "checkbox" plugin too.

Populate the tree from a viewmodel

Just like last time, let’s create a viewmodel for creating the tree structure.

Models/JsTreeModel.cs

namespace jsTreeDemo.Models
{
  public class JsTreeModel
  {
    public string data;
    public JsTreeAttribute attr;
    public JsTreeModel[] children;
  }
  
  public class JsTreeAttribute
  {
    public string id;
    public bool selected;
  }
}

Now let’s change our controller to create a viewmodel, serialize it to JSON, and pass that string to our View.
Controllers/HomeController.cs

public ActionResult Index()
{
    ViewBag.Message = "Welcome!";
            
    var model = GetTreeData();
            
    string jsonModel = new JavaScriptSerializer().Serialize(model);
            
    return View("Index", "_Layout", jsonModel);
}

private JsTreeModel[] GetTreeData()
{
var tree = new JsTreeModel[] 
{
    new JsTreeModel { data = "Confirm Application", attr = new JsTreeAttribute { id = "10", selected = true } },
    new JsTreeModel 
    { 
        data = "Things",
        attr = new JsTreeAttribute { id = "20" },
        children = new JsTreeModel[]
            {
                new JsTreeModel { data = "Thing 1", attr = new JsTreeAttribute { id = "21", selected = true } },
                new JsTreeModel { data = "Thing 2", attr = new JsTreeAttribute { id = "22" } },
                new JsTreeModel { data = "Thing 3", attr = new JsTreeAttribute { id = "23" } },
                new JsTreeModel 
                { 
                    data = "Thing 4", 
                    attr = new JsTreeAttribute { id = "24" },
                    children = new JsTreeModel[] 
                    { 
                        new JsTreeModel { data = "Thing 4.1", attr = new JsTreeAttribute { id = "241" } }, 
                        new JsTreeModel { data = "Thing 4.2", attr = new JsTreeAttribute { id = "242" } }, 
                        new JsTreeModel { data = "Thing 4.3", attr = new JsTreeAttribute { id = "243" } }
                    },
                },
            }
    },
    new JsTreeModel 
    {
        data = "Colors",
        attr = new JsTreeAttribute { id = "40" },
        children = new JsTreeModel[]
            {
                new JsTreeModel { data = "Red", attr = new JsTreeAttribute { id = "41" } },
                new JsTreeModel { data = "Green", attr = new JsTreeAttribute { id = "42" } },
                new JsTreeModel { data = "Blue", attr = new JsTreeAttribute { id = "43" } },
                new JsTreeModel { data = "Yellow", attr = new JsTreeAttribute { id = "44" } },
            }
    }
};

return tree;
}

Views/Index.cshtml

@model string
       
@{
    ViewBag.Title = "Home Page";
}

...

<script type="text/javascript">
    treeModel = @Html.Raw(Model);
</script>

Determining which items are checked when posting

Let’s put our tree inside a <form> and submit it.
Views/Index.cshtml

...
@using (Html.BeginForm("Results", "Home", FormMethod.Post))
{ 
    <div id="demoTree">
    </div>
    <br />
    <div>
        <input type="submit" value="Submit" id="btnSubmit" />
    </div>
} 
...

And now let’s add the Results method to the HomeController

Controllers/HomeController.cs

[HttpPost]
public ActionResult Results(FormCollection form)
{
    return View(form);
}

and the View

Views/Home/Results.cshtml

@model FormCollection

@{
    ViewBag.Title = "Results";
}

<h2>Results</h2>
<p>
    You chose:
    @foreach (var item in Model.AllKeys)
    {
        @Model[item]@: 
    } 
</p>

<p>
    @Html.ActionLink("Home", "Index")
</p>

If you press the Submit button, nothing will happen as nothing is passed through in the FormCollection to Results() in HomeController. Despite appearances, jsTree doesn’t by default render any HTML <input>s for the checkboxes. But it’s easy enough to tell it to render them using the real_checkboxes flag:

Scripts/index.js

$(function () {
    $("#demoTree").jstree({
        json_data: {
            data: treeModel
        },
        checkbox: {
            real_checkboxes: true,
            real_checkboxes_names: function (n) {
                return [("check_" + (n[0].id || Math.ceil(Math.random() * 10000))), n[0].id]
            }
        },
        plugins: ["themes", "json_data", "ui", "checkbox"]
    });
});

Now when we submit our form, we should see the ids of the values which were checked:

What about telling the tree to pre-check some items?

So how do we render the tree with some items already checked? Notice how we added “public bool selected” to JsTreeAttribute? This doesn’t do anything as far as the checkboxes are concerned, but it does add a custom property called “selected” to each node’s <li>. We can use that to tell the jsTree to check the given node, by binding to the ‘loaded.jstree’ event.

Scripts/index.js

$(function () {
    $("#demoTree").jstree({
        json_data: {
            data: treeModel
        },
        checkbox: {
            real_checkboxes: true,
            real_checkboxes_names: function (n) {
                return [("check_" + (n[0].id || Math.ceil(Math.random() * 10000))), n[0].id]
            }
        },
        plugins: ["themes", "json_data", "ui", "checkbox"]
    }).bind("loaded.jstree", function (event, data) {
        $('#demoTree').jstree('check_node', 'li[selected=selected]');
    });
});

You’ll see that the nodes that are marked with selected=true in GetTreeData() are now pre-checked when you first load the page.

Here’s a link to the solution (VS 2010).

About these ads

28 thoughts on “jsTree tri-state checkboxes with ASP.NET MVC revisited

  1. HI. Great demo.
    I have a slight issue…new to JsTree and i am using this in aspx (project demand) with the lastest 1.7.1 in MVC 4, i can get the tree to display however i cannot get the branch lines or images to display, when i select the tree in the page i can see that they appear, but it seems the images are missing.

    I have added the images folder as per your solution, i just cant see what i am missing?
    Thanks

    • You may need to specify the url of treeview.css in your script.
      .jstree({“theme” : {“url”: “urlhere”} })

  2. How can i also get the text of the selected nodes? This only writes the id….

    @foreach (var item in Model.AllKeys)
    {
    @Model[item] @:
    }

    Best regards,
    Manuel

  3. Is it also possible to extend the class to customize the look of the treeview?

    public class JsTreeAttribute
    {
    public string id;
    public bool selected;
    //new
    public bool showCheckBox;
    public bool enabled;
    public bool checkAffectParent;
    public bool checkAffectChild;
    public bool uncheckAffectParent;
    public bool uncheckAffectChild;
    }

    Best Regards,

    Manuel

  4. How to make same jstree using the data from mvc3 database table using linq to sql instead of taking hand-coded data for jstree.
    Thanks in advance.

  5. Nice. Helped me a lot. Just one small thing: in your article the GetTreeData method is missing the return statement.

      • Hi, thanks for this plgiun,I modified it somewhat so that could have multiple selected checkboxes behaving like a radio (once you check at least one, you cannot uncheck) so that you have all 4 circumstances “checked”normal radio : zero or one, once selected, always onenormal checkboxes : zero or multipleradioCheckboxGroup : the real zero or oneradioMultipleCheckboxGroup : zero or multiple, once selected at least one.to do so I added radioMultipleCheckboxGroup as a copy of radioMultipleCheckboxGroup and changed the x.click to :var boo = false;for (var inti=0;inti<x.length;inti++){if (x[inti].checked==true)boo=true;}if (!boo){return false;}

  6. I’m new to MVC and jQuery (coming from ASP.Net/ExtJS), so I’m probably missing something simple, but I can’t get this to run. It seems as though no matter how I try to include the jquery.jstree.js file, I still get “object does not support this method” on the .jstree() call.

    This is MVC 4 with jquery 1.7.2. Is there something I need to do to enable plugins with jQuery?

  7. Your sample checkbox tree works fine but i’m implementing the checkbox jstree in my project by fetching the data dynamically. All the nodes are displaing the checkbox and it also sends the id as required. But my problem is that i cannot precheck the checkbox. I’ve changed the selected value to true just to see if it works but nothing gets prechecked.
    Can you please point me where i might be wrong??

  8. Found the solution to my previous problem:) But can we precheck the nodes using html_data instead of json_data?? When i pass the model to the view. It shows the tree with checkbox but it is not prechecked!!

  9. Hello, first of all great post, you saved me a tons of time and code.

    I’m using your example to develop a tree, with some specified functions, and i’m stuck in a part.

    What i need to do is: in the view i have a button that returns more items to the tree thought a webservice, but the create_node function only creates a children, and not children of children. And i don’t want to reload the whole tree to do it.

    Since is your example, is it possible to have an idea in how would you do it? Thanks in advance

  10. I think this example is great, but i noticed that I can get it to run in MVC 3, but not in MVC 4. I even copied the entire Scripts folder to try to fix it. Does anybody know what needs to be changed to get it to work in MVC 4?

    • Hi Chuck

      Try registerting all the jstree related files in the bundle.config,cs file found in app_start. than try to run it,.,it will work for eg

      bundles.Add(new ScriptBundle(“~/bundles/jquery”).Include(
      “~/Scripts/jquery-{version}.js”,
      “~/Scripts/jquery-1.7.1.intellisense.js”,
      “~/Scripts/DataTables-1.9.4/media/js/jquery.dataTables.min.js”,
      “~/Scripts/jstree-1.0/jquery.jstree.js”));

      and in your _layout.html file in the head section add cross check whether these commands exists
      @Styles.Render(“~/Content/css”)
      @Scripts.Render(“~/bundles/jquery”)
      @Scripts.Render(“~/bundles/modernizr”)

  11. How to return the updated data from view to controller. I need to get all updated values of checkboxes in controller so that i can pass to database.Urgetn please reply

    • It’s been a long time since I looked at this, but I think in the Controller you only get a FormCollection of values which are now checked. You won’t know if an item has been unchecked, unless you look it up from the database and compare.

  12. Many many thanks for this. This tree makes my life so much easier

    Is there a property setting that I need to use for it to display the loading indicator? Tree loads instantly in Firefox, but lags a bit in IE

  13. Hi,
    Is it possible that you have a VS project that load the treeview with a SQL table? I am not sure how to load the tree with SQL.

    ID: Name: ParentID:
    1 John
    2 John Jr. 1
    3 Mary 1
    4 J Kid 2

  14. I am having a problem understanding where are these plugins located in your project form index.js script

    $(function () {
    $(“#demoTree”).jstree({
    json_data : {
    data : treeModel
    },
    plugins : [“themes”, “json_data”, “ui”, “checkbox”]
    });
    });

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s