AngularJS: filter und orderBy in ng-repeat nur mit Array Collection

24. Februar 2014 Web-Entwicklung von Eric Kubenka

In letzter Zeit probiere ich viele Sachen mit AngularJS. Während ich mich in den Basics sicher fühle, stoße ich ab und an noch auf so einige Verständnisprobleme. Zum Beispiel: Warum besteht ein Unterschied zwischen einer object-dotierten und Array-dotierten Liste von Objekten? Wahrscheinlich ist das eher ein JS-Bug/Feature und genau so gewollt, jedoch hat mich AngularJS damit verwirt, dass ng-repeat auf beide Listen problemlos anwendbar ist. Das Filtern und Sortieren hingegen nicht. Doch noch ein mal zum Ursprung dieses Problems.

Unterschied objekt-dotierte und array-dotierte Listen

 

Die wohl am häufigsten verwendete Direktive ist ng-repeat. Zusammen mit einem filter und einem orderBy lassen sich somit client-seitig in Windeseile große Listen filtern und sortieren. Bei der Verwendung einer objekt-dotierten Liste bin ich dann kurzzeitig gescheitert und verzweifelt.

Vereinfacht habe ich folgende Liste vom Server erhalten:

$scope.objectCollection = {
            1: {id: 1, name: 'Eric'},
            2: {id: 2, name: 'Robert'},
            3: {id: 3, name: 'Roberto'},
            4: {id: 4, name: 'Hans'},
            5: {id: 5, name: 'Taylor'},
            6: {id: 6, name: 'Jeff'},
            7: {id: 7, name: 'Mike'},
            8: {id: 8, name: 'Peter'}
        };

Lassen sich mittels ng-repeat noch alle Elemente ausgeben, stieß ich beim filtern und sortieren an die Grenzen von Angular. Eine Stunde hat es mich gekostet, eh ich einfach mal den Versuch nachgestellt habe und mittels folgendem Beispiel den Grund kannte.

Angular kann einfach keine objekt-dotierten Listen filtern und sortieren. Dazu müssen die Objekte in einem Array vorliegen. Der Unterschied zum vorherigen Schnipsel wird hier deutlich (diesmal eine array-Liste von Objekten):

 

$scope.arrayCollection = [
            {id: 1, name: 'Eric'},
            {id: 2, name: 'Robert'},
            {id: 3, name: 'Roberto'},
            {id: 4, name: 'Hans'},
            {id: 5, name: 'Taylor'},
            {id: 6, name: 'Jeff'},
            {id: 7, name: 'Mike'},
            {id: 8, name: 'Peter'}
        ];

Normalerweise würde man nun einfach das Ausgabeformat auf dem Server ändern und ein Array json encoded nach unten durchreichen. Leider habe ich keinen Einfluss auf die Server-Seite gehabt und so musste ich mittels folgendem Snippet aus der Objekt-Liste ein Array anfertigen.

$scope.converted = $.map($scope.objectCollection, function(value, index) {
            return [value];
        });

Damit wurde das Problem erfolgreich gelöst. Der Quellcode zur Verdeutlichung hängt folgend an, kann hier via GitHub eingesehen werden und hier als Live-Demo.

<!DOCTYPE html>
<html ng-app>
<head>
    <title>Angular Object Collection</title>
</head>
<body>

<div ng-controller="basicctrl">
    <div style="">
        <input type="text" ng-model="filter"/>
    </div>

    <div style="float: left; margin-right: 50px;">
        <h3>Array Collection</h3>
        <ul ng-repeat="item in arrayCollection | filter:{name: filter} | orderBy:'name'">
            <li>{{ item.id }}, {{ item.name }}</li>
        </ul>
    </div>

    <div style="float: left; margin-right: 50px;">

        <h3>Object Collection</h3>
        <ul ng-repeat="item in objectCollection | filter:{name: filter} | orderBy:'name'">
            <li>{{ item.id }}, {{ item.name }}</li>
        </ul>
    </div>

    <div style="float: left; margin-right: 50px;">

        <h3>Converted Collection</h3>
        <ul ng-repeat="item in converted | filter:{name: filter} | orderBy:'name'">
            <li>{{ item.id }}, {{ item.name }}</li>
        </ul>
    </div>
</div>

<script type="text/javascript" src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular-animate.js"></script>

<script>
    /**
     * Created by Eric on 24.02.14.
     */
    function basicctrl($scope, $log){

        scope = $scope;

        $scope.arrayCollection = [
            {id: 1, name: 'Eric'},
            {id: 2, name: 'Robert'},
            {id: 3, name: 'Roberto'},
            {id: 4, name: 'Hans'},
            {id: 5, name: 'Taylor'},
            {id: 6, name: 'Jeff'},
            {id: 7, name: 'Mike'},
            {id: 8, name: 'Peter'}
        ];

        $scope.objectCollection = {
            1: {id: 1, name: 'Eric'},
            2: {id: 2, name: 'Robert'},
            3: {id: 3, name: 'Roberto'},
            4: {id: 4, name: 'Hans'},
            5: {id: 5, name: 'Taylor'},
            6: {id: 6, name: 'Jeff'},
            7: {id: 7, name: 'Mike'},
            8: {id: 8, name: 'Peter'}
        };

        $scope.converted = $.map($scope.objectCollection, function(value, index) {
            return [value];
        });

        $scope.filter = '';

        $log.log($scope.arrayCollection);
        $log.log($scope.objectCollection);
        $log.log('init');
    }
</script>
</body>
</html>

Zurück