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

Troubleshooting: When Angular Fade In Out Animation Won't Work

Looking to create a simple animation with the angular animation package The goal is to smoothly transition from opacity 0 to opacity 1 for a fade effect. I've had success with other properties like height and display, but struggling with opacity. H ...

Submitting Data in Ionic 3 using Http Post and Storing in Sqlite with Angular 4

I am facing an issue while trying to post an array of contacts on a WebService. When I send the array, the data appears as NULL in the WebService response. I am confused about how to use Let params{} The error message shows "object undefined". Addition ...

Troubleshooting: Vue.js Component template not displaying items with v-for loop

I recently implemented a method that calls an AJAX request to my API and the response that it returns is being assigned to an array. In the template section, I am using the v-for directive to display the data. Surprisingly, it only renders once after I mak ...

Tips for optimizing the speed of uploading multiple images/files from a client's browser to a server using JavaScript

We are seeking ways to enhance the file upload process in our application, particularly for managing large files. Any suggestions on accelerating this process would be greatly appreciated. ...

The usage of 'import.meta' is restricted to within modules and cannot be utilized outside of them

I am working on a project that involves Node + Express + Babel + ES6. Within this project, I have the following files: /package.json { "name": "test-backend", "version": "1.0.0", "description": " ...

Monitor the item for fluctuations in its worth

Is there a way to watch an object and wait for all of its values to become truthy? const myObject = { key1: null, key2: null, key3: null, } // Perform asynchronous operations that update myObject const checkValues = async () => { awa ...

Ensure that the cursor is consistently positioned at the end within a contenteditable div

I'm working on a project where I need to always set the caret position at the end of the text. By default, this works fine but when dynamically adding text, the caret position changes to the starting point in Chrome and Firefox (Internet Explorer is s ...

Executing a JavaScript file within the Meteor JS ecosystem

Is there a way to execute a JavaScript file on the server side in a meteor JS environment? Providing some background information: I am looking to automatically insert a document into a mongo database. While I know how to do this in meteor through event-dr ...

Unable to retrieve the form from the website

My goal is to extract the login form from: Although I can see the form element when inspecting in Chrome, I'm encountering difficulties retrieving it using the jaunt API in Java. userAgent = new UserAgent(); userAgent.visit("https://etoro.com/login" ...

Leveraging LESS in an Angular2 component

Currently, I am attempting to integrate LESS with angular2. To do so, I have imported the less.js file in my index.html and I am using the following command within the component: less.modifyVars({ '@currentTheme-bar': '@quot ...

How to set return types when converting an Array to a dynamic key Object in Typescript?

Can you guide me on defining the return type for this function? function mapArrayToObjByKeys(range: [string, string], keys: { start: string; end: string }) { return { [keys.start]: range[0], [keys.end]: range[1] } } For instance: mapArrayToObj ...

How to change elements within an HTML string using jQuery

I am facing an issue where I have an HTML string (not DOM) that needs to be manipulated using jQuery. However, the code I am trying does not seem to work as expected: var html = '<div><h4><a class="preview-target" href="content.html"&g ...

Gaining entry into a JSON object

I'm currently working on a web page that utilizes API data from the Breaking Bad website. I have received this data in JSON format, but I am struggling to figure out how to access only the objects where the "author" is "Walter White." Here is the data ...

An issue arose while attempting to pin a file on IPFS: A TypeError [ERR_INVALID_ARG_TYPE] was encountered, specifying that the argument "url" must be a string data type

Adding the files to another project resulted in an error that I am struggling to understand. When running "node scripts1/runScript.js", the error message received is as follows: Error occurred while pinning file to IPFS: TypeError [ERR_INVALID_ARG_TYPE]: ...

Guide to capturing the response in Angular 4 using HttpInterceptor

I have an Interceptor called TokenInterceptor: @Injectable() export class TokenInterceptor implements HttpInterceptor { constructor(private tokenService: TokenService) { } intercept(req: HttpRequest<any>, next: HttpHandler): Observable<Http ...

What could be causing jQuery animate to malfunction on mobile devices when a viewport is present?

Everything seems to be working fine on my desktop webpage, but when I try it on mobile, there is no scroll... $("HTML, BODY").animate({ scrollTop: 500 }, 1000); This post suggests that mobile devices may not scroll on the body, but on the vi ...

Setting a background image in vanilla-extract/css is a straightforward process that can instantly enhance

I am brand new to using vanilla-extract/CSS and I have a rather straightforward question. I am attempting to apply a background image to the body of my HTML using vanilla-extract, but I am encountering issues as I keep getting a "failed to compile" error. ...

HTML link with "mailto:" not opening in a new tab

Just posted for the first time! I'm attempting to create a mailto link using 'templated' JavaScript that pulls specific data from a JSON object: var menu = { "menu": [ { "title": "let's talk", "link": "mailto:<a href ...

Tips on eliminating borders in react-table components

Within my React.js component, I have implemented a table using react-table along with some material-ui components displayed alongside the table: import React from 'react' import { useTable, useGlobalFilter, usePagination } from 'react-table& ...

What is the best way to adjust the size of an HTML5 SVG circle with a button click event?

If you click the button, the text size will increase or decrease. <div class="buttons"> <button (click)="fSize = fSize + 1">+</button> <button (click)="fSize = fSize - 1">-</button> </div> <br> <div [ ...