Utilizing prerender.io with lazy loading in Angular 2: A comprehensive guide

As Angular Universal is not expected to be included in the CLI for some time, I've had to resort to using prerender.io in order to ensure proper SEO functionality. However, my tests have shown that there are issues with lazy loaded modules causing SEO failures.

According to information on their website located here, Prerender.io states:

Is your page only partially rendered?

Our Prerender server does its best to detect when a page has finished loading by monitoring the number of active requests. Once this count reaches zero, we pause briefly before saving the HTML content. If you find that the page is being saved too soon, you can use the window.prerenderReady flag to signal to the server that your page is ready for saving.

Add this code snippet to your HTML:

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

When we observe window.prerenderReady set to false, we will wait until it becomes true before saving the HTML content. Trigger this change once your page is fully loaded (usually after any ajax calls):

window.prerenderReady = true;

The challenge lies in implementing this solution within an Angular environment, specifically when script tags are removed from templates. It's unclear whether this behavior is specific to the CLI or inherent in Angular itself. Additionally, updating the window property prerenderReady within a template script tag from a component class poses difficulties, as simply setting window.prerenderReady = true may not function as intended without inclusion in the template.

Has anyone encountered and resolved this issue, or do you have suggestions on how I could address it effectively?

Answer №1

EDIT:

server.use(prerender.removeScriptTags());

prerender.io allows for scripts to remain in the rendering process.

Dealing with a similar issue in my setup using Meteor and Angular2. While my header and footer modules rendered fine, the router-outlet output did not appear. I resolved this by adding:

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

to my main index.html file (which contains the <app></app> tag), and inserting:
window.prerenderReady = true;
at the end of my last callback function. Additionally, I disabled the line:
//server.use(prerender.removeScriptTags());

in the server.js file of my prerender server - resulting in successful rendering.

Answer №2

After some troubleshooting, I managed to get this solution up and running. It's quite intricate, so hopefully there's a genius out there who can simplify it for me :)

