Как использовать ng-класс в select с ng-options

У меня есть массив объектов Person

var persons = [
{Name:'John',Eligible:true},
{Name:'Mark',Eligible:true},
{Name:'Sam',Eligible:false},
{Name:'Edward',Eligible:false},
{Name:'Michael',Eligible:true}
];

и я использую select с ng-options как это:

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

Я хочу показать запись с правомочным:false красным цветом. Так что проблема в том, как я могу использовать ng-class в select чтобы добиться этого? Поскольку мы не используем какие-либо option пометить не получится, если я просто добавлю ng-class в select сам элемент.

9 ответов

Решение

Вы можете создать директиву, которая обрабатывает параметры после обработки директивы ngOptions, которая обновляет их соответствующими классами.

Обновление: в старом коде было несколько ошибок, и я немного узнал с тех пор, как ответил на этот вопрос. Вот Plunk, который был переделан в 1.2.2 (но должен работать и в 1.0.X)

Вот обновленный код:

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);
            }
          });
        });
      });
    }
  };
});

Вот как бы вы использовали это в своей разметке:

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

Он работает так же, как и ng-класс, за исключением того, что он действует для каждого элемента в коллекции.

В этом сценарии вы можете применять только ng-класс, только если вы используете ng-repeat с тегами опций:

<select ng-model="Blah">
  <option ng-repeat="person in persons" ng-class="{red: person.Eligible}">{{person.Name}}</option>  
</select>

Это даст индивидуальный класс вашим "подходящим" сотрудникам, но CSS не будет работать одинаково для всех игроков.

Плункер.

Я хотел прокомментировать принятый ответ, но, поскольку у меня недостаточно очков репутации, я должен добавить ответ. Я знаю, что это старый вопрос, но комментарии, которые недавно добавлены к принятому ответу.

Для angularjs 1.4.x предложенная директива должна быть адаптирована, чтобы она снова заработала. Из-за критического изменения в ngOptions значение параметра больше не является индексом, поэтому строка

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

больше не будет работать

Если вы измените код в плунжере на

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

В результате значение тега option теперь будет

value="number:x" (x - это идентификатор объекта item)

Измените директиву на

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

чтобы это снова заработало.

Конечно, это не общее решение, потому что, если у вас нет идентификатора в вашем объекте? Тогда вы найдете value="object:y" в вашем теге option, где y - число, сгенерированное angularjs, но с этим y Вы не можете сопоставить с вашими предметами.

Надеется, что это поможет некоторым людям снова начать работать после обновления angularjs до 1.4.x

Я пытался также использовать track byв ng-options, но не заставил его работать. Может быть, люди с большим опытом в angularjs, чем я (= мой первый проект в angularjs)?

Принятый ответ не работает для меня, поэтому я нашел альтернативу без специальной директивы, использующей track by:

<select ng-model="foo" ng-options="x.name for x in items track by x.eligible"></select>

Каждый параметр теперь получает значение x.elitable. В CSS вы можете стилизовать параметры со значением = true (я думаю, что true должно быть строкой). CSS:

option[value="true"]{
    color: red;
}

Если вы хотите не только показать их красным цветом, но и запретить пользователю выбирать опции, вы можете использовать disable when:

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

Затем вы можете использовать CSS, чтобы установить цвет отключенных опций.

Директива односторонняя, но я использовал собственный фильтр. Если вы знаете, как выбрать свой элемент, вам будет хорошо здесь. Задача состояла в том, чтобы найти текущий элемент option внутри select. Я мог бы использовать селектор "содержит", но текст в опциях не может быть уникальным для элементов. Чтобы найти параметр по значению, я ввел область действия и сам элемент.

<select ng-model="foo" ng-options="item.name|addClass:{eligible:item.eligible,className:'eligible',scope:this,item:item} for item in items"></select>

и в 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}
  ];
 });

Здесь вы можете увидеть, как это работает.

Я не могу написать это как комментарий из-за своей репутации, но я обновил планетарий для принятого ответа для работы с Angular 1.4.8. Спасибо Бену Лешу за оригинальный ответ, он мне очень помог. Разница, кажется, в том, что более новый Angular генерирует такие опции:

<option class="is-eligible" label="foo" value="object:1">foo</option>

так что код

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

не смогу найти вариант. Мое изменение анализирует ngOptions и определяет, какое поле элемента использовалось для метки, и находит параметр на основе этого вместо значения. Увидеть:

http://plnkr.co/edit/MMZfuNZyouaNGulfJn41

Я знаю, что немного опоздал на вечеринку, но для людей, которые хотят решить эту проблему с помощью чистого CSS, без использования директивы, вы можете создать класс css следующим образом:

    select.blueSelect option[value="false"]{
    color:#01aac7;
}

Это правило css гласит: Найти все элементы со значением = false с именем тега 'option' внутри каждого 'select', который имеет класс "blueSelect", и сделать цвет текста #01aac7; (оттенок синего)

В вашем случае ваш HTML будет выглядеть так:

   <select class="form-control blueSelect" name="persons" id="persons1"
                ng-options="person as person.name for person in $ctrl.persons track by person.Eligible"
                ng-model="$ctrl.selectedPerson" required>
            <option disabled selected value="">Default value</option>
   </select>

Отслеживание внутри ng-параметров - это то, что будет содержать то, за чем нужно отслеживать параметры, или поле "значение" каждого параметра. Обратите внимание, что в зависимости от потребностей вашего проекта вам, возможно, придется внести некоторые изменения, чтобы сделать эту работу в соответствии с вашими требованиями.

Но это не сработает, когда есть несколько вариантов с одинаковым значением для поля Допустимые. Таким образом, чтобы это работало, мы создаем составное выражение для отслеживания, таким образом мы можем иметь уникальные значения для отслеживания в каждой опции. В этом случае мы объединяем оба поля Имя и Допустимые

Так что теперь наш HTML будет выглядеть так

<select class="form-control blueSelect" name="persons" id="persons2"
                ng-options="person as person.name for person in $ctrl.persons track by (person.name + person.Eligible)"
                ng-model="$ctrl.selectedPerson" required>
            <option disabled selected value="">Default value</option>
   </select>

и наш css:

    select.blueSelect option[value*="False"]{
     color:#01aac7;
 }

Обратите внимание на * рядом со значением, это регулярное выражение, которое означает, что слово "False" находится где-то в поле значения элемента option.

Быстрое редактирование Вы также можете отключить опции с помощью Elitable = False, используя "отключить когда" в выражении ng-options, например:

отключение метки при отключении значения в массиве track с помощью trackexpr

Я оставлю, как использовать это в вашем случае, чтобы вы узнали;-)

Это работает для простых модификаций CSS, для более сложных вещей вам могут понадобиться директива или другие методы. Проверено в хром.

Я надеюсь, что это помогает кому-то там.:-)

Я нашел другой обходной путь, который был проще, чем добавление директивы или фильтра, а именно добавление обработчика для события onfocus, применяющего стиль.

angular.element('select.styled').focus( function() {
  angular.element(this).find('option').addClass('myStyle');
});
Другие вопросы по тегам