StackOverflow_24631325: customize-ng-options-selection-look

Illustration of answer to http://stackoverflow.com/questions/24631325/customize-ng-options-selection-look.

by ExpertSystem

HTML

<script src="http://code.angularjs.org/1.2.19/angular.min.js"></script>
<div ng-controller="myCtrl">
    <h4>
        Approach 1 - 
        <small>(Using &lt;option&gt; and binding to strings)</small>
    </h4>
    <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>
    <p>Selected color: <b style="color:{{color}}">{{color}}</b></p>
    <hr />
    <h4>
        Approach 2 - 
        <small>(Using ngOptions and binding to objects)</small>
    </h4>
    <select ng-model="obj" ng-options="o as o.color for o in objects"
            option-class-expr="color.toLowerCase()">
        <option value="">--- Select a color ---</option>
    </select>
    <p>Selected object: <b style="color:{{obj.color}}">{{obj | json}}</b></p>
    <button ng-click="addBlackAndWhite()" ng-disabled="added"
            title="Adds a Black and a White option">
        Add Black & White
    </button>
    <button ng-click="revBlackAndWhite()" ng-disabled="!added||reversed"
            title="Reverses the Black and the White option">
        Reverse Black & White
    </button>
    <br />
    <small>
        <ol>
            The buttons are used just to showcase live updating of classes, when:
            <li>either new objects are added or</li>
            <li>existing objects are updated</li>
        </ol>
    </small>
    <hr />
    <h4>
        Approach 3 -
        <small>(Similar to 2 but does NOT adapt to "runtime" changes)</small>
    </h4>
    <select ng-model="status" ng-options="s as s.label for s in statuses"
            option-class-expr-static="{true:'active',false:'inactive'}[value]">
        <option value="">--- Select a status ---</option>
    </select>
    <p>
        Selected status:
        <b ng-class="{active:status.value===true,
                      inactive:status.value===false}">
           ...

CSS

p { background-color: LightGray; }

.blue   { background-color: Blue; }
.cyan   { background-color: Cyan; }
.green  { background-color: Green; }
.orange { background-color: Orange; }
.pink   { background-color: Pink; }
.purple { background-color: Purple; }
.red    { background-color: Red; }

.black {
    background-color: Black;
    color: White;    
}
.White {
    background-color: White;
    color: Black;
}

.active {
    background-color: DarkGreen;
    color: LimeGreen;
}
.inactive {
    background-color: DarkRed;
    color: LightGray;
}

JavaScript

var app = angular.module('myApp', []);
app.controller('myCtrl', function ($scope) {
    $scope.colors = ['Blue', 'Cyan', 'Green', 'Orange', 'Pink', 'Purple', 'Red'];
    $scope.objects = [
        {id: 1, color: 'Blue'},
        {id: 2, color: 'Cyan'},
        {id: 3, color: 'Green'},
        {id: 4, color: 'Orange'},
        {id: 5, color: 'Pink'},
        {id: 6, color: 'Purple'},
        {id: 7, color: 'Red'}
    ];
    $scope.statuses = [
        {label: 'Active',   value: true},
        {label: 'Inactive', value: false}
    ];

    $scope.addBlackAndWhite = function () {
        $scope.objects.push({id: 8, color: 'Black'});
        $scope.objects.push({id: 9, color: 'White'});
        $scope.added = true;
    };

    $scope.revBlackAndWhite = function () {
        $scope.objects[7].color = 'White';
        $scope.objects[8].color = 'Black';
        $scope.reversed = true;
    };
});

app.directive('optionClassExpr', function ($compile, $parse) {
    var 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) {
            var optionsExp = attrs.ngOptions;
            if (!optionsExp) return;
            
            var match = optionsExp.match(NG_OPTIONS_REGEXP);
            if (!match) return;
            
            var values = match[7];

            scope.$watchCollection(function () {
                return elem.children();
            }, function (newValue) {
                angular.forEach(newValue, function (child) {
                    var child = angular.element(child);
                    var val   = child.val();
                    if (val) {
                        child.attr('ng-class', values + '[' + val + '].' +
                                              ...