AngularJS: Why does $element.find('option') return an empty array when using ng-options?

1k views Asked by At

In Angular 1.4, I am using the ngOptions directive to populate a <select> tag with <option> tags based on an object that looks like this:

{black: '#000000', red: '#FF0000', ...}

That works fine using ng-options="value as key for (key, value) in vm.colors", but now I want to add a class to each option tag that matches the key (e.g. '.black', '.red'). My thought was to simply find the option elements and use addClass(), but I'm having trouble getting those elements. (Note: I'm not using jQuery and would like to avoid adding it just for this.)

Here is a jsfiddle.

I expect to be able to bind the results of $element.find('option') to my view model and then watch it using $scope.$watch('vm.options', function() {...}), but when I log vm.options to the console all I am seeing is an empty array.

Am I using $scope.$watch incorrectly here? Is it an issue with $element? Are the elements within the scope of ngOptions unreachable from my controller? Or am I just making a stupid mistake?

Any help would be greatly appreciated...thanks in advance!

2

There are 2 answers

0
Shaun Scovil On BEST ANSWER

Okay, so I was trying to watch the results of a function that returned option elements, but what I should have done was watch a function that returned the length property: $element.find('option').length.

Here is a jsfiddle that behaves as I had originally expected.

The clue that ultimately tipped me off was this comment in the ngOptions code, which reminded me that "ngModel only watches for object identity change".

That answers my question, but as @jme11 pointed out, you can't style option elements so it doesn't help me achieve my goal.

However, this looks like it might: https://github.com/angular-ui/ui-select

4
jme11 On

The docs are pretty clear that DOM manipulation should only be done in a directive. I use this rule of thumb: if I find myself wanting or needing to use angular.element, then I need a directive.

In this case, you can use the built in ngStyle directive:

(function() {
    'use strict';
    
    var hexColors = {
        black : '#000000',
        red   : '#FF0000',
        green : '#00FF00',
        blue  : '#0000FF'
    };
    
    angular.module('sandbox', [])
        .constant('hexColors', hexColors)
        .controller('SandboxController', SandboxController)
    ;
    
    function SandboxController($element, $scope) {
        var vm = this;
        vm.colors = hexColors;
        vm.selected = '#000000';
       
    }
    
})();
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>

<div ng-app="sandbox" ng-controller="SandboxController as vm">
    <select ng-model="vm.selected" ng-options="value as key for (key, value) in vm.colors"></select>
    <p ng-style="{'background-color': vm.selected, color: 'white'}">Hex: {{ vm.selected }}</p>
</div>