0

I've checked as many questions as I can, and I've found some answers but I'm stuck on the last stretch. Most of what I've found covers how to turn things on with a checkbox, not how to turn everything off except what's been checked.

I've got a table with 2-3 checkboxes in each row. The objective is 1) when you check one box, the checkboxes in all other rows are disabled (your only options are checkboxes in the same row), then 2) when you check a 2nd box in the same row, if there's a third checkbox, it's also disabled, and finally 3) checking that second box enables the submit button. I've gotten #1 to work (though I think I broke some rules doing it), and I've kind of gotten #3 to work (well, it enables when you've checked just one box, which isn't exactly right but maybe close enough). It's the disabling the 3rd button in a three-button row that's giving me headaches.

I used VBAhole's demo from How to respond to clicks on a checkbox in an AngularJS directive? which got me some of the way. Here's the working plnkr.

html:

<tr ng-repeat="e in entities" ng-class="{active : isChecked(e.id) }" id="row{{e.id}}">
    <td>
        <label>
            <input type="checkbox" name="checkbox{{e.id}}" ng-disabled="checkThis(e.id, e.box1id)" ng-checked="isChecked(e.box1id)" ng-click="updateCheck($event, e.id)" />
            {{e.box1}}
        </label><br />
        <label>
            <input type="checkbox" name="checkbox{{e.id}}" ng-disabled="checkThis(e.id, e.box2id)" ng-checked="isChecked(e.box2id)" ng-click="updateCheck($event, e.id)" />
            {{e.box2}}
        </label><br />
        <label>
            <input type="checkbox" name="checkbox{{e.id}}" ng-disabled="checkThis(e.id, e.box3id)" ng-checked="isChecked(e.box3id)" ng-click="updateCheck($event, e.id)" />
            {{e.box3}}
        </label>
    </td>
    <td>
        <button disabled ng-disabled="!isChecked(e.id)">submit</button>
    </td>
</tr>

and here's the controller (haven't broken things out into a directive yet):

var app = angular.module('myApp', []);
app.controller('MyCtrl', function ($scope) {
    $scope.entities = [
        { id: 10, name: "boxrow10", box1: "red11", box1id: 11, box2: "blue12", box2id: 12, box3: "blue13", box3id: 13 },
        { id: 20, name: "boxrow20", box1: "red21", box1id: 21, box2: "blue22", box2id: 22, box3: "blue23", box3id: 23  },
        { id: 30, name: "boxrow30", box1: "red31", box1id: 31, box2: "blue32", box2id: 32, box3: "blue33", box3id: 33  }
    ];

    $scope.checked = [];
    var updateChecked = function (action, id) {
        if (action == 'add') $scope.checked.push(id);
        if (action == 'remove') $scope.checked.splice($scope.checked.indexOf(id), 1);
    }

    $scope.updateCheck = function ($event, id) {
        var checkbox = $event.target;
        var action = (checkbox.checked ? 'add' : 'remove');
        updateChecked(action, id);
    };

    $scope.isChecked = function (id) {
        return $scope.checked.indexOf(id) >= 0;
    };

    $scope.checkThis = function (id, boxid) {
        var check = $scope.checked;
        var current = id;
        if (check != current && check.length == 1) {
            return current;
        } else if (check.length == 2) {
            for (var i = check.length - 2; i >= 0; i--) {
                if (check[i] === current) {
                    check.splice(i, 1);
                }
            }
            return current;
        }
    };
});

