wait for angular app to be fully rendered from phantom script

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

I’m writing a script which generates png images from every page of my frontend.
I’m using angular for the UI and capturing the pages with phantom.

The view take a while until angular finish rendering it so I have to wait a little before capturing:

var page = require('webpage').create();
page.open('http://localhost:9000/', function () {
  window.setTimeout(function () {
    page.render('snapshot.png');
    phantom.exit();
  }, 2000);
});

I wonder if there is a better way to achieve this. I found angular can emit an event when the page is fully rendered:

$scope.$on('$viewContentLoaded', function () {
  // do something
});

And found a way for communicate to phantom with onCallback so I could write something like:

$scope.$on('$viewContentLoaded', function () {
  window.callPhantom({ hello: 'world' });
});

Then in other place in phantom script:

page.onCallback = function() {
  page.render('snapshot.png');
  phantom.exit();
};

But I’m lost in how to inject the angular $viewContentLoaded handle from the phantom script.

I don’t know if evaluate/evalueateAsyn are the way to go …

page.evaluateAsync(function () {
  $scope.$on('$viewContentLoaded', function () {
    window.callPhantom({ hello: 'world' });
  });
});

Maybe I could access the right $scope in some way.
Any ideas?

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

The associated PhantomJS API is onCallback; you can find the API doc on the wiki.

// in angular
$scope.$on('$viewContentLoaded', function () {
  window.callPhantom();
});

// in the phantomjs script
var page = require('webpage').create();
page.onCallback = function() {
  page.render('snapshot.png');
  phantom.exit();
};
page.open('http://localhost:9000/');

You can get access to the $rootScope by accessing the injector; for example, if you’re using the ng-app directive, you can find the element with the directive and call .injector().get("$rootScope") on it. However, I’m not sure if the $viewContentLoaded event will already have fired by then.

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