universal: After migrating from angular universal 8 to 9, the page is in eternal loading and does not load anything

🐞 bug report

What modules are related to this issue?

  • aspnetcore-engine
  • builders
  • common
  • express-engine
  • hapi-engine
  • module-map-ngfactory-loader

Description

after I upgraded from angular 8 to angular 9, Angular SSR stopped working. it compiles everything right, when I open it in the browser it keeps trying to load and can’t, there is no error in the browser console and I have no error in the terminal, I no longer know what to do. For several weeks I have been trying to solve this, but there is no way to identify what is happening and it is something related to the update…

🔬 Minimal Reproduction

https://github.com/GlauberF/minimal-reproduction

🔥 Exception or Error

ezgif-3-c3d591a12da4

🌍 Your Environment

Angular CLI: 9.1.7
Node: 12.16.3
OS: linux x64

Angular: 9.1.4
... animations, common, compiler, core, forms, platform-browser
... platform-browser-dynamic, platform-server, router
... service-worker
Ivy Workspace: Yes

Package                            Version
------------------------------------------------------------
@angular-devkit/architect          0.901.7
@angular-devkit/build-angular      0.901.7
@angular-devkit/build-optimizer    0.901.1
@angular-devkit/build-webpack      0.901.7
@angular-devkit/core               9.1.7
@angular-devkit/schematics         9.1.7
@angular/cdk                       9.2.2
@angular/cli                       9.1.7
@angular/compiler-cli              9.1.9
@angular/flex-layout               9.0.0-beta.29
@angular/language-service          9.1.9
@angular/material                  9.2.2
@angular/material-moment-adapter   9.2.2
@ngtools/webpack                   9.1.7
@nguniversal/builders              9.1.1
@nguniversal/common                9.1.0
@nguniversal/express-engine        9.1.0
@schematics/angular                9.1.7
@schematics/update                 0.901.7
rxjs                               6.5.5
typescript                         3.8.3
webpack                            4.42.0

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 2
  • Comments: 75

Most upvoted comments

@GlauberF as seeing your package.json, npm run serves: ssr command is not existed.

I first commented in June 2020 that my project also has this issue, so I am just reporting that 7 months later I am still here with the same problem, waiting to find out at least some info about the root cause. A solution ofc would be a nice new year gift 😃

@dev-assassin The correct flow is for you to do the buil first (build:ssr) and then run the server (serve:ssr)

if you run the server before, it will really give an error, because there is no build yet.

This is the basic build flow of an application, universal angular

well. i know the flow. before running npm run serve:ssr, i am sure i built it at first. 😃

when you run that command, did you get dist folder? seeing your server.ts file, you made dist folder. i think there is a problem in your server.ts file.

there is not existed dist/server/main.js. 😦

@GlauberF in my side, it works well. when i start the project with npm start, login dashboard is opened.

https://gofile.io/d/XhjChN please check it, let me know.

I found a solution and maybe I don’t know if it’s the best but it works. The problem is that it barely gets up and is loading and works only when the path is placed /index.html then to always force the entry to index.html as soon as the server is up, you have to make the following modification in server.ts

import 'zone.js/dist/zone-node';

import { ngExpressEngine } from '@nguniversal/express-engine';
import express, { Request, Response } from 'express';
import { join } from 'path';

import { AppServerModule } from './src/main.server';
import { existsSync, readFileSync } from 'fs';
const domino = require('domino');
const path = require('path')

const distFolder = join(process.cwd(), 'dist/chatroom/browser');
const template = readFileSync(path.join(distFolder, 'index.html')).toString();
const win = domino.createWindow(template.toString());


global['window'] = win;
Object.defineProperty(win.document.body.style, 'transform', {
  value: () => {
    return {
      enumerable: true,
      configurable: true,
    };
  },
});
(global as any)['document'] = win.document;
(global as any)['CSS'] = null;
(global as any)['Prism'] = null;

// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
  const server = express();
  const distFolder = join(process.cwd(), 'dist/chatroom/browser');
  const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index.html';

  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
  server.engine('html', ngExpressEngine({
    bootstrap: AppServerModule,
  }));

  server.set('view engine', 'html');
  server.set('views', distFolder);

  // Example Express Rest API endpoints
  // server.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get('*.*', express.static(distFolder, {
    maxAge: '1y'
  }));

  // All regular routes use the Universal engine
  server.get('*', (req: Request, res: Response) => {
    res.redirect(indexHtml);
  });

  return server;
}

function run(): void {
  const port = process.env.PORT || 4000;

  // Start up the Node server
  const server = app();
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  run();
}

export * from './src/main.server';

you must make two modifications:

  1. replace this code
change const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';

with

const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index.html';
  1. replace this code
server.get('*', (req, res) => {
    res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
 });

with

 server.get('*', (req, res) => {
    res.redirect(indexHtml);
 });

and open a new tab private…

@alan-agius4 Here https://github.com/sadriddin/angular-universal-10-loading-issue a minimal repro. It has one protected route with the guard. If you run npm run dev:ssr and navigate to http://localhost:4200/protected it loads infinitely.

It’s working in Angular V8. Here is https://github.com/sadriddin/angular-univerasal-v8-example working example in Angular V8

I want to give updates about our issue (with @rmcsharry).

I upgraded the project to angular 11 and activated onPush with async and SSR works like magic. That’s good news but unfortunately, we did not know why it was not working in the previous versions.

Another advice about SSR with angular (it might help you…) , we decide to drop the Flex-layout library because it was not designed to work with SSR (even with the FxLayoutServerModule…). The style are injected dynamically so when the page loads the layout is always mangled , and sometimes we got infinite loading.

Good luck

Hi @DMZakaria , I’ve been waiting for a position from the angular team for a long time. It seems to me that this issue has been overlooked.

Some further information.

All our components were using the OnPush strategy and we would use ChangeDetectorRef to decide when to trigger change detection.

After removing OnPush so that components used the default, SSR mode started working again. We were still able to use the async pipe with ngIf and ngFor.

So either we remove use of async pipe OR we remove OnPush. But using them together (not necessarily in the same component) caused this problem for us.

@GlauberF Just jumping in here to confirm that we see the exact same issue. We have narrowed it down to using async pipe with ngIf or ngFor.

If we remove the async pipe from all uses of those structural directives and instead use subscriptions in the ts file, then SSR works and loads the site.

Why that should be, I have no £F?&^!! idea.

https://stackoverflow.com/questions/62246010/angular-rxjs-pipe-async-does-not-work-with-ssr?noredirect=1#comment110112454_62246010

@dev-assassin does this link redirect me to the issues? Captura de Tela_selecionar área_20200608111746