How to use ng-class in select with ng-options

I have an array of Person objects

var persons = [
{Name:'John',Eligible:true},
{Name:'Mark',Eligible:true},
{Name:'Sam',Eligible:false},
{Name:'Edward',Eligible:false},
{Name:'Michael',Eligible:true}
];

and i am using select with ng-options like this:

<select ng-model="Blah" ng-options="person.Name for person in persons"></select>

I want to show the record with Eligible:false in red color. So the problem is how do i use the ng-class in select inorder to achieve this? Since we are not using any option tag it wont work if i simply add ng-class in the select element itself.

You could create a directive that processed the options after the ngOptions directive is processed that updated them with the appropriate classes.

Update: The old code had a few bugs, and I've learned a bit since I answered this question. Here is a Plunk that was redone in 1.2.2 (but should work in 1.0.X as well)

Here is updated the Code:

app.directive('optionsClass', function ($parse) {
  return {
    require: 'select',
    link: function(scope, elem, attrs, ngSelect) {
      // get the source for the items array that populates the select.
      var optionsSourceStr = attrs.ngOptions.split(' ').pop(),
      // use $parse to get a function from the options-class attribute
      // that you can use to evaluate later.
          getOptionsClass = $parse(attrs.optionsClass);

      scope.$watch(optionsSourceStr, function(items) {
        // when the options source changes loop through its items.
        angular.forEach(items, function(item, index) {
          // evaluate against the item to get a mapping object for
          // for your classes.
          var classes = getOptionsClass(item),
          // also get the option you're going to need. This can be found
          // by looking for the option with the appropriate index in the
          // value attribute.
              option = elem.find('option[value=' + index + ']');

          // now loop through the key/value pairs in the mapping object
          // and apply the classes that evaluated to be truthy.
          angular.forEach(classes, function(add, className) {
            if(add) {
              angular.element(option).addClass(className);
            }
          });
        });
      });
    }
  };
});

Here's how you'd use it in your markup:

<select ng-model="foo" ng-options="x.name for x in items" 
   options-class="{ 'is-eligible' : eligible, 'not-eligible': !eligible }"></select>

It works like ng-class does, with the exception that it's on a per-item-in-the-collection basis.

In this scenario you can only apply ng-class only if you use ng-repeat with option tags:

<select ng-model="Blah">
  <option ng-repeat="person in persons" ng-class="{red: person.Eligible}">{{person.Name}}</option>  
</select>

This will give custom class to your 'Eligible' persons, but CSS won't work consistently across bowsers.

Plunker.

Use filter, it's a better way to manipulate ng-options with restrictions.

check this

The directive is one way, but I used a custom filter. If you know how to select your element, you should be fine here. The challenge was to find the current option element inside the select. I could have used the "contains" selector but the text in the options may not be unique for items. To find the option by value, I injected the scope and the item itself.

<select ng-model="foo" ng-options="item.name|addClass:{eligible:item.eligible,className:'eligible',scope:this,item:item} for item in items"></select>

and in the js:

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

app.filter('addClass', function() {
  return function(text, opt) {
    var i;
    $.each(opt.scope.items,function(index,item) {
      if (item.id === opt.item.id) {
        i = index;
        return false;
      }
    });
    var elem = angular.element("select > option[value='" + i + "']");
    var classTail = opt.className;
    if (opt.eligible) {
      elem.addClass('is-' + classTail);
      elem.removeClass('not-' + classTail);
    } else {
      elem.addClass('not-' + classTail);
      elem.removeClass('is-' + classTail);
    }
    return text;
  }
})

app.controller('MainCtrl', function($scope) {
  $scope.items = [
    { name: 'foo',id: 'x1',eligible: true},
    { name: 'bar',id: 'x2',eligible: false}, 
    { name: 'test',id: 'x3',eligible: true}
  ];
 });

Here you can see it work.

The accepted answer did not work for me, so I found an alternative without a custom directive using track by :

<select ng-model="foo" ng-options="x.name for x in items track by x.eligible"></select>

Each option now gets the value x.eligible. In CSS you can style options with value = true (I think true has to be a string). CSS:

option[value="true"]{
    color: red;
}