After doing some experimentation, I found a way to get an approximation how long a view takes to render.
The scenario:
- Route to a page in an Angular app
- Make AJAX call to get a lengthy list of items
- Take list of items, pass into an ng-repeatul
- View rendered list
In an Angular app, ng-repeat is a notorious performance killer, but it's extremely convenient. Here's a discussion on the performance and here.
Let's say that we want to get an approximation of the time it takes to get from #3 to #4, as testing the AJAX call performance is pretty straightforward.
Here's some context:
<ul>
  <li ng-repeat="item in items">
    {{item.name}}
    <!-- More content here -->
  </li>
</ul>
And inside the Angular controller:
// Get items to populate ng-repeater
MyApiService.getItems(query)
  .then( function (data) {
    $scope.items = data;
    // When callback finishes, Angular will process items
    // and prepare to load into DOM
  }, function (reason) {
    console.log(reason);
  });
The events mentioned by @runTarm, $routeChangeStart, $routeChangeSuccess, and $viewContentLoaded fire as soon as the route is loaded, but before the DOM renders the items, so they don't solve the issue. However, through experimentation, I found that once the AJAX callback finishes and sets $scope.items, Angular begins a blocking operation of processing items and preparing the ng-repeat ul to be inserted into the DOM. Thus, if you get the time after your AJAX call finishes, and get the time again in a setTimeout specified in the callback, the setTimeout callback will be queued to wait until Angular is done with the repeater process and get you the time a split second before the DOM renders, giving you the closest approximation for rendering time. It won't be actual render time, but the slow part for us is not the DOM doing it's work, but Angular, which is what we're looking to measure.
Here's the revised example:
// Get items to populate ng-repeater
MyApiService.getItems(query)
  .then( function (data) {
    var start = new Date();
    $scope.items = data;
    // When callback finishes, Angular will process items
    // and prepare to load into DOM
    setTimeout( function () {
      // Logs when Angular is done processing repeater
      console.log('Process time: ' + (new Date() - start));
    }); // Leave timeout empty to fire on next tick
  }, function (reason) {
    console.log(reason);
  });