7

I'm using a ng-options for a select dropdown menu. I would like to use different color for an option depending on a condition:

select(ng-model='myCompany', ng-options='company.code as company.name for company in companies' **if company.active -> text-color=green**)

Is it possible to do that?

Edit (my jade code):

 form(role='form', name='addForm', novalidate, rc-submit="add()")
    .form-group
        div.row
            div.col-xs-12.col-md-3
                select.form-control(ng-model='form.contract', ng-options='contract as contract.number for contract in contracts', options-class="{true:'active',false:'inactive'}[active]")
Nate
  • 7,606
  • 23
  • 72
  • 124

5 Answers5

10

If you only need to bind the select to string values (not object), you can easily achieve what you want by using ngRepeated <option> elements (instead of ngOptions):

<select ng-model="color">
  <option value="">--- Select a color ---</option>
  <option value="{{ c }}" style="background-color:{{ c }}" ng-repeat="c in colors">
    {{ c }}
  </option>
</select>

If you are in for a little custom directive, you can implement it like this:

app.directive('optionClassExpr', function ($compile, $parse) {
  const NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
  
  return {
    restrict: 'A',
    link: function optionClassExprPostLink(scope, elem, attrs) {
      const optionsExp = attrs.ngOptions;
      if (!optionsExp) return;
      
      const match = optionsExp.match(NG_OPTIONS_REGEXP);
      if (!match) return;
      
      const values = match[7];
      const classExpr = $parse(attrs.optionClassExpr);
      
      scope.$watchCollection(() => elem.children(), newValue => {
        angular.forEach(newValue, child => {
          const child = angular.element(child);
          const val   = child.val();
          if (val) {
            child.attr('ng-class', `${values}[${val}].${attrs.optionClassExpr}`);
            $compile(child)(scope);
          }
        });
      });
    }
  };
});

And use it like this:

<select
    ng-model="someObj"
    ng-options="obj as obj.color for obj in objects"
    option-class-expr="color">
</select>

See, also, this updated short demo.

gkalpak
  • 47,844
  • 8
  • 105
  • 118
  • In my example I bing myCompany to the object company...Will that work? – Nate Jul 08 '14 at 12:22
  • How can I display contract.number but bind the object contract? – Nate Jul 08 '14 at 12:28
  • 1
    Like I said, if you use `` – gkalpak Jul 08 '14 at 12:37
  • I think I gor my answer there: http://stackoverflow.com/questions/15264051/how-to-use-ng-class-in-select-with-ng-options – Nate Jul 08 '14 at 12:38
  • Yeah (I am trying to work out something similar). The solution feels a little hacky and won't work if you use `track by` in the `ngOptions`, but it might work for you. – gkalpak Jul 08 '14 at 12:42
  • @ncohen: Did you take a look at my updated answer ? It uses the regular expression used internally by Angular for parsing `ngOptions` expressions, so it will also recognize `track by` etc. – gkalpak Jul 12 '14 at 13:26
  • Looks good but I still have a problem with this approach... in my case, my variable 'color' is a boolean (active: true/false) and therefore doesn't contain a string that could be used as a class. how would you change your directive? I tried to custom it myself but I don't get what I want. I would like to get ng-class=inactive if it's set to false and ng-class=active if set to true... – Nate Jul 30 '14 at 13:14
  • @ncohen: In such cases it usually makes more sense to change the data. In any case I updated the fiddle with a third approach, introducing a slightly modified version of the directive (`optionsClassExprStatic`). This new directive is static in the sense that it does not adapt to dymanic changes in the data. On the bright side, it can evaluate any Angular expression (the expressions are evaluated in the context of each `option`'s value). <- let me know if you need further clarification :) – gkalpak Jul 30 '14 at 14:45
  • I tried your directive (third approach) my app and have the following problem... my scope doesn't have statuses (in my app it's contracts) so scope.$eval(match[7]) is undefined (I checked match and it has the right values!). Why is that? I have the the jade code (please see my original question) – Nate Aug 05 '14 at 07:30
  • @ncohen: You mean `match[7] === 'contracts'`, but `scope.$eval(match[7])` yields `undefined` ? That's strange. Do you have an isolate scope on the element ? Could you reproduce it in a fiddle ? – gkalpak Aug 05 '14 at 08:03
  • Right! I think I know where it comes from... I'm using angularUI modal view. I will try to reproduce it on fiddle – Nate Aug 05 '14 at 08:12
  • Sorry, but I can't figure out how to do that on fiddle...Could you please help me? – Nate Aug 05 '14 at 08:34
  • @ncohen: You could either use my fiddle as a base and change what is necessary. But if it's your first time, using plunkr is more easy (just create the files you need (.html, .js etc), put your code in them and you are good to go. You can use this as a base: http://plnkr.co/edit/tpl:FrTqqTNoY8BEfHs9bB0f – gkalpak Aug 05 '14 at 08:43
  • Thanks! I tried and got to this point(still not opening the modal view though): http://plnkr.co/edit/FzEHUFFAQODFOYaAUdtK?p=preview – Nate Aug 05 '14 at 09:13
  • @ncohen: There were some issues (e.g. you were including the Bootstrap JS (you should not when using UI-Bootstrap), you were not including ui-bootstrap.js from a CDN, you were not binding the `ModelInstanceCtrl` correctly etc). I fixed them and everything seems to work fine: http://plnkr.co/edit/bexVy9I54l2cvpU8tQcB?p=preview (BTW, if my answer turns out useful, don't forget to accept it and/or upvote it :)) (BTW2, I made a small addition in `modal.html` so that the ` – gkalpak Aug 05 '14 at 10:25
  • OK I think I understand now... my select is disabled and only when my array is available, it gets enabled but your directive is called at the beginning. – Nate Aug 05 '14 at 16:03
