web-dev-qa-db-fra.com

Comment utiliser ng-class dans select avec ng-options

J'ai un tableau d'objets Person

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

et j'utilise select avec ng-options comme ceci:

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

Je veux montrer l'enregistrement avec Éligible: faux en rouge couleur. Le problème est donc de savoir comment utiliser le ng-class dans select pour y parvenir? Comme nous n'utilisons aucune balise option, cela ne fonctionnera pas si j'ajoute simplement ng-class dans l'élément select lui-même.

36

Vous pouvez créer une directive qui a traité les options après le traitement de la directive ngOptions qui les a mises à jour avec les classes appropriées.

Mise à jour : L'ancien code avait quelques bugs, et j'ai appris un peu depuis que j'ai répondu à cette question. Voici un Plunk qui a été refait en 1.2.2 (mais devrait aussi fonctionner en 1.0.X)

Voici mis à jour le Code:

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

Voici comment vous l'utiliseriez dans votre balisage:

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

Il fonctionne comme le fait ng-class, à l'exception du fait qu'il se fait par article dans la collection.

33
Ben Lesh

Dans ce scénario, vous ne pouvez appliquer la classe ng que si vous utilisez ng-repeat avec des balises d'option:

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

Cela donnera une classe personnalisée à vos personnes "éligibles", mais CSS ne fonctionnera pas de manière cohérente entre les bowsers.

Plunker .

13
Stewie

Je voulais commenter la réponse acceptée, mais comme je n'ai pas assez de points de réputation, je dois ajouter une réponse. Je sais que c'est une vieille question, mais des commentaires ont récemment été ajoutés à la réponse acceptée.

Pour angularjs 1.4.x, la directive proposée doit être adaptée pour la faire fonctionner à nouveau. En raison du changement de rupture dans ngOptions, la valeur de l'option n'est plus l'index, donc la ligne

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

ne fonctionnera plus.

Si vous changez le code dans le plunker en

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

En conséquence, la valeur de la balise option sera désormais

value="number:x" (X est l'id de l'objet item)

Remplacez la directive par

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

pour le faire fonctionner à nouveau.

Bien sûr, ce n'est pas une solution générique, car que faire si vous n'avez pas d'identifiant dans votre objet? Ensuite, vous trouverez value="object:y" Dans votre balise d'option où y est un nombre généré par angularjs, mais avec ce y vous ne pouvez pas mapper à vos éléments.

J'espère que cela aidera certaines personnes à faire fonctionner à nouveau leur code après la mise à jour d'angularjs vers 1.4.x

J'ai également essayé d'utiliser le track by Dans ng-options, mais je ne l'ai pas fait fonctionner. Peut-être que les gens avec plus d'expérience dans angularjs que moi (= mon premier projet dans angularjs)?

4
evb

Si vous souhaitez non seulement les afficher en rouge mais empêcher l'utilisateur de sélectionner les options, vous pouvez utiliser disable when:

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

Vous pouvez ensuite utiliser CSS pour définir la couleur des options désactivées.

3
lex82

La directive est à sens unique, mais j'ai utilisé un filtre personnalisé. Si vous savez comment sélectionner votre élément, tout va bien ici. Le défi était de trouver l'élément d'option actuel dans la sélection. J'aurais pu utiliser le sélecteur "contient" mais le texte dans les options peut ne pas être unique pour les articles. Pour trouver l'option par valeur, j'ai injecté la portée et l'élément lui-même.

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

et dans le 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}
  ];
 });

Ici, vous pouvez voir le fonctionnement .

3
westor

La réponse acceptée ne fonctionnait pas pour moi, j'ai donc trouvé une alternative sans directive personnalisée en utilisant track by:

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

Chaque option obtient désormais la valeur x.eligible. En CSS, vous pouvez styliser les options avec value = true (je pense que true doit être une chaîne). CSS:

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

Je sais que je suis un peu en retard à la fête, mais pour les personnes qui veulent résoudre cela avec du CSS pur, sans utiliser de directive, vous pouvez créer une classe CSS comme celle-ci:

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

Cette règle css dit: Trouvez tous les éléments avec value = false avec le nom de balise 'option' dans chaque 'select' qui a une classe "blueSelect" et faites la couleur du texte # 01aac7; (une nuance de bleu)

Dans votre cas, votre code HTML ressemblera à ceci:

   <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>

La piste par à l'intérieur des ng-options est ce qui contiendra quoi pour suivre les options, ou le champ "valeur" de chaque option. Notez que selon les besoins de votre projet, vous devrez peut-être faire quelques ajustements pour que cela fonctionne selon vos besoins.

Mais cela ne fonctionnera pas correctement lorsqu'il existe plusieurs options avec la même valeur pour le champ Éligible. Donc, pour que cela fonctionne, nous créons une expression composée à suivre, de cette façon, nous pouvons avoir des valeurs uniques à suivre dans chaque option. Dans ce cas, nous combinons les deux champs Nom et Éligible

Alors maintenant, notre html ressemblera à ceci

<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>

et notre css:

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

Remarquez le * à côté de value, c'est une expression régulière qui signifie trouver le mot "False" quelque part dans le champ value de l'élément option.

Édition rapide Vous pouvez également choisir de désactiver les options avec Eligible = False en utilisant le "désactiver quand" dans l'expression ng-options, par exemple:

label désactiver lorsque désactiver for value in array track by trackexpr

Je vais laisser comment l'utiliser dans votre cas pour que vous le découvriez ;-)

Cela fonctionne pour les modifications CSS simples, pour les choses plus complexes, vous pourriez avoir besoin d'une directive ou d'autres méthodes. Testé en chrome.

J'espère que cela aide quelqu'un là-bas. :-)

2
RGonzalez

Je ne peux pas écrire ceci en tant que commentaire, en raison de la réputation, mais j'ai mis à jour le plongeur pour que la réponse acceptée fonctionne avec Angular 1.4.8. Merci à Ben Lesh pour la réponse originale, cela m'a beaucoup aidé. La différence semble être que le nouveau Angular génère des options comme celle-ci:

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

donc le code

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

ne serait pas en mesure de trouver l'option. Ma modification analyse ngOptions et détermine quel champ d'élément a été utilisé pour l'étiquette et trouve l'option basée sur cela au lieu de valeur. Voir:

http://plnkr.co/edit/MMZfuNZyouaNGulfJn41

2
Shelby Robertson