It gets really spaghetti-like in that last bit (the 2nd if statement), and I can't see how returning current really changes anything, but if I remove that, then I don't get the disabling feature. The problem is (among many) that if I have two boxes checked & uncheck one, it enables all the boxes in all the rows again, instead of leaving them disabled (since there's still a box checked).

(I know I could do this in jquery easy, but my instructions are to not mix the streams, so... pure angular.) I've been banging my head against this for several days now. Any enlightenment would be greatly appreciated. tia!

Community
  • 1
  • 1
kl02
  • 580
  • 6
  • 24

2 Answers2

3

You can do it using only angularjs quite easily.

Here is a working example : http://plnkr.co/GsZWG1mEcKsB7zpz6Spy

All is based on ng-disabled and a few methods in the scope.

Each checkbox calls a toggle method in the controller with his id and entity.

The controller keeps the selected entity and boxes in variables to disable the right checkboxes.

I added a utility method in the controller to know if a checkbox should be disabled since there are a few things to check...

Here is the controller :

app.controller('MyCtrl', function ($scope) {
        $scope.entities = [
      { id: 10, name: "boxrow10", box1: "red11", box1id: 11, box2: "blue12", box2id: 12, box3: "blue13", box3id: 13 },
      { id: 20, name: "boxrow20", box1: "red21", box1id: 21, box2: "blue22", box2id: 22, box3: "blue23", box3id: 23  },
      { id: 30, name: "boxrow30", box1: "red31", box1id: 31, box2: "blue32", box2id: 32, box3: "blue33", box3id: 33  }
    ];
    $scope.selectedEntity = null;
    $scope.selectedBoxes = [];

    $scope.toggleChecked = function(entity, boxid) {
      if (entity == $scope.selectedEntity) {
        if ($scope.selectedBoxes.length == 1 && $scope.selectedBoxes[0]  == boxid) {
          $scope.selectedEntity  = null;
          $scope.selectedBoxes = [];
          return;
        } else if ($scope.selectedBoxes.length > 0) {
          for (var box in $scope.selectedBoxes) {
            if ($scope.selectedBoxes[box] == boxid) {
              $scope.selectedBoxes.splice(box, 1);
              return;
            }
          }
        }
      }
      $scope.selectedEntity = entity;
      $scope.selectedBoxes.push(boxid);
    };

    $scope.inSelected = function(id) {
      for (var box in $scope.selectedBoxes)  {
        if ($scope.selectedBoxes[box] == id) 
          return true;
      }
      return false;
    };

    $scope.isDisabled = function(e, id) {
      return ($scope.selectedEntity 
            && $scope.selectedEntity != e)
            ||  ($scope.selectedBoxes.length >= 2 
            && !$scope.inSelected(id));
    };
});

Finally, each checkbox looks like this :

<label>
  <input type="checkbox"  ng-click="toggleChecked(e, e.box3id)" ng-disabled="isDisabled(e, e.box3id)" />
  {{e.box3}}
</label>

As you can see in the plunker, this solution answers your 3 requirements without messy code... It could probably be improved by using a service instead of the controller for the logic, but this version is simpler to understand.

Galdo
  • 945
  • 6
  • 12
1

I have modified your code, and the plnkr a bit. The problem you have is solved here but it's not perfect. There are 2 issues:

I. The Checkboxes get disabled once selection reaches 2.

II. All the submit buttons are enabled, the $scope.submitDisabled isn't working properly mostly because $scope.updateCheck isn't updating selection.

I think you can debug and solve these :). You needed what I have done in $scope.isEnabled which is working fine.

var app = angular.module('myApp', []);

app.controller('MyCtrl', function ($scope) {
  $scope.entities = [
    { id: 10, name: "boxrow10", box1: "red11", box1id: 11, box2: "blue12", box2id: 12, box3: "blue13", box3id: 13 },
    { id: 20, name: "boxrow20", box1: "red21", box1id: 21, box2: "blue22", box2id: 22, box3: "blue23", box3id: 23  },
    { id: 30, name: "boxrow30", box1: "red31", box1id: 31, box2: "blue32", box2id: 32, box3: "blue33", box3id: 33  }
  ];

  $scope.checked = [];
  $scope.checkedCount = 0;
  var updateChecked = function (action, id) {
    if (action == 'add') {
      $scope.checked.push(id);
      $scope.checkedCount++;
    }
    else if (action == 'remove') {  // Unnecessary check?
      $scope.checked.splice($scope.checked.indexOf(id), 1);
      $scope.checkedCount--;
    }
  };

  $scope.updateCheck = function ($event, id) {
    var checkbox = $event.target;
    var action = (checkbox.checked ? 'add' : 'remove');
    updateChecked(action, id);
  };

  $scope.isChecked = function (id) {
    return $scope.checked.indexOf(id) >= 0;
  };

  $scope.isEnabled = function (rowId, boxid) {
    switch($scope.checkedCount) {
      case 2: return false;
      case 1: return $scope.checked[0] == rowId;
      case 0: return true;
    }
  };

  $scope.submitDisabled = function (rowId) {
    return !($scope.checkedCount == 2 && $scope.checked[0] == rowId);
  };
});

And the template is:

<!DOCTYPE html>
<html ng-app="myApp" ng-controller="MyCtrl">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js" data-semver="1.0.7" data-require="angular.js@1.2.0-rc1"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>

  <body>
    <table>
      <tbody>
        <tr ng-repeat="e in entities" ng-class="{active : isChecked(e.id) }" id="row{{e.id}}">
          <td>
            <label>
             <input type="checkbox" name="checkbox{{e.id}}" ng-disabled="!isEnabled(e.id)" ng-checked="isChecked(e.box1id)" ng-click="updateCheck($event, e.id)" />
          {{e.box1}} 
        </label><br />
        <label>
          <input type="checkbox" name="checkbox{{e.id}}" ng-disabled="!isEnabled(e.id)" ng-checked="isChecked(e.box2id)" ng-click="updateCheck($event, e.id)" />
              {{e.box2}}
            </label><br />
            <label>
              <input type="checkbox" name="checkbox{{e.id}}" ng-disabled="!isEnabled(e.id)" ng-checked="isChecked(e.box3id)" ng-click="updateCheck($event, e.id)" />
              {{e.box3}}
            </label>
          </td>
          <td>
            <button ng-disabled="submitDiasabled(e.id)">submit</button>
          </td>
        </tr>
      </tbody>
    </table>
  </body>
</html>
0xc0de
  • 8,028
  • 5
  • 49
  • 75