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

2019-01-03 10:26发布

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.

9条回答
beautiful°
2楼-- · 2019-01-03 10:52

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.

查看更多
Juvenile、少年°
3楼-- · 2019-01-03 10:52

I wanted to comment on the accepted answer, but because I don't have enough reputation points, I must add an answer. I know that this is an old question, but comments where recently added to the accepted answer.

For angularjs 1.4.x the proposed directive must be adapted to get it working again. Because of the breaking change in ngOptions, the value of the option isn't anymore the index, so the line

option = elem.find('option[value=' + index + ']');

won't work anymore.

If you change the code in the plunker to

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

As result the value of the option tag will now be

value="number:x" (x is the id of the item object)

Change the directive to

option = elem.find('option[value=\'number:' + item.id + '\']');

to get it working again.

Of course this isn't a generic solution, because what if you have not an id in your object? Then you will find value="object:y" in your option tag where y is a number generated by angularjs, but with this y you can't map to your items.

Hopes this helps some people to get their code again working after the update of angularjs to 1.4.x

I tried also to use the track byin ng-options, but didn't get it to work. Maybe people with more experience in angularjs then me (= my first project in angularjs)?

查看更多
叛逆
4楼-- · 2019-01-03 10:59

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.

查看更多
放荡不羁爱自由
5楼-- · 2019-01-03 11:00

In case you not only want to show them in red color but prevent the user from selecting the options, you can use disable when:

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

You can then use CSS to set the color of disabled options.

查看更多
ゆ 、 Hurt°
6楼-- · 2019-01-03 11:01

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.

查看更多
爷、活的狠高调
7楼-- · 2019-01-03 11:01

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;
}
查看更多
登录 后发表回答