To begin with, remove the comment tags from all browser polyfills located in src/polyfills.ts (some may not be necessary, but I haven't individually tested them):

/** The following polyfills are required for IE9, IE10, and IE11 **/
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';

Following that, you must insert the prerenderReady flag within a script tag in the <head> section of your index.html. Since Angular doesn't strip out comments, leave a placeholder before building:

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

Subsequently, once you execute ng build, replace the placeholder with the actual script tag using a shell script and the sed command. Here is an example:

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

INDEX_HTML=$(sed "s/$TPL/$PRERENDER_SCRIPT/g;" dist/browser/index.html);

rm dist/browser/index.html
touch dist/browser/index.html

echo "$INDEX_HTML" > dist/browser/index.html

Please Note: I vaguely recall setting up sed on OSX, but the specifics elude me. In case sed doesn't function on your system, some research might be needed.

Lastly, ensure to change the prerenderReady flag to true at some point. This can be done in the OnInit method of the AppComponent:

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

It's worth mentioning that to utilize window in a TypeScript application, declarations like the one below need to be present in a file named typings.d.ts:

interface Window
{
    prerenderReady: boolean;
}

Additionally, include a reference to the typings file at the start of app.module.ts:

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

This guide should prove helpful to someone in need.

Answer №3

To optimize the performance of your website, I recommend placing the initial script in index.html close to the top of the page before incorporating Angular JS files using angular.json or modifying the webpack build to include: window.prerenderReady = false

Subsequently, within the main app.ts file, access the window object and set window.prerenderReady = true in the ngOnViewInit hook:

 public ngAfterViewInit(): void {
        this.windowReference.prerenderReady = true;
    }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Apply a spread of nested elements onto another spread

I am working with an array containing last names of Persons and need to populate new entries. However, I only have the last names and not the full Person objects. How can I address this issue? type Person = { name: string, lastName: string, age: ...

Generate SVG components without displaying them

Is there a way to generate a custom SVG graphic through a function without the need to attach it to any element? Can I simply create an empty selection and return that instead? Here is my current implementation: function makeGraphic(svgParent) { retur ...

Creating a dynamic configuration for service instantiation in Angular 4/5

Currently working on a library that has an AuthModule with an AuthService for managing OAuth2 authentication using oidc-client-js. I want the application using this library to be able to set up the configuration for the OAuth client. One way to do this is ...

Modifying an object's label based on a particular value using JavaScript

In my current project involving React.js charts, I am looking to organize data by month. In Django, I have set up a view to display JSON containing the total events per month in the following format: [ { "month": "2022-06-01T00:00:0 ...

Utilizing SVG within Sproutcore allows for seamless access to DOM nodes and the ability to effortlessly bind Sproutcore events directly to the DOM

Exploring Sproutcore, I am delving into the world of SVG (Standard Vector Graphics) integration within the app. The goal is to utilize a canvas for drawing elements like lines, boxes, and images, all of which SVG offers. My approach involved incorporating ...

considering multiple website addresses as one collective entity for the Facebook like button

Currently, I am tackling an issue on a website that employs a custom CMS which automatically generates URLs based on the titles of articles. The main problem faced by our contributors is that when they update the title of an article, it creates a new URL ...

Having issues setting a property value on a Mongoose result in a Node.js application

Hello there, I am currently working with a MongoDB object retrieved via a findById method, and I need to convert the _id within this object from an ObjectID type to a string. I have developed the following function: student: async (parent, { _id }, ...

Showing JSON Array Values in a Table

I have created an array and am attempting to display its values in a table. Initially, my solution only displayed a single value from the array that matched an exact ID. You can see this implementation at (). Entering "jjones" would yield a result. I then ...

Is it possible to run two commands in npm scripts when the first command initiates a server?

When running npm scripts, I encountered an issue where the first command successfully starts a node server but prevents the execution of the second command. How can I ensure that both commands are executed successfully? package.json "scripts": { "dev ...

Using jQuery Plugin for Date Operations

I am in search of a jQuery tool that can assist me with date manipulations, such as: 1) Adding a specific number of days to a date and obtaining a new Date. 2) Determining the week of the year for a given date. 3) Calculating the number of days between two ...

Acquire the Dynamic HTML Tag from the source code of the page

I am currently using Selenium to develop a bot that will automate a task by fetching a report from a website. I must admit, I am not well-versed in HTML or Javascript, so please forgive any misuse of terms. Here is the problem at hand: Using Python, I wri ...

What is the process by which Node can access predefined variables in clustering?

Consider the following code snippet: var running = false; var cluster = require('cluster'); if(cluster.isMaster){ cluster.fork(); running = true; } If this code is executed within multiple forks, would the value of 'running' ...

No input value provided

I can't figure out what's going wrong. In other js files, this code works fine. Here is my HTML code: <table class='table'> <tr> <th>Event</th><td><input class='for ...

Angular directive that verifies the presence of a specific number of child li elements

Currently, I have a basic ul that utilizes ng-repeats to display li elements fetched from an external source via a promise. There is also a search input present which filters these elements, and I want the ul to be hidden when there are no more elements th ...

Setting the type of a prop dynamically based on another prop value

Consider the following scenario with an interface: interface Example { Component: React.ReactElement; componentProperties: typeof Example.Component; } Is there a way to determine the type of properties expected by a passed-in custom component? For ...

Optimal strategies for initializing Knockout JS models from backend code

Upon taking over a website that utilizes knockout js and asp.net, I noticed some performance issues during the initial page load. After investigating, I found that there are approximately 20 models on the site, each making an ajax call to retrieve data fro ...

Is there a way to customize CKEditor to prevent it from continuously adding <p></p> tags within the textarea automatically?

As I was going through the CKEditor tutorial, I implemented the following: $( '#editor' ).ckeditor(function(){}); //it's working great!! However, after submitting the form, I noticed that by default, the textarea displays <p></p ...

Is your $http request causing an XML parsing issue?

I'm attempting to utilize the $HTTP method from angularJS to access my restful web service. On entering my web service URL in the browser, I receive a result of APPLICATION/JSON type. {"id":20418,"content":"Hello, World!"} The URL for my web servic ...

"Utilizing Ajax technology for website functionality while implementing effective SEO strategies

I've been facing an issue with my website not being indexed by Google for the past 5 weeks. The problem is not just with internal pages, but with the entire website itself. When searching for "xyz," my website "ww.xyz.com" is completely ignored by Go ...

Challenge with Typescript Interfaces

Can someone help me with understanding interfaces in typescript? I am currently facing an issue with the code. The error message says: Type 'Response' is missing the following properties from type 'myObj': userId, title, id I believe ...