2

So the overview of the problem; I am retrieving data from an api and creating a CRUD page for it. The data has a set of labels that the user can select.

Below is a code sample representing my problem. The labels selected by the user are represented by the user.labels relationship and the total available labels that can be selected are represented by user.parent.grandparent.labels.

I'm able to sync the selection. What I can't seem to figure out is how to get rid of options that have already been selected from the list of options on any other subsequent select field.

angular.module('app', [])
  .controller('select', ['$scope', '$filter', '$location',
    function($scope, $filter, $location) {
      $scope.user = {
        "parent": {
          "grandparent": {
            "labels": [{
              "id": 28,
              "name": "Label 1",
            }, {
              "id": 17,
              "name": "Label 2",
            }, {
              "id": 39,
              "name": "Label 3",
            }, {
              "id": 77,
              "name": "Label 4"
            }, {
              "id": 100,
              "name": "Label 5"
            }]
          }
        },
        "labels": [{
          "id": 28,
          "name": "Label 1",
          "meta": {
            "score": 3
          }
        }, {
          "id": 17,
          "name": "Label 2",
          "meta": {
            "score": 5
          }
        }]
      };
    }
  ]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="app" ng-controller="select">
  <div ng-repeat="label in user.labels track by $index">
    <div class="form-field">
      <span>Label</span>
      <select ng-model="user.labels[$index]" ng-options="department.name for department 
                        in user.parent.grandparent.labels track by department.id">
      </select>
    </div>
    <div>
      <span>Score</span>
      <select ng-model="label.meta.score">
        <option value="1">1 (lowest)</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
        <option value="5">5 (highest)</option>
      </select>
    </div>
  </div>
  <button ng-click="user.labels.push({})">Add Label</button>
</div>

1

1 Answer 1

3

You can use a filter function inside the ng-repeat to achieve this, here is a sample Codepen showing you how:

http://codepen.io/anon/pen/ZYveOo

You need to pass the filter in the repeat definition:

<select ng-model="user.labels[$index]" ng-options="department.name for department in user.parent.grandparent.labels | filter:removeSelected track by department.id ">

Which refers to this function on scope:

      $scope.removeSelected = function(val){
        return !_.find($scope.user.labels, function(label) {
          return label.id === val.id;
        });
      };

Even then though I think you are missing one use case which is that you want to be able to have the currently selected label included in the options, by removing all selected options you are removing that ability.

Updated:

Ok then, so after giving this some thought I have come up with the following filter which could be optimised but does seem to work as expected:

.filter('duplicatesFilter', function() {
        return function(items, index, selected) { 
          var res = [selected[index]];
          _.forEach(items, function(item){
            if(!_.find(selected, function(label) {
              return label.id === item.id;
            })){
              res.push(item);
            }
          });

          return res;
        };
    })

Use it like so:

<select ng-model="user.labels[$index]" ng-options="department.name for department in user.parent.grandparent.labels | duplicatesFilter:$index:user.labels track by department.id "></select>

This is something I have hit a few times and each time I've worked around it. I'll take a look later if I can find a custom filter that better solves the problem and if I can't I'll tidy up this code and release one; however this should be good to go for your use-case.

Working code-pen:

http://codepen.io/anon/pen/ZYveOo

Sign up to request clarification or add additional context in comments.

3 Comments

Yeah thats the issue I ran into. I would filter the selected results but then they are no longer in the selection :(
It's working for already created fields but when you try to add a label it doesn't work :(. I'll try to take a closer look at the code later today to see if I can adjust that. But this already has gotten me further so thank you :). I can't believe there isn't already a solution out there, it seems like this would be a common problem.
So I plugged it in to my app, and it works perfectly. Not sure whats up with the codepen but Thank you soo much! I owe you a beer good sir!

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.