Wie kann man eine Klasse am besten bedingt anwenden?

Nehmen wir an, Sie haben ein Array, das in einem "ul" mit einem "li" für jedes Element und eine Eigenschaft auf dem Controller namens "SelectedIndex" gerendert wird. Was wäre der beste Weg, um eine Klasse zum "li" mit dem Index "selectedIndex" in AngularJS hinzuzufügen?

Ich dupliziere derzeit (von Hand) den "li"-Code und füge die Klasse zu einem der "li"-Tags hinzu und verwende "ng-show" und "ng-hide", um nur ein "li" pro Index anzuzeigen.

Lösung

Wenn Sie die CSS-Klassennamen nicht wie ich in den Controller eingeben wollen, gibt es einen alten Trick, den ich seit den Tagen vor v1 verwende. Wir können einen Ausdruck schreiben, der direkt zu einem Klassennamen ausgewählt ausgewertet wird, es sind keine benutzerdefinierten Direktiven erforderlich:

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

Bitte beachten Sie die alte Syntax mit Doppelpunkt.

Es gibt auch eine neue, bessere Möglichkeit, Klassen bedingt anzuwenden, wie z.B.:

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

Angular unterstützt nun Ausdrücke, die ein Objekt zurückgeben. Jede Eigenschaft (Name) dieses Objekts wird nun als Klassenname betrachtet und abhängig von seinem Wert angewendet.

Allerdings sind diese Wege nicht funktional gleich. Hier ist ein Beispiel:

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

Wir könnten also bestehende CSS-Klassen wiederverwenden, indem wir eine Modelleigenschaft grundsätzlich auf einen Klassennamen abbilden und gleichzeitig CSS-Klassen aus dem Controller-Code heraushalten.

Kommentare (21)

Hier ist eine viel einfachere Lösung:

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>
Kommentare (3)

Ich stand vor kurzem vor einem ähnlichen Problem und beschloss, einfach einen bedingten Filter zu erstellen:

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

Er nimmt ein einzelnes Argument entgegen, das entweder ein Array mit 2 Elementen oder eine Zeichenkette ist, die in ein Array umgewandelt wird, an das eine leere Zeichenkette als zweites Element angehängt wird:

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