3

I'm experiencing some problems with AngularJS and $timeout.
I need to run some code, after the page is loaded. Http.get() loads items and ng-repeat displays them in table. Then I need to run the code to highlight row when I click it.

I found a solution for that using $timeout.

$timeout(function () {
    console.log('in timeout');
    $scope.highlightRows();
});

this does work, but not every time. In the function I log number of rows in the table and sometimes I get 0, therefore the click handler is not registered and it highlighting doesnt work.

$scope.highlightRows = function () {
    console.log($("#tripsTable").children("tbody").children("tr").length);
    $("#tripsTable").children("tbody").children("tr").children().click(function () {
        console.log('row click');
        $("#tripsTable").children("tbody").children("tr").removeClass("focusedRow");
        $(this.parentNode).addClass("focusedRow");
    });
};

when try to simulate I have to refresh by Ctrl + F5.

console log:

in timeout tripsController.js:14
0 

I couldn't find any solutions to this problem, any suggestions will be appreciated :)
Thanks
Mark

EDIT: this is my HTML

<table class="table table-bordered" id="tripsTable">@*table-hover*@
    <thead>
        ....
    </thead>
    <tbody>
        <tr ng-repeat="trip in tripsVM.tripsList | orderBy: 'startDate'" ng-class="(trip.tripID == 0) ? 'newRow' : ''" class="row">
            ....
Mara
  • 118
  • 1
  • 10
  • I expect the problem here will be that you can guarantee whether the DOM manipulation that Angular is doing with the `ngRepeat` directive happens before or after the timeout. Sometimes it will happen before (in which case you see it work) and sometimes it will happen after (in which case you will see it not work). To take an Angular approach to doing what you are seeking here, the DOM manipulation shown in the `$scope.highlightRows` function would be better done in a directive. The directive can wire up the `click` handler at the time the DOM elements in the table are created. – chrisg Jul 23 '14 at 06:16
  • Are you calling $timeout from within your directive's link function? If its inside the controller, it doesn't guarantee that it runs after the render phase. – Michael Kang Jul 23 '14 at 06:16
  • 1
    Looks like the highlight row is getting called before the table is rendered. Could you please post the $http code to understand this better. – msapkal Jul 23 '14 at 06:18
  • 1
    This can be done using Angular with ng-click and ng-class. – Anthony Chu Jul 23 '14 at 06:22
  • Anthony Chu: Thanks, tried it and it works great ! :) Mahesh Sapkal: indeed, thats the problem.. but only on ctrl f5 or when the server starts.. chrisg, pixelbits: inside the controller, will try within directive, now its working with the ngClick and ngClass. not familiar with how to do it within directive, will try to google something or can you put together some basic code please? thank you all for suggestions:) – Mara Jul 23 '14 at 06:34
  • Great! Try to keep DOM manipulation out of your controllers. Also, even if you wanted to use jQuery, there's a much easier way to do this without needing a timeout. Using event delegation you can attach a handler on DOM ready... `$("#tripsTable").on('click', 'tr', function() { ... });` – Anthony Chu Jul 23 '14 at 06:45

1 Answers1

9

This looks like a timing issue. As you are not providing a duration parameter to the $timeout function it is executing straight away. This might be executing before the ng-repeat has finished rendering the data so nothing appears to be happening. As a test you could try adding a large delay to the $timeout function to see if that makes a difference. If it does you will then need to think of a way of triggering this once you know the items have been displayed.

$timeout(function () {
    console.log('in timeout');
    $scope.highlightRows();
}, 2000);

Also I would strongly advise you don't do any JQuery in your controller - in fact I strongly advise you don't use JQuery at all!

You could do all of this logic simply using angular directives such as ng-click & ng-class.

<table><tbody><tr ng-repeat="item in items" ng-click="selectItem(item)" ng-class={'focusedRow': item === selectedItem}"><td>{{item.Name}}</td></tr></tbody></table>

Then in your controller have the selectItem method

$scope.selectItem = function (item) {
    $scope.selectedItem = item;
}
Jon
  • 4,295
  • 6
  • 47
  • 56
  • 1
    The jQuery is deselecting other rows when one is selected, so perhaps a slight change... `ng-click="selectedItem = item" ng-class={'focusedRow': item === selectedItem}"` – Anthony Chu Jul 23 '14 at 06:35
  • Yeah, for that I go through the whole collection and deselect it on click and then select the one i clicked, this one looks much cleaner ! EDIT: but now it doesnt get de-selected when clicked on other row – Mara Jul 23 '14 at 06:38
  • Great see Anthony Chu comment - I'll update the code to match – Jon Jul 23 '14 at 06:39
  • It works great, but the row doesnt get deselected. How come is that ? if i click another row, the condition shouldnt match, does it? IMO there should be reference into another item, but it stays focused. Thanks – Mara Jul 23 '14 at 06:45
  • See the update which is the comment from Anthony Chu :-) – Jon Jul 23 '14 at 06:46