Customize ng-options selection look
Asked Answered
D

5

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]")
Dearborn answered 8/7, 2014 at 12:0 Comment(2)
yes, you could do this with ng-class and an expression.Selmore
Could you please give me an example? How do you add the function?Dearborn
L
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.

Lonely answered 8/7, 2014 at 12:12 Comment(16)
In my example I bing myCompany to the object company...Will that work?Dearborn
How can I display contract.number but bind the object contract?Dearborn
Like I said, if you use <option> you can't bind to objects. Maybe you could bind to the company id (so you can retrieve the company) and display the company number: <option value="{{company.id}}" ...>{{company.number}}</option>Lonely
I think I gor my answer there: #15264551Dearborn
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.Lonely
@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.Lonely
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...Dearborn
@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 :)Lonely
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)Dearborn
@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 ?Lonely
Right! I think I know where it comes from... I'm using angularUI modal view. I will try to reproduce it on fiddleDearborn
Sorry, but I can't figure out how to do that on fiddle...Could you please help me?Dearborn
@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: plnkr.co/edit/tpl:FrTqqTNoY8BEfHs9bB0fLonely
Thanks! I tried and got to this point(still not opening the modal view though): plnkr.co/edit/FzEHUFFAQODFOYaAUdtK?p=previewDearborn
@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: 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 <select> itself is styled (not just the <option>s: ng-class="{true:'active',false:'inactive'}[status.value]"Lonely
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.Dearborn
M
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

Misplace answered 8/7, 2014 at 12:12 Comment(7)
Because there is only one option and you are ngRepeating on the select, which will create multiple selects each with one option...Lonely
sorry, you were right. with select the ng-repeat doesn't work the same as on span or divs. i corrected my solution.Selmore
same question as ExpertSystem: How can I display contract.number but bind the object contract?Dearborn
in your option just use <option>{{company.contract.number}}</option>Selmore
@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 :))Lonely
Please see the following: #15264551Dearborn
yes sure you can also write a directive or you can write an inline expression like this: <option ng-class="{ 'green' : company.active, 'blue': !company.active }">...</option> @ExpertSystem Thanks, I noticed it ;)Selmore
D
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.

Diabetes answered 12/4, 2016 at 14:35 Comment(0)
A
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; }
Agrology answered 8/7, 2014 at 12:7 Comment(2)
I think they mean based on an expression evaluated in the context of AngularJS...Lonely
Exactly... I need to base the expression on $scope.company.activeDearborn
S
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>
Secretarial answered 16/4, 2017 at 15:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.