ng-options with disabled rows

AngularjsNg Options

Angularjs Problem Overview


Is it possible to use ng-options that it will render into disabled rows based on criteria?

this:

 <select ng-options="c.name group by c.shade for c in colors">

maybe possible to turn into something like this:

 <select ng-options="c.name group by c.shade for c in colors | disabled(c.shade)">

and let's say via a filter that could return disabled='disabled' for all the colors that have shade = "dark"

<select>
   <optgroup label="dark">
      <option value="0" disabled="disabled">black</option>
      <option value="2" disabled="disabled">red</option>
      <option value="3" disabled="disabled">blue</option>
   </optgroup>
   <optgroup label="light">
      <option value="1">white</option>
      <option value="4">yellow</option>
   </optgroup>
 </select>

Angularjs Solutions


Solution 1 - Angularjs

@lucuma's answer (originally the accepted answer) was correct, but by now should be updated, because this was fixed in Angular 1.4. See the docs of ng-options which also contains an example.

I'm using Angular 1.5 and this works for me:

View

<select ng-model="$ctrl.selectedItem" ng-options="item as item.label disable when item.disabled for item in $ctrl.listItems">

Controller

vm.items = [ { id: 'optionA', label: 'Option A' }, { id: 'optionB', label: 'Option B (recommended)' }, { id: 'optionC', label: 'Option C (Later)', disabled: true } ]; vm.selectedItem = vm.items[1];

Solution 2 - Angularjs

As pointed by @Lod Angular added support for this in 1.4.0-beta.5.

For angular js >= 1.4.0-beta.5.

<select ng-options="c.name disable when c.shade == 'dark' 
group by c.shade for c in colors">

And for angular js < 1.4.0-beta.5 refer the solution below:

Similar to the one given by @lucuma but without jQuery dependency.

Check this http://jsfiddle.net/dZDLg/46/

Controller

<div ng-controller="OptionsController">
    <select ng-model="selectedport" 
        ng-options="p.name as p.name for p in ports"
        options-disabled="p.isinuse for p in ports"></select>
    <input ng-model="selectedport">
</div>

Directive

angular.module('myApp', [])
.directive('optionsDisabled', function($parse) {
    var disableOptions = function(scope, attr, element, data, 
                                  fnDisableIfTrue) {
        // refresh the disabled options in the select element.
        var options = element.find("option");
        for(var pos= 0,index=0;pos<options.length;pos++){
            var elem = angular.element(options[pos]);
            if(elem.val()!=""){
                var locals = {};
                locals[attr] = data[index];
                elem.attr("disabled", fnDisableIfTrue(scope, locals));
                index++;
            }
        }
    };
    return {
        priority: 0,
        require: 'ngModel',
        link: function(scope, iElement, iAttrs, ctrl) {
            // parse expression and build array of disabled options
            var expElements = iAttrs.optionsDisabled.match(
                /^\s*(.+)\s+for\s+(.+)\s+in\s+(.+)?\s*/);
            var attrToWatch = expElements[3];
            var fnDisableIfTrue = $parse(expElements[1]);
            scope.$watch(attrToWatch, function(newValue, oldValue) {
                if(newValue)
                    disableOptions(scope, expElements[2], iElement, 
                        newValue, fnDisableIfTrue);
            }, true);
            // handle model updates properly
            scope.$watch(iAttrs.ngModel, function(newValue, oldValue) {
                var disOptions = $parse(attrToWatch)(scope);
                if(newValue)
                    disableOptions(scope, expElements[2], iElement, 
                        disOptions, fnDisableIfTrue);
            });
        }
    };
});

Note: This solution doesn't work with group by as rightly pointed by everyone. Refer the solution below by @DHlavaty if you are looking to make it work with group by.

Solution 3 - Angularjs

Angular added support for this in 1.4.0-beta.5

<select ng-options="c.name disable when c.shade == 'dark' group by c.shade for c in colors">

Solution 4 - Angularjs

I do not believe there is a way to do what you are asking just using ng-options. This issue was raised on the angular project and is still open:

https://github.com/angular/angular.js/issues/638

It seems that the work around is to use a directive which is referenced here and in the github issue: http://jsfiddle.net/alalonde/dZDLg/9/

