0

I have a Parent-Child Hierarchy of checkboxes in my ASP.NET MVC application. I have it working where if you select a parent, the children are all selected. However, I'd like the parent(s) to become indeterminate if you deselect their child.

Jsfiddle: https://jsfiddle.net/vx4zvabg/2/

For instance, if you check Admin, then all checkboxes are selected in the children below. However, I'd like it to make the parent indeterminate if a child is unchecked. Further, make the parent completely unchecked if there are no children checked.

I have found solutions online, however, they have all been overly complicated or don't use actual <input type="checkbox"> for the checkboxes. I figure there must be a simple way to get this handled.

Activity Model:

public class Activity
{
    public int ActivityId { get; set; }
    public string Name { get; set; }
    public bool IsAllowed { get; set; }
    public IList<Activity> Children { get; set; } = new List<Activity>();
}

Razor Helper:

@helper CheckBoxShowTree(IEnumerable<Activity> parents)
{
    var Html = ((System.Web.Mvc.WebViewPage)WebPageContext.Current.Page).Html;
    <ul style="list-style:none;">
        @foreach (var child in parents)
        {
        <li>
            @Html.HiddenFor(x => child.ActivityId)
            @Html.HiddenFor(x => child.Name)
            @Html.CheckBoxFor(x => child.IsAllowed, new { @class = "groupsusers-checkbox", @style = "margin-right:5px; cursor:pointer;", @value = child.ActivityId })
            @Html.LabelFor(x => child.IsAllowed, child.Name, new { @class = "build-checkbox-label", @style = "font-weight:normal; margin-top:-2px;" })
            @if (child.Children.Any())
            {
            @CheckBoxShowTree(child.Children)
            }
        </li>
        }
    </ul>
}

JQuery:

$(function () {
    $("input[type='checkbox']").change(function () {
        var val = $(this).val();
        $(this).siblings('ul').find("input[type='checkbox']").prop('checked', this.checked);

    });
});

The Helper / Model may be irrelevant here. The JS just really needs to be generalized for a checkbox hierarchy and checking all children if parent is selected / indeterminate if some children are selected / unchecking parents if no children selected.

Additionally, the actual value (value="true") ONLY matters for the farthest children on the post for me. The hierarchy is just used in this particular case to make selecting those nodes easier. The value on the checked parent with no children is irrelevant to me.

I appreciate any help. I've been at this for over a day and JS / JQuery is not a strength for me. I have attempted to use Kendo UI and jstree as well, but it doesn't work in my situation. I think it is simpler than what those kits provide.

4
  • 1
    Check this link: https://css-tricks.com/indeterminate-checkboxes/ Seems to be what you want? Commented Jun 13, 2017 at 16:04
  • @sleeyuen Essentially, but I can't get it to work as the user would expect it would work. I need to learn how to check siblings properly and move up through the parents to determine if they should be unchecked, indeterminate, or checked. Commented Jun 13, 2017 at 17:22
  • 1
    If you've got a specific error or issue with which you need help, someone can probably assist, but as it stands, it sounds like you need some jQuery tutorials more than anything. Looks to me like the Codepen in that link has everything you need to do what you want. Commented Jun 13, 2017 at 19:45
  • @sleeyuen I have absolutely no freaking idea how I glossed over that section of the page. Thank you so much! That is EXACTLY what I was talking about. Commented Jun 13, 2017 at 20:16

1 Answer 1

1

Article about indeterminate checkboxes

Codepen from said article.

Code from Codepen, as per SO requirement to accompany link with actual code:

$('input[type="checkbox"]').change(function(e) {

  var checked = $(this).prop("checked"),
      container = $(this).parent(),
      siblings = container.siblings();

  container.find('input[type="checkbox"]').prop({
    indeterminate: false,
    checked: checked
  });

  function checkSiblings(el) {

    var parent = el.parent().parent(),
        all = true;

    el.siblings().each(function() {
      return all = ($(this).children('input[type="checkbox"]').prop("checked") === checked);
    });

    if (all && checked) {

      parent.children('input[type="checkbox"]').prop({
        indeterminate: false,
        checked: checked
      });

      checkSiblings(parent);

    } else if (all && !checked) {

      parent.children('input[type="checkbox"]').prop("checked", checked);
      parent.children('input[type="checkbox"]').prop("indeterminate", (parent.find('input[type="checkbox"]:checked').length > 0));
      checkSiblings(parent);

    } else {

      el.parents("li").children('input[type="checkbox"]').prop({
        indeterminate: true,
        checked: false
      });

    }

  }

  checkSiblings(container);
});
body {
  padding: 20px;
}
ul { 
  list-style: none;
  margin: 5px 20px;
}
li {
  margin: 10px 0;
}
<h1>Indeterminate Checkboxes</h1>

<ul>
  <li>
    <input type="checkbox" name="tall" id="tall">
    <label for="tall">Tall Things</label>

    <ul>
      <li>
        <input type="checkbox" name="tall-1" id="tall-1">
        <label for="tall-1">Buildings</label>
      </li>
      <li>
        <input type="checkbox" name="tall-2" id="tall-2">
        <label for="tall-2">Giants</label>

        <ul>
          <li>
            <input type="checkbox" name="tall-2-1" id="tall-2-1">
            <label for="tall-2-1">Andre</label>
          </li>
          <li>
            <input type="checkbox" name="tall-2-2" id="tall-2-2">
            <label for="tall-2-2">Paul Bunyan</label>
          </li>
        </ul>
      </li>
      <li>
        <input type="checkbox" name="tall-3" id="tall-3">
        <label for="tall-3">Two sandwiches</label>
      </li>
    </ul>
  </li>
  <li>
    <input type="checkbox" name="short" id="short">
    <label for="short">Short Things</label>

    <ul>
      <li>
        <input type="checkbox" name="short-1" id="short-1">
        <label for="short-1">Smurfs</label>
      </li>
      <li>
        <input type="checkbox" name="short-2" id="short-2">
        <label for="short-2">Mushrooms</label>
      </li>
      <li>
        <input type="checkbox" name="short-3" id="short-3">
        <label for="short-3">One Sandwich</label>
      </li>
    </ul>
  </li>
</ul>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.