3

Define your CSS classes:

.green {
    background-color: green;   
}

.blue {
    background-color: blue;
}

and use a function as an angular expression:

$scope.getCompanyClass = function(company)
{
   return company.active ? "green" : "blue";
}

and in your html:

<select>
    <option data-ng-repeat="company in companies" data-ng-class='getCompanyClass(company)'>...</option>
</select>

and the working example as jsfiddle

Raphael Müller
  • 2,180
  • 2
  • 15
  • 20
  • Because there is only one `option` and you are `ngRepeat`ing on the `select`, which will create multiple `selects` each with one option... – gkalpak Jul 08 '14 at 12:16
  • sorry, you were right. with select the ng-repeat doesn't work the same as on span or divs. i corrected my solution. – Raphael Müller Jul 08 '14 at 12:22
  • same question as ExpertSystem: How can I display contract.number but bind the object contract? – Nate Jul 08 '14 at 12:29
  • in your option just use `` – Raphael Müller Jul 08 '14 at 12:32
  • 1
    @RaphaelMüller: Actually, `ngRepeat` works exactly the same on `select` and `span`/`div`. It repeats the element it is on. If you want to repeat a `span` you put it on the `span`. Likewise, if you want to repeat an `option` you put it on the `option`. And if you want to repeat a `select` you put it on the `select` :)) – gkalpak Jul 08 '14 at 12:38
  • Please see the following: http://stackoverflow.com/questions/15264051/how-to-use-ng-class-in-select-with-ng-options – Nate Jul 08 '14 at 12:39
  • yes sure you can also write a directive or you can write an inline expression like this: `` @ExpertSystem Thanks, I noticed it ;) – Raphael Müller Jul 08 '14 at 12:46
1

There is a neat solution for the special case that you want to show disabled options in a certain color. You can use disable when:

ng-options='c.code as c.name disable when !c.active for c in companies'

You can then use CSS to match on the disabled attribute and style the respective options the way you like.

lex82
  • 11,173
  • 2
  • 44
  • 69
0

Please see here http://jsbin.com/wahak/2/edit?html,css,console,output you can do that using css

CSS:

select { color: red; }
option:not(:checked) { color: black; }
sylwester
  • 16,498
  • 1
  • 25
  • 33
0

This is how I am colouring the ng-options. I am using my own example.

var app = angular.module("app", []);
app.controller("homeController", [homeController]);

function homeController() {
    var vm = this;
    vm.differentOptions = [
        { name: "External Visitors", color: "Red" },
        { name: "Internal Visitors", color: "Green" },
        { name: "Other Visitors", color: "Gray" },
        { name: "Extrateresstrial Visitors", color: "Yellow" }
    ];
}

angular.module("app").directive("colorTheOptions", colorTheOptions);
function colorTheOptions($timeout) {
    return {
        link: function (scope, element) {
            $timeout(function () {
                var options = $("option", element);
                options.each(function (index, eachOption) {
                    $eachOption = $(eachOption);
                    var optionText = $eachOption.text();
                    if (optionText) {
                        for (var i = 0; i < scope.vm.differentOptions.length; i++) {
                            var eachAngularOption = scope.vm.differentOptions[i];
                            if (eachAngularOption.name === optionText) {
                                $eachOption.addClass(eachAngularOption.color);
                            }
                        }
                    }
                });
            });
        }
    }
}
.Red {
    color: red;
}

.Green {
    color: green;
}

.Gray {
    color: gray;
}

.Yellow {
    color: yellow;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="container" ng-app="app">
    <div class="row" ng-controller="homeController as vm">
        <select class="form-control" color-the-options
                ng-options="option.name for option in vm.differentOptions"
                ng-model="vm.selectedOption"></select>
    </div>
</div>
Karthik
  • 1,447
  • 1
  • 18
  • 26