Here is the entire code from the jsfiddle for reference (the code below is from alande's jsfiddle):

<div ng-controller="OptionsController">
    <select ng-model="selectedport" 
        ng-options="p.name as p.name for p in ports"
        options-disabled="p.isinuse for p in ports"></select>
    <input ng-model="selectedport">
</div>

angular.module('myApp', [])
.directive('optionsDisabled', function($parse) {
    var disableOptions = function(scope, attr, element, data, fnDisableIfTrue) {
        // refresh the disabled options in the select element.
        $("option[value!='?']", element).each(function(i, e) {
            var locals = {};
            locals[attr] = data[i];
            $(this).attr("disabled", fnDisableIfTrue(scope, locals));
        });
    };
    return {
        priority: 0,
        require: 'ngModel',
        link: function(scope, iElement, iAttrs, ctrl) {
            // parse expression and build array of disabled options
            var expElements = iAttrs.optionsDisabled.match(/^\s*(.+)\s+for\s+(.+)\s+in\s+(.+)?\s*/);
            var attrToWatch = expElements[3];
            var fnDisableIfTrue = $parse(expElements[1]);
            scope.$watch(attrToWatch, function(newValue, oldValue) {
                if(newValue)
                    disableOptions(scope, expElements[2], iElement, newValue, fnDisableIfTrue);
            }, true);
            // handle model updates properly
            scope.$watch(iAttrs.ngModel, function(newValue, oldValue) {
                var disOptions = $parse(attrToWatch)(scope);
                if(newValue)
                    disableOptions(scope, expElements[2], iElement, disOptions, fnDisableIfTrue);
            });
        }
    };
});

function OptionsController($scope) {
    $scope.ports = [{name: 'http', isinuse: true},
                    {name: 'test', isinuse: false}];

    $scope.selectedport = 'test';
}

Solution 5 - Angularjs

A similar effect can be achieved using ng-repeat and ng-disabled on the option itself, avoiding the use of a new directive.

HTML

<div ng-controller="ExampleController">
    <select ng-model="myColor">
        <option ng-repeat="c in colors" ng-disabled="c.shade=='dark'" value="{{$index}}">
            {{c.name}}
        </option>
    </select>
</div>

Controller

function ExampleController($scope, $timeout) {
    $scope.colors = [
      {name:'black', shade:'dark'},
      {name:'white', shade:'light'},
      {name:'red', shade:'dark'},
      {name:'blue', shade:'dark'},
      {name:'yellow', shade:'light'}
    ];
    $timeout(function() {
        $scope.myColor = 4; // Yellow
    });
}

Fiddle

http://jsfiddle.net/0p4q3b3s/

Known issues:

  • Does not use ng-options
  • Does not work with group by
  • Selects the index, not the object
  • Requires $timeout for initial selection

Edit : Any object property can be selected (besides the index), but not the object itself. Also, if you have a simple array and not an array of objects, below method will work.

Change this line in HTML :

<option ng-repeat="c in colors" ng-disabled="c.shade=='dark'" value="{{c.name}}">

Change this line in Controller :

$scope.myColor = $scope.colors[4].name; // Yellow

Solution 6 - Angularjs

Since February 2015 there has been a way to disable options in your ng-options tag.

This Link shows the addition of the feature on github

I found that using angular 1.4.7, the syntax had changed from 'disable by' to 'disable when'.

The syntax for this is:

'ng-options': 'o.value as o.name disable when o.unavailable for o in options'

Solution 7 - Angularjs

I had an interesting situation. An array of dropdowns and I need it to disable the options that were already selected in each of the dropdowns, but I also need it to keep enable the one that was selected already...

here is my plunker: Enable/Disable values with ng-options

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

app.controller('MainCtrl', function($scope) {

  // disable the fields by default
  $scope.coverage = [
    { CoverageType: '', CoverageName: 'No Coverage' },
    { CoverageType: 'A', CoverageName: 'Dependent Only' },
    { CoverageType: 'B', CoverageName: 'Employee Plus Children' },
    { CoverageType: 'C', CoverageName: 'Employee Only' },
    { CoverageType: 'D', CoverageName: 'Employee Plus One' },
    { CoverageType: 'E', CoverageName: 'Employee Plus Two' },
    { CoverageType: 'F', CoverageName: 'Family' },
];
            
            
  // values already set ex: pulled from db          
  $scope.rates = ['A','C', 'F'];     

  $scope.changeSelection = function (index, rate){
     $scope.rates[index] = rate;
     disableRecords();
  }
  
  // start by disabling records
  disableRecords(); 
  
  function disableRecords () {
      // set default values to false
      angular.forEach($scope.coverage, function (i, x) {
             i.disable = false; 
      });
      // set values to true if they exist in the array
      angular.forEach($scope.rates, function (item, idx) {
          angular.forEach($scope.coverage, function (i, x) {
              if (item == i.CoverageType) {
                   i.disable = true; 
              }
          });
      });
  }
  

});

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.21/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="ngoptions">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script data-require="[email protected]" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <table>
      <thead></thead>
      <tbody>
        <tr ng-repeat="rate in rates">
          <td>
            <select 
            ng-model="rate" 
            ng-change="changeSelection($index, rate)" 
            ng-options="type.CoverageType as type.CoverageName disable when (type.disable == true && type.CoverageType != rate) for type in coverage"></select>
          </td>
        </tr>
      </tbody>
    </table>
  </body>

</html>

Solution 8 - Angularjs

You can disable using ngOptions in angular 1.4.1 or above

HTML template

<div ng-app="myapp">
<form ng-controller="ctrl">
    <select id="t1" ng-model="curval" ng-options='reportingType.code as reportingType.itemVal disable when reportingType.disable for reportingType in reportingOptions'>
        <option value="">Select Report Type</option>
    </select>
   
</form>

Controller code

angular.module('myapp',[]).controller("ctrl", function($scope){
$scope.reportingOptions=[{'code':'text','itemVal':'TEXT','disable':false}, {'code':'csv','itemVal':'CSV','disable':true}, {'code':'pdf','itemVal':'PDF','disable':false}];

})

Solution 9 - Angularjs

Similar "without jQuery" solution as @Vikas-Gulati's, but it works with group by

In my case, group by doesn't work, because my first <option> was without value, just with Please select and item from dropdown text. This is a slightly modified version, that fixes this particular situation:

Usage is simmilar to @Vikas-Gulati answer: https://stackoverflow.com/a/20790905/1268533

Directive

angular.module('disabledModule', [])
    .directive('optionsDisabled', function($parse) {
        var disableOptions = function(scope, attr, element, data, fnDisableIfTrue) {
            var realIndex = 0;
            angular.forEach(element.find("option"), function(value, index){
                var elem = angular.element(value);
                if(elem.val()!="") {
                    var locals = {};
                    locals[attr] = data[realIndex];
                    realIndex++; // this skips data[index] with empty value (IE first <option> with 'Please select from dropdown' item)
                    elem.attr("disabled", fnDisableIfTrue(scope, locals));
                }
            });
        };
        return {
            priority: 0,
            require: 'ngModel',
            link: function(scope, iElement, iAttrs, ctrl) {
                // parse expression and build array of disabled options
                var expElements = iAttrs.optionsDisabled.match(/^\s*(.+)\s+for\s+(.+)\s+in\s+(.+)?\s*/);
                var attrToWatch = expElements[3];
                var fnDisableIfTrue = $parse(expElements[1]);
                scope.$watch(attrToWatch, function(newValue, oldValue) {
                    if(newValue)
                        disableOptions(scope, expElements[2], iElement, newValue, fnDisableIfTrue);
                }, true);
                // handle model updates properly
                scope.$watch(iAttrs.ngModel, function(newValue, oldValue) {
                    var disOptions = $parse(attrToWatch)(scope);
                    if(newValue)
                        disableOptions(scope, expElements[2], iElement, disOptions, fnDisableIfTrue);
                });
            }
        };
    });

Solution 10 - Angularjs

As I cannot upgrade to latest angularJS, so created a simpler directive to handle it.

.directive('allowDisabledOptions',['$timeout', function($timeout) {
    return function(scope, element, attrs) {
        var ele = element;
        var scopeObj = attrs.allowDisabledOptions;
        $timeout(function(){
            var DS = ele.scope()[scopeObj];
            var options = ele.children();
            for(var i=0;i<DS.length;i++) {
                if(!DS[i].enabled) {
                    options.eq(i).attr('disabled', 'disabled');
                }
            }
        });
    }
}])

for more details: https://github.com/farazshuja/disabled-options

Solution 11 - Angularjs

I also hid disabled options adding fallowing line:

$(this).css("display", fnDisableIfTrue(scope, locals) ? "none" : "block");

It was necessary as I couldn't simply filter them out, as the initial value of this select could be one of the disabled options.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioniLemmingView Question on Stackoverflow
Solution 1 - AngularjsBartView Answer on Stackoverflow
Solution 2 - AngularjsVikas GulatiView Answer on Stackoverflow
Solution 3 - AngularjslodView Answer on Stackoverflow
Solution 4 - AngularjslucumaView Answer on Stackoverflow
Solution 5 - AngularjsWasmooView Answer on Stackoverflow
Solution 6 - AngularjsdafyddPrysView Answer on Stackoverflow
Solution 7 - AngularjsSebastian CastaldiView Answer on Stackoverflow
Solution 8 - AngularjsRaja RathinamView Answer on Stackoverflow
Solution 9 - AngularjsDHlavatyView Answer on Stackoverflow
Solution 10 - AngularjsFarazShujaView Answer on Stackoverflow
Solution 11 - AngularjsKamilaView Answer on Stackoverflow