0

So I'm developing a AngularJS website, and I have been charged with the task of making it friendly to facebook sharing and SEO. I've chosen PhantomJS as a possible solution, to evaluate the Javascript and spit out executed html code, which for now is just about filling the facebook meta tags with information.

However after getting phantomJS started, and evaluating the page, I get this error in my chrome:

Error: [ngTransclude:orphan] Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element: <ul ng-transclude=""> http://errors.angularjs.org/1.2.22/ngTransclude/orphan?p0=%3Cul%20ng-transclude%3D%22%22%3E

After looking over the code, I only use transclude once in the site, and that is for generating my menu, which consist of a ul and transcluded li items.

    app.directive('mvMenu', ['$rootScope', '$location', function ($rootScope, $location) {
return {
    restrict: 'E',
    transclude: true,
    controller: function ($scope, $element, $attrs) {
        ...
    },
    template: '<nav id="nav" role="navigation">' +
                '<div class="block">' +
                '<ul ng-transclude>' +
                '</ul>' +
                '</div>' +
                '</nav>'

}
}]);

My li items are also a directive, and its code is this:

app.directive('mvMenuItem', ['$location', function ($location) {
return {
    require: '^mvMenu',
    restrict: 'E',
    scope: {
        text: '@text',
        navLink: '@link',
        icon: '@faicon'
    },
    link: function (scope, element, attr, menuCtrl) {
        ...
    },
    template: '<li ng-class="{\'is-active\': isActive(navLink)}">' +
    '<a ng-click="navAndToggleMenu(navLink)"><span class="fa {{icon}}"></span>{{text | translate}}</a>' +
    '</li><!--'
}
}]);

I have never seen this error when the site is used on any browser on any device. It only comes up when evaluating the page with phantomJS.

This is the phantomJS code I am using to generate the html:

var page = require('webpage').create();
var system = require('system');

var lastReceived = new Date().getTime();
var requestCount = 0;
var responseCount = 0;
var requestIds = [];
var startTime = new Date().getTime();

page.onResourceReceived = function (response) {
if (requestIds.indexOf(response.id) !== -1) {
    lastReceived = new Date().getTime();
    responseCount++;
    requestIds[requestIds.indexOf(response.id)] = null;
}
};
page.onResourceRequested = function (request) {
if (requestIds.indexOf(request.id) === -1) {
    requestIds.push(request.id);
    requestCount++;
}
};

function checkLoaded() {
return page.evaluate(function () {
    return document.all;
}) != null;
}
// Open the page
page.open(system.args[1], function () { });

var checkComplete = function () {
// We don't allow it to take longer than 5 seconds but
// don't return until all requests are finished
if ((new Date().getTime() - lastReceived > 300 && requestCount ===            
responseCount) || new Date().getTime() - startTime > 10000 || checkLoaded()) {
    clearInterval(checkCompleteInterval);
    var result = page.content;
    //result = result.substring(0, 10000);
    console.log(result);
    //console.log(results);
    phantom.exit();
}
}
// Let us check to see if the page is finished rendering
var checkCompleteInterval = setInterval(checkComplete, 300);

This code is taken from How to make a SPA SEO crawlable?, with the only difference beeing the "return document.all" in the evaluate method.

Thanks in advance!

Community
  • 1
  • 1
Marfyy
  • 63
  • 6
  • Does it work if you remove the menu by chance? – AlexMA Mar 04 '15 at 17:32
  • Where are you using the mvMenu directive? – HankScorpio Mar 04 '15 at 17:59
  • @AlexMA I tried removing the menu, and yes it executed without errors. Obviously I want the menu there when using a normal browser, so maybe there is a way to remove the menu in phantomjs only? – Marfyy Mar 04 '15 at 18:13
  • @HankScorpio I am using a ASP.NET backend, and the directive is used directly in my index.cshtml file. After attempting to comment out the menu, I notice that my angular code doesnt seem to execute at all, since after checking what phantomjs spits out in chrome, I notice that the ng-view isnt filled at all, and neither is my facebook meta tags. I googled around and tried setting in a $scope.htmlReady() after my apicalls, but to no avail – Marfyy Mar 04 '15 at 18:15

1 Answers1

0

After you run your PhantomJS script, the html that you get out is already rendered. It doesn't make sense to let angular try to render it again when you open it in a normal browser after it already rendered.

Take for example the following markup:

<div ng-repeat="stuff in stuffList">{{stuff}}</div>

This gets for example rendered to

<div class="ng-repeat" ng-repeat="stuff in stuffList">Stuff1</div>
<div class="ng-repeat" ng-repeat="stuff in stuffList">Stuff2</div>

What would happen if you open the already rendered markup in a browser? The ng-repeat would be executed again which would result in the following markup:

<div class="ng-repeat" ng-repeat="stuff in stuffList">Stuff1</div>
<div class="ng-repeat" ng-repeat="stuff in stuffList">Stuff1</div>
<div class="ng-repeat" ng-repeat="stuff in stuffList">Stuff2</div>
<div class="ng-repeat" ng-repeat="stuff in stuffList">Stuff2</div>

That's not what you want.

You should remove all page scripts before saving the markup. The JavaScript is not needed anymore.

page.evaluate(function(){
    [].forEach.call(document.querySelectorAll("script"), function(s){
        s.parentNode.removeChild(s)
    });
});
fs.write("content.html", page.content);
phantom.exit();
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Thank you that seems to have fixed the error. You seem to have some experience with phantomjs, maybe you can assist me with the new problem Ive noticed. No angularjs code seem to be executed at all after running the code above. PhantomJS is beeing executed via ASP.NET in my MVC controller behind my index.cshtml. I am using the code from [this link](http://stackoverflow.com/questions/18530258/how-to-make-a-spa-seo-crawlable) which executes the phantomjs.exe and the script. Is there a step I am missing? – Marfyy Mar 04 '15 at 18:28
  • You mean after applying my fix? That's the idea. JavaScript won't be executed. It looks like you're using it for SEO when bots come along that don't know JS. Maybe I misunderstood your problem. – Artjom B. Mar 04 '15 at 19:27