title: Getting checked nodes from a dynamically populated jsTree in MVC
body: <p>(<strong>Updated: see bottom of post</strong>)
  I'm new to jstree (and JS) and trying to use it in a MVC4 scenario where the nodes must be dynamically loaded from some repository. I have managed to get this working, but I'm facing some difficulties in determining which nodes were effectively selected by the user, as this depends on the UI status. Let's start from the MVC controller's method which returns nodes and is called by JS. Its code looks like this:</p>
  
  <pre><code>
  public JsonResult GetTreeNodes(string id)
  {
          // selectedIds contains some pre-selected IDs...
  
      IEnumerable nodes = _repository.GetChildNodes(id);
      return Json(
          (from n in nodes
           select new
                      {
                          id = n.Id,
                          data = n.Value,
                          attr = new
                                     {
                                         id = n.Id,
                                         selected = selectedIds.Any(s => s == n.Id)
                                     },
                          state = "closed"
                      }).ToArray(), JsonRequestBehavior.AllowGet);
  }
  </code></pre>
  
  <p>This works fine and returns the child nodes of the node with the specified ID, in the form jstree expects them.</p>
  
  <p>My view has a div for the tree, and a hidden field for including the checked nodes. When the form is submitted, a JS function retrieves the checked nodes and stores them into a hidden field, so that the MVC action can receive them. The view is like:</p>
  
  <pre><code>    ...
  &lt;section&gt;
      @using (Html.BeginForm())
      {
          @Html.Hidden("selectedNodes")
          &lt;div id="tree" style="max-width: 70%"&gt;&lt;/div&gt;
          &lt;input id="submit" type="submit" value="Submit" /&gt;
      }
  &lt;/section&gt;
  
  &lt;script type="text/javascript"&gt;
      $(function () {
          fillTree();
          $("form:first").submit(function(e) {
              storeSelected();
          });
      });
  
      function fillTree() {
          $("#tree").jstree({
              checkbox: {
                  real_checkboxes: true,
                  checked_parent_open: true
              },
              plugins: ["themes", "json_data", "ui", "checkbox"],
              json_data: {
                  "ajax": {
                      "type": 'GET',
                      "url": function(node) {
                          var url;
                          if (node == -1) {
                              url = "@Url.Action("GetTreeNodes", "Home")";
                          } else {
                              var nodeId = node.attr('id');
                              url = "@Url.Action("GetTreeNodes", "Home")" + "?id=" + nodeId;
                          }
  
                          return url;
                      },
                      "success": function (newData) {
                          return newData;
                      }
                  }
              },
              animation: 200
          }).bind("open_node.jstree", function(event, data) {
              $("#tree").jstree("check_node", "li[selected=selected]");
          });
      }
  
      function storeSelected() {
          var checkedIds = [];
          $("#tree").jstree("get_checked", null, true).each(function() {
              checkedIds.push(this.id);
          });
          $("#selectedNodes").val(checkedIds.join(","));
      }
  &lt;/script&gt;
  </code></pre>
  
  <p>As you can see, when the user submits the form the storeSelected function uses jstree get_checked function to retrieve the checked nodes. The problem is that the nodes here are dynamically loaded, so this works only with reference to the nodes which are effectively present in the page when the user submits the form. If I expand a branch, check and uncheck some nodes, and then contract the same branch before submitting, I will get an empty list of checked ID's just because the list item elements representing the nodes in the tree UI are not present in the page.</p>
  
  <p>So what would be the best strategy for dealing with this? I was thinking of reacting to the checked/unchecked event, and keeping track of what the user does, so that I can know which nodes he checked regardless of the UI state. So I'dd add another bind like this:</p>
  
  <pre><code>bind("check_node.jstree uncheck_node.jstree", function(event, data) {
              var id = data.rslt.obj[0].id;
              addToCheckedOrUnchecked(id, event.type == "check_node"); });
  </code></pre>
  
  <p>and implement the addToCheckedOrUnchecked function so that it can store a list of ID's the users checks or unchecks. Of course when adding an ID to the checked list I'd have to remove it from the unchecked list, if present, and vice-versa. I also have to use some persistence mechanism to preserve these lists between different AJAX calls: I suppose session data or cookies, according to the browser capabilities.</p>
  
  <p>Please note that opening all the branches before submitting is not an option, as this would imply querying the server branch by branch, and some of these trees are large, while I want to preserve bandwidth and responsivity.</p>
  
  <p>My first issue concerns the check and uncheck node event: they do not seem to be documented (I found them googling around), and it seems that not all the browsers handle them correctly. For instance, in IE9 the event do not seem to fire at all, while they do in FF. And at any rate, could you suggest more efficient approaches to this problem? It seems this is a lot of code for a relatively simple task, and as I'm going to use several trees in different pages I'll probably have to create an HTML helper to avoid redundancy in my MVC project.</p>
  
  <h2>Update #1</h2>
  
  <p>By trial and error I have further enhanced the JS code, but I'm still facing some issues. I must add that I need to synch the received branch with not-yet-submitted user edits: if I open a branch, uncheck a (preselected) item and check another one, then close the branch and reopen it, this does not trigger a new ajax call, so I need to handle the open_node event to be able to modify the nodes checkboxes before displaying them. Anyway, the tree seems to ignore my synching code. First of all, the function used to add a node ID to the checked or unchecked IDs list, mantained in session storage (or in a cookie):</p>
  
  <pre><code>
  function addToCheckedOrUnchecked(id, checked) {
      // get arrays of IDs from storage
      var listChecked = getFromStore("checkedNodes");
      if (listChecked === null) {
          listChecked = [];
      }
      var listUnchecked = getFromStore("uncheckedNodes");
      if (listUnchecked === null) {
          listUnchecked = [];
      }
  
      // if checking, add ID to checked and remove from unchecked if present
      if (checked) {
          if ($.inArray(id, listChecked) == -1) {
              listChecked.push(id);
          }
          var i = $.inArray(id, listUnchecked);
          if (i > -1) {
              listUnchecked.splice(i, 1);
          }
          // else add ID to unchecked and remove from checked if present
      } else {
          if ($.inArray(id, listUnchecked) == -1) {
              listUnchecked.push(id);
          }
          var j = $.inArray(id, listChecked);
          if (j > -1) {
              listChecked.splice(j, 1);
          }
      }
  
      // update storage
      putInStore("checkedNodes", listChecked);
      putInStore("uncheckedNodes", listUnchecked);
  }
  </code></pre>
  
  <p>This function simply reads from storage the lists and updates them according to the id received and whether the id is being checked or unchecked.</p>
  
  <p>Then I added this function:</p>
  
  <pre><code>
  function synchChecks(data) {
      var listChecked = getFromStore("checkedNodes");
      var listUnchecked = getFromStore("uncheckedNodes");
  
          $(data).each(function () {
              var id = $(this).id;
              if ($.inArray(id, listChecked) > -1) {
                  $(this).attr.selected = true;
                  console.log("overridden=1: " + id);
              } else if ($.inArray(id, listUnchecked) > -1) {
                  $(this).attr.selected = false;
                  console.log("overridden=0: " + id);
              }
          });
  }
  </code></pre>
  
  <p>this gets the lists from storage and overrides the attr.selected value of each node received (in data) so that it is in synch with user edits. Finally I glue everything together in the jstree call:</p>
  
  <pre><code>
  function fillTree() {
      $("#tree").jstree({
          checkbox: {
              real_checkboxes: true,
              checked_parent_open: true
          },
          plugins: ["themes", "json_data", "ui", "checkbox"],
          json_data: {
              "ajax": {
                  "type": 'GET',
                  "url": function(node) {
                      var url;
                      if (node == -1) {
                          url = "@Url.Action("GetTreeNodes", "Home")";
                      } else {
                          var nodeId = node.attr('id');
                          url = "@Url.Action("GetTreeNodes", "Home")" + "?id=" + nodeId;
                      }
  
                      return url;
                  },
                  "success": function (newData) {
                      synchChecks(newData);
                      return newData;
                  }
              }
          },
          animation: 200
      }).bind("open_node.jstree", function (event, data) {
          openingNode = true;
  
          var tree = $("#tree");
          tree.jstree("check_node", "li[selected=selected]");
  
          // get and synch children
          var listChecked = getFromStore("checkedNodes");
          var listUnchecked = getFromStore("uncheckedNodes");
  
          $(data.inst._get_children(data.rslt.obj[0])).each(function () {
              var id = $(this).attr("id");
              if ($.inArray(id, listChecked) > -1) {
                  $(this).addClass("jstree-checked");
                  $(this).removeClass("jstree-unchecked");
              } else if ($.inArray(id, listUnchecked) > -1) {
                  $(this).addClass("jstree-unchecked");
                  $(this).removeClass("jstree-checked");
              }
          });
  
          openingNode = false;
      }).bind("check_node.jstree uncheck_node.jstree", function (event, data) {
          if (!openingNode) {
              var id = data.rslt.obj[0].id;
              addToCheckedOrUnchecked(id, event.type == "check_node");
          }
      });
  }
  </code></pre>
  
  <p>On ajax call success I call synchChecks to synchronize the received nodes (not yet updated as nothing has been posted to the server) with user edits. Also, on open_node I retrieve the checked and unchecked IDs lists and use them to override the check state of each child node of the node being opened. Finally, when the user checks or unchecks a node, I call addToCheckedOrUnchecked to update my lists of IDs.</p>
  
  <p>Anyway, this does not seem to work. For instance, if I open a branch, uncheck a preselected node, close it, and reopen it, I can see from the debugger that the list IDs are as expected (the unchecked list has the ID of the node I unchecked), and that the id retrieved from the li in the loop is ok, yet my edits on li classes seem to have no effect and I see the branch with checks coming from the server. So, assuming that there is no other viable approach, do you have any hint for letting this work as expected?</p>