Push rows in a table rendered with ng-repeat in angular

All we need is an easy explanation of the problem, so here it is.

I want to push in an extra row inline in a table when the client clicks on the row. The data should not be prefetched, since I expect there to be at most 30 rows but where each row has associated data that would be unreasonable to fetch in one get.

My approach so far is to use ng-repeat to iterate my collection and render a table. When the client presses the row, the client expects details about the row to be shown inline as an extra row under the pressed row.

<tr ng-repeat="court in courts">            
  <td>{{court.name}}</td>
  <td>{{court.number}}</td>
  <td>{{court.nrOfPlayers}}</td>
  <td>
    <a href ng:click="toggle(court.number)">Details</a>  <!-- click toggles extra row with data loaded async -->
  </td>
</tr>
<!-- extra row here -->

I have managed to show the details beneath the table with a ng-show in a hacky way, but that is not what I want.

How do you accomplish this with angular.js? What is the angular way to do this?

Here is a fiddle with a stupid squash court example http://jsfiddle.net/HByEv/

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

I think a possible solution could be http://jsfiddle.net/HByEv/2/.

There is also an alternative for the “No players” message commented in the fiddle if you want to also get rid of the extra <tr ng-show="..."></tr>.

Edit:

As pointed in the comments, in AngularJS 1.2+ you can now use ng-repeat-start and ng-repeat-end to solve this problem.

Jossef Harush provided a fiddle: http://jsfiddle.net/3yamebfw/

Method 2

One sample

var app = angular.module('test-app', []);

app.controller('MyController', function($scope, $rootScope, $timeout){

    $scope.copy = {
        p1: ['c1 p1', 'c1 p2'],
        p3: ['c3 p1', 'c3 p2', 'c3 p3', 'c3 p4', 'c3 p5']
    }

    $scope.courts = [
        {
            "number": 1,
            "name": "the best court",
            "nrOfPlayers": 2
        }, {
            "number": 2,
            "name": "the bad court",
            "nrOfPlayers": 0
        }, {
            "number": 3,
            "name": "the other court",
            "nrOfPlayers": 5
        }
    ];

    $scope.loadPlayers = function(court){
        //Implement your logic here
        //Probably using ajax
        $timeout(function(){
            $scope.players = $scope.copy['p' + court.number] || [];
        }, Math.random() * 2000);
    }

    $scope.shouDetails = function(court){
        if(court.nrOfPlayers) {
            delete $scope.players;
            $scope.loadPlayers(court);
        } else {
            $scope.players = [];
        }
    }

})

Demo: Fiddle

Method 3

Well. In fact, the main issue with your design is that you want to show thousands of rows in the same table. It will work, but it might be hard to render in some browsers (IE). For each rows, you will have a few bindings and each binding add watchers. You should always try to minimize the amount of binding in the page. I suggest you to use a pagination system in your array. Pagination on a list using ng-repeat

If you really want to do what you want without prerendering the rows, you will have to edit the dom, which is not a good practice in angular when avoidable. In my case, i would place the data somewhere else on the page, in a static zone. When i did something like this, I added a twitter bootstrap modal in my page and when the user clicked on “more info” the modal was opened with the info of the selected object.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply