2

I am attempting to use a custom orderBy function. Initially, I want the data to appear in the order it was added to $scope.rows, and only after clicking on a column heading should it order by a specific property. Here's my fiddle:

http://jsfiddle.net/S8M4c/

Here's my view:

<table ng-app ng-controller="ctrl">
    <tr>
        <th><a ng-click="orderBy = 'id'">ID</a></th>
        <th><a ng-click="orderBy = 'name'">Name</a></th>
    </tr>
    <tr ng-repeat="row in rows | orderBy:mySort">
        <td>{{row.object.id}}</td>
        <td>{{row.object.name}}</td>
    </tr>
</table>

Here's my controller:

function ctrl($scope)
{
    // Initially, we don't sort by anything
    $scope.orderBy = "";
    $scope.rows = [];

    // Add some rows
    for(var i = 10;i < 30;i++)
    {
        $scope.rows.push({settings: {foo: true}, object: {id: i, name: "Name " + i}})   
    };

    $scope.mySort = function(row)
    {
        if($scope.orderBy != "")
        {
            return row.object[$scope.orderBy];
        }

        // What do I return here??
        return "";
    }
}

In the case that $scope.orderBy isn't set and I want to return $scope.rows in it's original order, what do I return in $scope.mySort? I cannot return row.object.id because the rows are not guaranteed to be added in order of their ID. Running my code as is on Chrome 32, the first row that appears has an ID of 20, which is the halfway row.

4
  • Just a guess: return row.object.id ? Commented Jan 30, 2014 at 19:33
  • The thing is that in my real app, the rows are not guaranteed to be added in the order of their ID. In fact, they may (appear) to be completely unsorted when first loaded, which is desired. I updated my question to reflect this. Commented Jan 30, 2014 at 19:36
  • 1
    Then your best bet is probably to add a new property to each object - this property should represent an ever-growing index usable for this sorting. Commented Jan 30, 2014 at 19:45
  • Unfortunately, I cannot add properties to my object because it is bound to socketIO and Redis. The object itself has no concept of the fact that it's in a row object. That's why I'm hoping there's a way to preserve the natural ordering of the rows when I use a custom orderBy function. Commented Jan 30, 2014 at 19:50

3 Answers 3

2

return $scope.rows.indexOf(row);

(Fiddle.)

You can also do this with out-of-the-box orderBy by providing a function returning that as the default predicate:

Controller:

$scope.mySort = $scope.unsorted = function(row)
    {
        return $scope.rows.indexOf(row);
    }

View:

<div ng-app ng-controller="ctrl">
    <table>
        <tr>
            <th><a ng-click="mySort = 'object.id'">ID</a></th>
            <th><a ng-click="mySort = 'object.name'">Name</a></th>
        </tr>
        <tr ng-repeat="row in rows | orderBy:mySort">
            <td>{{row.object.id}}</td>
            <td>{{row.object.name}}</td>
        </tr>
    </table>
    <button ng-click="mySort = unsorted;">Original Sort</button>
</div>

Fiddle here. (I've changed the numbers used in the objects so that sort by id, sort by name, and the original sort aren't all the same.)

Sign up to request clarification or add additional context in comments.

Comments

1

I think you have to write your own sortby function. The original angulars orderBy is a regular filter that returns the sorted array. Your filter may look something like this:

.filter('mySort', function(){
    return function(values, param){
        if(param===''){
            return values;
        }else{
             // very important! create a copy of the array - otherwise 
             // the $wtachCollection function will fire to often!
             var arrayCopy = [];
             for ( var i = 0; i < values.length; i++) { arrayCopy.push(values[i]); }

            return arrayCopy.sort(function(a,b){
                var v1 = a.object[param];
                var v2 = b.object[param];
                // you know best how to sort it!
                if (v1 === v2) return 0;
                return v1 < v2 ? -1 : 1;
            });   
        }
    }
})

You can use this filter in this way:

<table ng-app="myApp" ng-controller="ctrl">
    <tr>
        <th><a ng-click="orderBy = 'id'">ID</a></th>
        <th><a ng-click="orderBy = 'name'">Name</a></th>
    </tr>
    <tr ng-repeat="row in rows | mySort:orderBy">
        <td>{{row.object.id}}</td>
        <td>{{row.object.name}}</td>
    </tr>
</table>

here is your modified fiddle: http://jsfiddle.net/spRf6/ I have changed the names a little bit so you may see that the sorting works.

Comments

1

Create a copy of the objects array and the ordering then becomes trivial:

Controller:

$scope.objects = [];

angular.forEach($scope.rows, function(row){
    $scope.objects.push(row.object);
});

View:

<tr ng-repeat="object in objects | orderBy:orderBy">
    <td>{{object.id}}</td>
    <td>{{object.name}}</td>
</tr>

No need for the mySort function.

2 Comments

In all honesty, this is probably the answer in most cases. In my particular situation, though, I need to keep the object as a property of the row because it's needed elsewhere. Thanks though!
It still is. The object array contains just references to the original objects.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.