undefined
undefined

Reputation: 479

Angular filter out duplicates in multiple selects on ng-repeat

I have a select box and an add button that adds another select box to the form by adding an empty object to an object ng-repeat is using. How can I dynamically assign an object that each select box can use that excludes any items that the other select boxes are using?

For example, if I have a select box with 'Branch' selected and then I add another select box, how can I exclude 'Branch' from that select box as well as all future select boxes? So if I have 4 select boxes all selecting something and I go to make a 5th select box, I only want to be able to select the 1 item that hasn't been selected yet.

I tried using a filter I saw in this post but that didn't really help me. I've tried playing around with other filters but I just can't seem to get this right.

I have the following HTML:

<form class="form-inline">
    <div ng-repeat="(key, value) in searchObject">
        <div class="input-group">
            <div class="input-group-addon">Search by</div>
            <select id="searchOption" class="form-control" ng-model="searchObject[key].searchOption" ng-options="searchOptions.Name for searchOptions in searchOptions">
                <option value="" selected disabled>Select an option to search by...</option>
            </select>
            <div class="input-group-btn">
                <button class="btn btn-secondary btn-custom" ng-click="clearSearchOption(searchObject[key])" ng-disabled="!searchObject[key].searchOption">
                    <span class="fa fa-times"></span>
                </button>
            </div>
        </div>
        <div ng-if="searchObject[key].searchOption" class="input-group">
            <div class="input-group-addon">Search for</div>
            <input type="text" class="form-control" ng-model="searchObject[key].searchText" placeholder="Search by {{searchObject[key].searchOption.Name}}..." />
            <div class="input-group-btn">
                <button class="btn btn-secondary btn-custom" ng-click="modifyCriteria($index, $last)">
                    <span class="fa fa-minus"></span>
                </button>
            </div>
        </div>
        <div class="input-group">
            <button ng-if="$last" type="button" class="btn btn-primary" ng-click="searchEmployee(searchObject)" ng-disabled="!searchValid()">Search <i class="fa fa-search"></i></button>
            <div ng-if="$last" class="input-group-btn">
                <button class="btn btn-secondary btn-custom" ng-click="modifyCriteria($index, $last)" ng-disabled="!searchValid()">
                    <span class="fa fa-plus"></span>
                </button>
            </div>
        </div>
        <br/><br/>
    </div>
</form>

With the following JS:

var app = angular.module('app', [
    'ngAnimate',
    'ngTouch',
    'ngSanitize',
    'ui.bootstrap',
    'ui.bootstrap.modal',
    'angular-loading-bar'
])

app.filter('exclude', function () {
    return function (items, exclude) {
        return items.filter(function (item) {
            return exclude.indexOf(item.ID) == -1;
        })
    }
})

app.controller('appController', function($scope, $http) {   
    $scope.searchOptions = [
        {ID: 1, Name: 'Branch', Show: true},
        {ID: 2, Name: 'BranchArea', Show: true},
        {ID: 3, Name: 'EmployeeType', Show: true},
        {ID: 4, Name: 'LastUpdateDateStart', Show: true},
        {ID: 5, Name: 'LastUpdateDateEnd', Show: true}      
    ]

    $scope.searchObject = [{}]

    $scope.excludedOptions = []

    $scope.modifyCriteria = function (index, last) {
        if (index < $scope.searchOptions.length - 1 && last == true) {
            $scope.searchObject.push({})
        } else {
            $scope.searchObject.splice(index, 1)
        }
    }

    $scope.searchValid = function () {
        for (var i = 0; i < $scope.searchObject.length; i++) {
            if (!$scope.searchObject[i].searchOption || !$scope.searchObject[i].searchText) {
                return false
            }
        }

        return true
    }

    $scope.clearSearchOption = function (searchForm) {
        delete searchForm.searchOption
    }

    function containsId(id) {
        for (var i = 0; i < $scope.searchObject.length; i++) {
            if ($scope.searchObject[i].searchOption.ID == id) {
                return true
            }
        }

        return false
    }
})

Upvotes: 0

Views: 1014

Answers (1)

Vivek Venkatachari
Vivek Venkatachari

Reputation: 103

So looks like for this scenario to work across multiple select box, it would be beneficial to have a component/directive of our own select box which would behave according to our requirement.

So first step we need to create a directive/component to take is a list of option and display in select box. This directive will also handle the filter to eliminate those options which are selected from precious order

here is the plunker link which will show this all together: http://plnkr.co/edit/Zxr17TQXwHpiQUzKAc8f?p=preview

directive:

app.directive('mySelectBox', function($filter) {
  return {
    restrict: 'E',
    replace: true,
    scope: {
      optionList:'=',
      allOptions: '=',
      index: '='
    },
    templateUrl:'selectTemplate.html',
    link: function(scope) {
      scope.onChange = function () {
        for(var i=0;i<scope.optionList.length;i++) {
          scope.optionList[i].Show = true;
        }
        scope.person.Show = false;
      };

      scope.getValues = function(val) {
        if(scope.index > 0) {
          var j = scope.index;
          for(var l=j;l>0;l=l-1) {
            var previousVal = $filter('filter')(scope.allOptions[j-1].optionList, {ID:val.ID}, true);
            if(!previousVal[0].Show) {
              return false;
            } else {
              j=j-1;
            }
          }
        }
        return true;
      }
    }
  }
});

Now we will have the controller which will hold this and logic to clone and add the select box on click of button

app.controller('MainCtrl', function($scope) {
  $scope.name = 'Boxes';
  $scope.options=[];
  $scope.optionList = 
      [ {ID: 1, Name: 'Branch', Show: true},
        {ID: 2, Name: 'BranchArea', Show: true},
        {ID: 3, Name: 'EmployeeType', Show: true},
        {ID: 4, Name: 'LastUpdateDateStart', Show: true},
        {ID: 5, Name: 'LastUpdateDateEnd', Show: true}  ];


  $scope.addCount = function() {
    $scope.options.push({optionList:angular.copy($scope.optionList)});
  }

});

Now the templates

directive:

<div>
      Select Box {{index+1}}: <select ng-change="onChange(index)" ng-options='person.Name for person in  optionList | filter:getValues' ng-model='person'><option value="">-- Select one --</option></select> 
      <span style="color:red">{{person.Name}}</span>
    </div>

index html which holds this directive call

<!DOCTYPE html>
<html ng-app="angularjs-starter">

  <head lang="en">
    <meta charset="utf-8">
    <title>Custom Plunker</title>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <h1>Hello {{name}}</h1>
    <input type="button" ng-click="addCount()"> Click here to add a select box
    <div ng-repeat="count in options">
      <my-select-box data-index="$index" data-all-options="options" data-option-list="count.optionList" ></my-select-box>
    </div>

  </body>

</html>

Upvotes: 1

Related Questions