Qual è il modo migliore per applicare condizionatamente una classe?

Diciamo che hai un array che è reso in un ul con un li per ogni elemento e una proprietà sul controller chiamata selectedIndex. Quale sarebbe il modo migliore per aggiungere una classe al li con l'indice selectedIndex in AngularJS?

Attualmente sto duplicando (a mano) il codice li e aggiungendo la classe a uno dei tag li e usando ng-show e ng-hide per mostrare solo un li per indice.

Soluzione

Se non volete mettere i nomi delle classi CSS in Controller come faccio io, ecco un vecchio trucco che uso fin dai giorni pre-v1. Possiamo scrivere un'espressione che valuta direttamente un nome di classe selezionato, non sono necessarie direttive personalizzate:

ng:class="{true:'selected', false:''}[$index==selectedIndex]"

*Si prega di notare la vecchia sintassi con i due punti.

C'è anche un nuovo modo migliore di applicare le classi in modo condizionale, come:

ng-class="{selected: $index==selectedIndex}"

Angular ora supporta le espressioni che restituiscono un oggetto. Ogni proprietà (nome) di questo oggetto è ora considerata come un nome di classe e viene applicata a seconda del suo valore.

Tuttavia questi modi non sono funzionalmente uguali. Ecco un esempio:

ng-class="{admin:'enabled', moderator:'disabled', '':'hidden'}[user.role]"

Potremmo quindi riutilizzare le classi CSS esistenti fondamentalmente mappando una proprietà del modello in un nome di classe e allo stesso tempo mantenere le classi CSS fuori dal codice del Controller.

Commentari (21)

Ecco una soluzione molto più semplice:

function MyControl($scope){
    $scope.values = ["a","b","c","d","e","f"];
    $scope.selectedIndex = -1;

    $scope.toggleSelect = function(ind){
        if( ind === $scope.selectedIndex ){
            $scope.selectedIndex = -1;
        } else{
            $scope.selectedIndex = ind;
        }
    }

    $scope.getClass = function(ind){
        if( ind === $scope.selectedIndex ){
            return "selected";
        } else{
            return "";
        }
    }

    $scope.getButtonLabel = function(ind){
        if( ind === $scope.selectedIndex ){
            return "Deselect";
        } else{
            return "Select";
        }
    }
}
.selected {
    color:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<div ng-app ng-controller="MyControl">
    <ul>
        <li ng-class="getClass($index)" ng-repeat="value in values" >{{value}} {{getButtonLabel($index)}}</li>
    </ul>
    <p>Selected: {{selectedIndex}}</p>
</div>
Commentari (3)

Ho affrontato un problema simile di recente e ho deciso di creare semplicemente un filtro condizionale:

  angular.module('myFilters', []).
    /**
     * "if" filter
     * Simple filter useful for conditionally applying CSS classes and decouple
     * view from controller 
     */
    filter('if', function() {
      return function(input, value) {
        if (typeof(input) === 'string') {
          input = [input, ''];
        }
        return value? input[0] : input[1];
      };
    });

Prende un singolo argomento, che è o un array di 2 elementi o una stringa, che viene trasformata in un array a cui viene aggiunta una stringa vuota come secondo elemento:

<li ng-repeat="item in products | filter:search | orderBy:orderProp |
  page:pageNum:pageLength" ng-class="'opened'|if:isOpen(item)">
  ...
</li>
Commentari (2)