Blair Holmes Blair Holmes - 29 days ago 14
AngularJS Question

Angular with multiple selects that must have unique options selected

I have an angular page that will have a dynamic number of select elements on it. Each select will have the same option collection but once an option is selected from one, that option should be removed from all of the subsequent select elements.

I found this: http://jsfiddle.net/Zv5NE/63/ which works exactly how I'd like (when an option is selected from one select, it's removed from the others and then if that same select is changed, it adds the previously selected option back to the others).

The problem is, this is using a hard coded number of select elements and also using hard coded filters for each select element...that won't work for my purposes because, as I said, my users are going to need to be able to dynamically add n number of select elements.

I've done some playing around trying to create my own filter to accommodate for this, but I'm super green to angular (angular 1 btw) and I've hit the wall.

this is a small snippet from what I've tried. Essentially I've just tried creating an array and adding selected items to that array then checking against the values in the array for the filter (I would have to add some logic for changing options obviously, but I'm really not sure this is the right direction to go):

$scope.filter = function (item) {
for (i = 0; i < $scope.names.length; i++) {
if (item == $scope.names[i]) {
return false;
}
}
return true;
};


any guidance would be greatly appreciated.

Answer

I shelved this for a while but came back to it this morning. I was able to come up with a working solution.

Here's what I wrote up. May not be the most elegant way to do it, but it works for my purposes:

    <!DOCTYPE html>

<html ng-app="app">
<head>
    <meta name="viewport" content="width=device-width" />
    <title>AngularTest</title>
</head>
<body ng-controller="HellowWorldCtrl">
    <select ng-model="selectname0" ng-options="item as item.name for item in classes | customFilter:'selectname0':this">
        <option value="">- select -</option>
    </select>
    <div id="selectsDiv"></div>
    <br />
    <input type="button" value="Add Select" ng-click="addSelect()" ng-show="cnt < classes.length -1" />



    <script src="~/Scripts/angular.js"></script>
    <script type="text/javascript">

        var app = angular.module('app', []).controller('HellowWorldCtrl', function ($scope, $compile) {
            $scope.cnt = 0;
            $scope.selectsAdded = [];
            $scope.selectsAdded.push('selectname0');
            $scope.addSelect = function () {
                $scope.cnt++;
                $scope.selectsAdded.push('selectname' + $scope.cnt);
                var newSelect = $compile('<div><select ng-model="selectname' + $scope.cnt + '" ng-options="item as item.name for item in classes | customFilter:\'selectname' + $scope.cnt + '\':this"><option value="">- select -</option></select></div>')($scope);
                angular.element(document.getElementById('selectsDiv')).append(newSelect);
            };

            $scope.classes = [
              {
                  id: 1,
                  name: 'Biology 101',
                  courseid: '12345'
              },
              {
                  id: 2,
                  name: 'Chemistry 101',
                  courseid: '12374'
              },
              {
                  id: 3,
                  name: 'Psychology 101',
                  courseid: '32165'
              },
              {
                  id: 4,
                  name: 'Geology 101',
                  courseid: '78945'
              },
              {
                  id: 5,
                  name: 'Math 101',
                  courseid: '65478'
              }

            ];

        });

        app.filter('customFilter', function () {
            return function (items, which, scope) {
                var alreadySelectedCourses = [];
                var courses = [];
                for (i = 0; i < items.length; i++) { // loop over all of the items in the class array...cwc
                    for (j = 0; j < scope.selectsAdded.length; j++) { // loop over all of the selects added to the page...cwc
                        if (which == scope.selectsAdded[j]) { // check if the calling select is the same one in the loop...cwc
                            if (scope['selectname' + j] && scope['selectname' + j].id) { // check if the calling select has alraedy been selected...cwc
                                if (scope['selectname' + j].id == items[i].id) { // check if the selected value of the calling select is the same as the item in the iteration and add it to the return array if so...cwc
                                    courses.push(items[i]);
                                    alreadySelectedCourses.push(items[i]);
                                }
                            }
                        } else { // not the calling select so find out the value and don't add it to the return array...cwc
                            if ((scope['selectname' + j] && scope['selectname' + j].id)) { // other selects (not calling select) have values selected so add them to the alreadyselectedarray...cwc
                                if (scope['selectname' + j].id == items[i].id) {
                                    alreadySelectedCourses.push(items[i]);
                                }
                            }
                        }
                    }
                    if (alreadySelectedCourses.indexOf(items[i]) > -1) {
                        continue;
                    } else {
                        courses.push(items[i]);
                    }
                }
                return courses;
            }
        });
    </script>
</body>
</html>