skip to Main Content

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?

3

Answers


  1. 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.

    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
  3. 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;
        }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search