0

Hey guys so i'm trying to implement a selectall checkbox at the top of my list of checkboxes using a custom directive and i've referred to this thread to do so: https://github.com/lorenzofox3/Smart-Table/issues/270

So far I'm getting an error that says TypeError: Cannot read property 'forEach' of undefined. Would really appreciate it if someone can help me out with this one. Thanks

My html:

<div class="row">
      <div class="col-md-12">
        <table id="document-table" st-table="documents" class="table">
          <thead>
            <tr>
              <th>
                <st-select-all all="yourDisplayedCollection"></st-select-all>
              </th>
            </tr>
          </thead>
          <tbody>
            <tr ng-repeat="document in documents">
              <td><input type="checkbox" ng-model="checkedDocument"/></td>
            </tr>
          </tbody>
         </table>
      </div>
    </div>

My Directive:

.directive('stSelectAll', function () {
        return {
            restrict: 'E',
            template: '<input type="checkbox" ng-model="isAllSelected" />',
            scope: {
            all: '='
            },
            link: function (scope, element, attr) {

            scope.$watch('isAllSelected', function () {
                scope.all.forEach(function (val) {
                val.isSelected = scope.isAllSelected;
                })
            });

            scope.$watch('all', function (newVal, oldVal) {
                if (oldVal) {
                oldVal.forEach(function (val) {
                    val.isSelected = false;
                });
                }

                scope.isAllSelected = false;
            });
            }
        }
        });
1
  • Are you sure that yourDisplayedCollection array's length is actually greater than 0? Commented Oct 7, 2016 at 19:47

3 Answers 3

1

I don't think you need to watch all, only isAllSelected. Try removing that watch altogether. I'm using the same directive for Smart Table and I don't watch all. You also want to add a check to make sure all exists:

scope.$watch('isAllSelected', function() {
  if(scope.all) {
    scope.all.forEach(function (val) {
      val.isSelected = scope.isAllSelected;
    }
  }
});

Also, you are supposed to make a copy of your original array to use for st-safe-src attribute on your table. Then use the original array for your directive.

// in your controller (not in your directive)
$scope.yourDisplayedCollection = [].concat($scope.documents);

Then change your view.

<table id="document-table" st-table="documents" st-safe-src="yourDisplayedCollection" class="table">

<st-select-all all="documents"></st-select-all>
Sign up to request clarification or add additional context in comments.

9 Comments

did you get the error that says foreach is undefined?
Well, you can't get that error any more if you are checking to see if it's defined.
From looking at the GitHub thread, it looks like you are just not making the array copy. You need to make a copy of the original array to use for your directive AS WELL AS to use for the st-safe-src attribute on your table. Not 2 copies, just one copy to use for both.
hmm so are you saying that i should change my yourDisplayedCollection to documents?
Yep. And add the st-safe-src attribute with your copy to the table tag (if your results are from AJAX). I actually mispoke in my last comment.
|
0
 all  <input type="checkbox" ng-click="ctrl.toggleAll(ctrl.all)" ng-model="ctrl.all">
 a    <input type="checkbox" ng-model="ctrl.checks.alpha" ng-value="allChecked">
 b    <input type="checkbox" ng-model="ctrl.checks.beta" ng-value="allChecked">

script

function MyController() {
    this.data = [
        {title: 'a', data: [1,2,3]},
        {title: 'b', data: [4,5,6]},
        {title: 'c', data: [7,8,9]}
    ];

    let ctrl = this;

    ctrl.checks = {
       alpha: false,
       beta: false
    };

    ctrl.toggleAll = function(toggle) {
        for (let check in ctrl.checks) {
            ctrl.checks[check] = toggle;
        }
   };
   return this;
}
angular.module('test').controller('MyController',MyController);

All of the children checkboxes will assume the state of the parent checkbox in this implementation. Rather than simply toggling the prior state of the child checkbox.

Enjoy. It's has been tested, but feel free to ask if it's broken or you have questions.

Comments

0

In your javascript, modify your directive as

function rowSelectAll() {
    return {
        require: '^stTable',
        $scope: {
            all: '=rowSelectAll',
            selected: '='
        },
        link: function (scope, element, attr) {
            $scope.isAllSelected = false;
            element.bind('click', function (evt) {
                $scope.$apply(function () {
                    $scope.all.forEach(function (val) {
                        val.isSelected = $scope.isAllSelected;
                    });
                });
            });

            $scope.$watchCollection('selectedItems', function(newVal) {
                var s = newVal.length;
                var a = ($scope.all !== undefined) ? $scope.all.length : 0;
                if ((s == a) && s > 0 && a > 0) {
                    element.find('input').prop('checked', true);
                    scope.isAllSelected = false;
                } else {
                    element.find('input').prop('checked', false);
                    $scope.isAllSelected = true;
                }
            });
        }
    };
}
app.directive('rowSelectAll', rowSelectAll);

And in HTML file, inside table header use your directive and assign table collection i.e, displayedCollection to it.

<th row-select-all="displayedCollection" selected-items="selected_items" ng-click="selectAll(displayedCollection)"><input type="checkbox" ng-disabled="displayedCollection.length == 0"></th>

if you want to selected elements then use the following code:

    $scope.selected_items = [];
    // Function to get data for all selected items
    $scope.selectAll = function (collection) {
        // if there are no items in the 'selected_items' array, push all elements to 'selected_items'.
        if ($scope.selected_items.length === 0) {
            angular.forEach(collection, function(val) {
                if (val.bank_flag) {
                    $scope.selected_items.push(val);
                }
          });

        // if there are items in the 'selected_items' array, add only those that are not.
        } else if ($scope.selected_items.length > 0 && $scope.selected_items.length != $scope.displayedCollection.length) {
            angular.forEach(collection, function(val) {

                var found = $scope.selected.indexOf(val);
                if(found == -1) {
                    $scope.selected_items.push(val);
                }

            });
        // Otherwise, reinitiate the variable.
        } else  {
            $scope.selected_items = [];
        }
    };

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.