Angular 2 - How to use prerender.io along with lazy loading
Asked Answered
T

3

6

Since Angular Universal isn't going to be in the CLI for quite a while I must use prerender.io to allow SEO to work properly. However, after some tests it appears that it doesn't work all that great because it doesn't appear to wait for lazy loaded modules so the SEO still fails.

On their site at this location, they say this:

Is your page only partially rendered?

Our Prerender server tries its best to determine when the page is done loading by counting the number of requests in flight. Once the number of requests in flight reaches zero, we wait a short period of time and then save the HTML. If this is saving the page too early, you can use our window.prerenderReady flag to notify the server that your page is ready to be saved.

Put this in your HTML:

<script> window.prerenderReady = false; </script>

When we see window.prerenderReady set to false, we'll wait until it's set to true to save the HTML. Execute this when your page will be ready (usually after ajax calls):

window.prerenderReady = true;

Here's where I don't understand how to go about it. Since Angular removes script tags from the template (not sure if this is just CLI behavior or Angular itself), I can't set the initial script tag. Nor can I figure out how I would update the window property prerenderReady inside a template script tag from my component class, since I can't just use window.prerenderReady = true and expect it to work since the script has to be added to the template.

Has anyone managed to figure this one out or have any ideas on how I could achieve this?

Timbal answered 14/3, 2017 at 7:42 Comment(4)
did you get any success on this? I am also facing same issue.Reube
@GirishRathod I did, but you don't have to use this method if your server is configurated correctly.Timbal
What I can use? Please Do you have proper configuration example so I can use.Reube
@GirishRathod I didn't fix it myself so I don't know exactly how it was done :/ I'm sorry.Timbal
T
0

REMOVE: server.use(prerender.removeScriptTags());

prerender.io can be set to leave in scripts so they can run.

My Meteor-Angular2 setup gave me the same grief. My header and footer modules would render, but not my router-outlet output. I placed:
<script> window.prerenderReady = false; </script>
on my main index.html (containing the main <app></app> tag), and placed:
window.prerenderReady = true;
at the end of my last callback. Then I commented out:
//server.use(prerender.removeScriptTags());
in the server.js of my prerender server - and it finally rendered correctly.

Trounce answered 6/6, 2017 at 2:36 Comment(0)
S
0

Okay, I got this working, but it seems like a rather complex solution, so hopefully there's someone out there smarter than me at this :)

First, uncomment all the browser polyfills in src/polyfills.ts (it may be that not all of them are needed, but I haven't tested them one-by-one):

/** IE9, IE10 and IE11 requires all of the following polyfills. **/
import 'core-js/es6/symbol';
import 'core-js/es6/object';
import 'core-js/es6/function';
import 'core-js/es6/parse-int';
import 'core-js/es6/parse-float';
import 'core-js/es6/number';
import 'core-js/es6/math';
import 'core-js/es6/string';
import 'core-js/es6/date';
import 'core-js/es6/array';
import 'core-js/es6/regexp';
import 'core-js/es6/map';
import 'core-js/es6/weak-map';
import 'core-js/es6/set';

Next, you need to get your prerenderReady flag in a script tag in the <head> of your index.html. Angular doesn't strip out comments so before I build I leave a flag in there:

<head>
    <!--prerender-->
</head>

Now, AFTER you run ng build, you need to replace that flag with the actual script tag. I do this using a shell script and the sed command. Something like:

TPL="\<!--prerender--\>"
PRERENDER_SCRIPT="\<script\>window.prerenderReady = false;\<\/script\>"

# replace the comment with the actual script tag in the html file and save
# that file's contents as a string in the INDEX_HTML variable
INDEX_HTML=$(sed "s/$TPL/$PRERENDER_SCRIPT/g;" dist/browser/index.html);

# remove the original index.html file and create an empty one in its place
rm dist/browser/index.html
touch dist/browser/index.html

# echo the INDEX_HTML string into the new file
echo "$INDEX_HTML" > dist/browser/index.html

NOTE I have a vague recollection of installing sed on OSX, but I don't really remember the steps. If you're not on linux and sed doesn't work for you, you'll just have to Google around for a solution.

Finally, you need to set that prerenderReady flag to true somewhere. I do this in the AppComponent's OnInit implementation:

export class AppComponent implements OnInit
{
    ngOnInit()
    {
        window.prerenderReady = true;
    }
}

It probably also bears mentioning that in order to use window in a TypeScript app you need something like this in file I call typings.d.ts:

interface Window
{
    prerenderReady:boolean;
}

As well as a reference to that typings file I put at the top of my app.module.ts file:

/// <reference path="../typings.d.ts" />

Hope that helps someone out.

Suasion answered 20/1, 2018 at 22:30 Comment(0)
A
0

I would suggest putting the first script in index.html near the top of the page before Angular JS files are injected using angular.json or edit webpack build to inject: window.prerenderReady = false

Then in the main app.ts file, get a reference to the window object and put window.prerenderReady = true in the ngOnViewInit hook:

 public ngAfterViewInit(): void {
        this.windowReference.prerenderReady = true;
    }
Anguish answered 14/1, 2022 at 15:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.