Skip to content Skip to sidebar Skip to footer

Cascading Ajax Response To Access Objects That "haschildren" That Are Not Included In The Json Object

I am trying to build a dropdown that has child and grandchild level objects that i need to access that are not included in the initial response of an ajax call. I'm using this str

Solution 1:

First of all, there are 2 statements I don't understand (lines 7-8):

var areaOption = [];
dropdownObject.push('<optionvalue=""></option>');

Maybe the 1st one is useful elsewhere, so ok (though you'd probably better to put it outside of the success: function). But I don't figure out how the 2nd one might be legitimate.

Source data structure.

Regarding the nested levels, I'm surprised you have 3 levels, while it seems that only 2 could do the job. When children exist you're building something like this:

<optgrouplabel="level-1-text"><optionvalue="level-3-id">Level-2 text: Level-3 text</option></optgroup>

where the 2nd level is useful only to get "Level-2 text". So schematically it appears that the corresponding source structure was like this:

calling Id:"~some guid~"returns {Id:"level-1-id", Text:"Level-1 text", HasChildren:true}
>>><optgrouplabel="level-1-text">calling Id:"level-1-id"returns {Id:"level-2-id", Text:"Level-2 text", HasChildren:true}
>>>(nothing)calling Id:"level-2-id"returns {Id:"level-3-id", Text:"Level-3 text", HasChildren:false}
>>><optionvalue="level-3-id">Level-2text:Level-3text</option>

So if you have hand on the source data structure you'd better to use something like this:

calling Id: "~some guid~"
returns {Id: "level-1-id", Text: "Level-1 text", HasChildren: "Level-2 text"}
>>> <optgroup label="level-1-text">
calling Id: "level-1-id"
returns {Id: "level-3-id", Text: "Level-3 text", HasChildren: false}
>>> <option value="level-3-id">Level-2 text: Level-3 text</option>

Note: here I keeped the "level-2" and "level-3" names for consistency, while there is only 2 levels now.

Usage of jQuery.

I was surprised you iterate the received JSON array with $(data).each(function(): this form is intended to process jQuery objects, while for arrays and for other, not-jQuery, objects we are supposed to use $.each(data, function().

So I had some tests and was surprised to discover it works too, though it someway should not! You can find the detailed investigation reported in the question I posted about that.

Another point is the way you address each item passed by the above iteration: $(this)[0].HasChildren is unnecessarily complicated, because its successively encapsulates this in a jQuery object then extracts it! You should merely use this.HasChildren.

Suggested simple way to achieve the whole work.

Building HTML as a linear flux of strings is not an easy way, because:

  • the closing tag for <optgroup> must appear _after inner <option>s have been created
  • but the async mode of Ajax makes difficult to know when to do it

At the opposite it's easy to use jQuery to build <option>s and <optgroup>s each in turn when needed. Note that it means we must use jQuery.append() instead of jQuery.html() to do that.

Regarding the nested aspect of the source data, the simplest way is to use recursion rather than using $.ajax several times in nested levels. It also has the advantage of reducing the code and making it more readable.

Here is the snippet founded on all the above considerations. Note that I also simplified it following my previous suggestion to merely use HasChildren (renamed Children for clarity) to provide the information about nested <optgroup>. For an <option> it not even has to be present.

functionbuildMenu(Id, $parent, prependage) {
  $parent = $parent || $('#dropdown');
  $.ajax({
    url:"../../api/getEnum",
    data: {itemFilter: "", Id: Id},
    dataType: "json",
    success: function(data) {
      $.each(data, function() {
        if (!this.Children) {
          // create a simple <option>
          $parent.append('\
<option value="' + this.Id + '">\
  ' + (!!prependage ? (prependage + ': ') : '') + this.Text + '\
</option>\
          ');
        } else {
          // create an <optgroup>
          $parent.append('\
<optgroup label="' + this.Text + '">\
</optgroup>\
          ');
          // call for <option>s in this optgroupbuildMenu(this.Id, $('optgroup:last', $parent), this.Children);
        }
      });
    }
  })
}

buildMenu('~some guid~');

To be sure, I tested with a slightly modified version you can try to check it works as expected:

<?php$menus = [
  '~some guid~' => [
    ["Id" => "level-1-1", "Text" => "Text-1-1"],
    ["Id" => "level-1-2", "Text" => "Text-1-2"],
    ["Id" => "level-1-3", "Text" => "Text-1-3", "Children" => "level-1-3"],
    ["Id" => "level-1-4", "Text" => "Text-1-4"],
    ["Id" => "level-1-5", "Text" => "Text-1-5", "Children" => "level-1-5"],
  ],
  'level-1-3' => [
    ["Id" => "level-1-3-1", "Text" => "Text-1-3-1"],
    ["Id" => "level-1-3-2", "Text" => "Text-1-3-2"],
    ["Id" => "level-1-3-3", "Text" => "Text-1-3-3"],
  ],
  'level-1-5' => [
    ["Id" => "level-1-5-1", "Text" => "Text-1-5-1"],
    ["Id" => "level-1-5-2", "Text" => "Text-1-5-2"],
    ["Id" => "level-1-5-3", "Text" => "Text-1-5-3"],
  ],
];
if ($Id = @$_REQUEST['Id']) {
  echo json_encode($menus[$Id]);
  exit;
}
?><selectid="dropdown"></select><scriptsrc="http://code.jquery.com/jquery-1.11.3.js"></script><script>functionbuildMenu(Id, $parent, prependage) {
  $parent = $parent || $('#dropdown');
  $.ajax({
    url:'',
    data: {itemFilter: "", Id: Id},
    dataType: "json",
    success: function(data) {
      $.each(data, function() {
        if (!this.Children) {
          // create a simple <option>
          $parent.append('\
<option value="' + this.Id + '">\
  ' + (!!prependage ? (prependage + ': ') : '') + this.Text + '\
</option>\
          ');
        } else {
          // create an <optgroup>
          $parent.append('\
<optgroup label="' + this.Text + '">\
</optgroup>\
          ');
          // call for <option>s in this optgroupbuildMenu(this.Id, $('optgroup:last', $parent), this.Children);
        }
      });
    }
  });
}

buildMenu('~some guid~');
</script>

Post a Comment for "Cascading Ajax Response To Access Objects That "haschildren" That Are Not Included In The Json Object"