Steven Lemmens
Steven Lemmens

Reputation: 630

AngularJS: ngRepeat for select on change ngOptions-list

I have an angularjs app, which has a select filled with options from an arry, using ngOptions. Every time the user clicks the button to add one, the ngRepeat directive generates a new select at the bottom.

I want to make sure that the user cannot select duplicate values.

So if my list contains 3 items: Item1, Item2 and Item3, and the user selects Item3 and then presses the button, the last generated select list should contain only items 'Item1' and 'Item2'. If the user would then select 'Item1' and presses the button, the user should see the next select be generated with only the 'Item2' option.

So generally, in the case above, the generated HTML should be something like this:

<div data-ng-repeat="item in selectedOptions">
  <select>
    <option value="1">Item1</option>
    <option value="2">Item2</option>
    <option value="3">Item3</option>
  </select>

 <select>
  <option value="1">Item1</option>
  <option value="2">Item2</option>
 </select>

 <select>
  <option value="2">Item2</option>
 </select>
</div> 

Keep in mind: the user will keep seeing all THREE of the selects, once with every option available, once with just two options available, and once with just one option available.

I've been thinking of a lot of ways to make this work, but so far I haven't had any luck. Does anyone know of a pattern I can use in angular to achieve this behavior?

This is something that I've tried so far.

<div data-ng-repeat="function in item.Functions">
    <select data-ng-model="function.Id" data-ng-options="j.Id as j.Name for j in getCorrectFunctions(functionsList)">
        <option selected="selected" value="">---</option>
     </select>

     <a data-ng-click="addFunction()">
        <i class="fa fa-plus fa-plus-lower"></i>
     </a>
</div>

and in my directive code I have following function:

  function getCorrectFunctions(functionList) {
                var item = scope.item;

                var list = functionList.slice();

                //excluded for brevity: this was a loop which would remove every item that wasn't available anymore

                return list;
            }

I thought this would be executed once for every item in the list, but that does not seem to be case. I don't think applying a filter would be any different, would it?

Upvotes: 0

Views: 1733

Answers (1)

jmustonen
jmustonen

Reputation: 470

Here's one take on this. This does not have support for dynamically adding new functions, but however it does prevent user from selecting any given item twice.

See Plunker for working example and more details.

First the Angular setup part. Here I've defined a mock array of function objects ($scope.functions) and array for user made selections ($scope.selected)

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

app.controller('SelectCtrl', function ($scope) {
  $scope.functions = [
    {id: 1, name: 'First'},
    {id: 2, name: 'Second'},
    {id: 3, name: 'Third'},
  ];
  $scope.selected = [];

  $scope.selectedFilter = function (selectNumber) {
    // snipped for now
  };
});

In html, showing only one select, but similar approach used for all 3 selects: set the selected value to the selected array in given index (0 for the case shown below), and use selectedFilter to filter functions with same index value.

  <select ng-options="j.id as j.name for j in functions | filter:selectedFilter(0)" ng-model="selected[0]">
    <option value="" selected="selected">---</option>
  </select>

Then finally the filtering function. It returns true for all unselected functions and for the selected function of the given select.

  $scope.selectedFilter = function (selectNumber) {
    return function (func) {
      if ($scope.selected.length === 0) {
        return true;
      } else {
        var unselectedFunctions = _.filter($scope.functions, function (fn) {
          return _.findIndex($scope.selected, function (sel) {
            return fn.id === sel;
          }) === -1;
        });
        var selectedForCurrentId = $scope.selected[selectNumber];
        var selectedForCurrent = _.find($scope.functions, {id: selectedForCurrentId});
        return func === selectedForCurrent || _.findIndex(unselectedFunctions, {id: func.id}) > -1;
      }
    };
  };

Here I've used Lodash for some nice helper functions. Not affiliated with Lodash in any way, but I really suggest you to take a look at it, or any other similar library.

Hopefully this helps you to get things moving on!

Upvotes: 1

Related Questions