universal: "Window is not defined" even when isPlatformBrowser used.

🐞 Bug report

What modules are related to this issue?

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

Is this a regression?

Yes the project worked correctly in all circumstances in Angular 8.1.1

Description

Large Angular 8 application with SSR support. Upgraded to Angular 9. All migration scripts related to my project ran and worked correctly with no errors.

Project works fine with ng serve and builds fine when targeting SSR with the command: NODE_OPTIONS=--max-old-space-size=8192 ng build --source-map --configuration=development && ng run front:server:development.

When I try to run the resulting server (to host with express / express-engine) it gives the “Window not defined” because of a reference in a third party module. All uses of that module are guarded with an ‘isPlatformBrowser’ check.

🔬 Minimal Reproduction

Seemingly you just have to include any module that references 3rd party modules that try to access ‘window’. Note that this did not cause any problem in Angular 8 as long as you guarded against use of those modules with ‘isPlatformBrowser’ checks.

Now it seems merely having the module referenced is enough.

Set up a vanilla universal app and then add any angular module that references any non-angular JS module that makes a call to window. Note that this will cause the problem even if the angular module is correctly using isPlatformBrowser.

https://github.com/jasonburrows/angularUniversal1675.git

đŸ”„ Exception or Error


> front@2.30.0 servedev:ssr /Users/me/Development/front
> NODE_ENV=dev node dist/server/main

/Users/me/Development/front/dist/server/main.js:200603
}( window, function factory( Outlayer, getSize ) {
   ^

ReferenceError: window is not defined
    at Object../node_modules/masonry-layout/masonry.js (/Users/me/Development/front/dist/server/main.js:200603:4)
    at __webpack_require__ (/Users/me/Development/front/dist/server/main.js:20:30)
    at Module../node_modules/ngx-masonry/__ivy_ngcc__/fesm2015/ngx-masonry.js (/Users/me/Development/front/dist/server/main.js:225304:72)
    at __webpack_require__ (/Users/me/Development/front/dist/server/main.js:20:30)
    at Object../src/modules/core/core.module.ts (/Users/me/Development/front/dist/server/main.js:286092:23)
    at __webpack_require__ (/Users/me/Development/front/dist/server/main.js:20:30)
    at Object../src/app/app.module.ts (/Users/me/Development/front/dist/server/main.js:276737:23)
    at __webpack_require__ (/Users/me/Development/front/dist/server/main.js:20:30)
    at Object../src/app/app.server.module.ts (/Users/me/Development/front/dist/server/main.js:276890:22)
    at __webpack_require__ (/Users/me/Development/front/dist/server/main.js:20:30)
    at Object../src/main.server.ts (/Users/me/Development/front/dist/server/main.js:277166:27)
    at __webpack_require__ (/Users/me/Development/front/dist/server/main.js:20:30)
    at Object../server.ts (/Users/me/Development/front/dist/server/main.js:276464:23)
    at __webpack_require__ (/Users/me/Development/front/dist/server/main.js:20:30)
    at Object.0 (/Users/me/Development/front/dist/server/main.js:354592:18)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! front@2.30.0 servedev:ssr: `NODE_ENV=dev node dist/server/main`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the front@2.30.0 servedev:ssr script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/me/.npm/_logs/2020-05-11T15_57_49_863Z-debug.log

🌍 Your Environment


  ng:analytics getGlobalAnalytics +0ms
  ng:analytics Client Analytics config found: false +26ms
  ng:analytics Analytics disabled. Ignoring all analytics. +1ms
  ng:analytics getSharedAnalytics +0ms

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

Angular CLI: 9.1.5
Node: 12.16.2
OS: darwin x64

Angular: 9.1.6
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... platform-server, router
Ivy Workspace: Yes

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.901.5
@angular-devkit/build-angular     0.901.5
@angular-devkit/build-optimizer   0.901.5
@angular-devkit/build-webpack     0.901.5
@angular-devkit/core              9.1.5
@angular-devkit/schematics        9.1.5
@angular/cdk                      9.2.3
@angular/cli                      9.1.5
@angular/material                 9.2.3
@ngtools/webpack                  9.1.5
@nguniversal/builders             9.1.0
@nguniversal/common               9.1.0
@nguniversal/express-engine       9.1.0
@schematics/angular               9.1.5
@schematics/update                0.901.5
rxjs                              6.5.5
typescript                        3.8.3
webpack                           4.42.0

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 4
  • Comments: 55 (1 by maintainers)

Most upvoted comments

So hey @alan-agius4 - any updates on this?

Kinda looks like SSR has been effectively broken for two versions of Angular and its even more broken in 10 than in 9.

What’s the update? This is a pretty core feature of Angular - how is it not getting any attention?

After an update to Angular 10+

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

Package                                    Version
--------------------------------------------------------------------
@angular-devkit/architect                  0.1001.0
@angular-devkit/build-angular              0.1001.0
@angular-devkit/build-ng-packagr           0.1001.0
@angular-devkit/build-optimizer            0.1001.0
@angular-devkit/build-webpack              0.1001.0
@angular-devkit/core                       10.1.0
@angular-devkit/schematics                 10.1.0
@angular/cdk                               10.2.0
@angular/flex-layout                       10.0.0-beta.32
@angular/material                          10.2.0
@angular/material-moment-adapter           10.2.0
@ngtools/webpack                           10.1.0
@nguniversal/builders                      10.1.0
@nguniversal/common                        10.1.0
@nguniversal/express-engine                10.1.0
@nguniversal/module-map-ngfactory-loader   9.0.0-next.9
@schematics/angular                        10.1.0
@schematics/update                         0.1001.0
ng-packagr                                 10.1.0
rxjs                                       6.5.5
typescript                                 3.9.7
webpack                                    4.44.1

I encountered a regression compared to Angular 9 version On the main.server.ts side, I had defined window via domino (everything was functional in ng9):

global['window'] = win;
global['document'] = win.document;
global['CSS'] = null;
global['XMLHttpRequest'] = xmlhttprequest.XMLHttpRequest;
global['Prism'] = null;
global['HTMLAnchorElement'] = win.HTMLAnchorElement;
global['navigator'] = win.navigator;
global['requestAnimationFrame'] = requestAnimationFrame as unknown as (callback: FrameRequestCallback) => number;
global['cancelAnimationFrame'] = cancelAnimationFrame;
global['Element'] = win.Element;
global['Event'] = win.Event;
global['KeyboardEvent'] = win.KeyboardEvent;

Since the release in angular version 10. The same code no longer works. It seems that global ['window'] is no longer recognized in the main.js suite

ReferenceError: window is not defined because of node_modules / leaflet / dist / leaflet-src.js from ngx-leaflet which does not work in SSR.

I tried to move the redefinition of the global in the server.ts and the main.server.ts but nothing there still makes the same error.

Has there been a change in the consideration of the global between the versions @nguniversal/common": “^9.1.0 and @nguniversal/express-engine”: “^9.1.0 compare to @nguniversal/common”: “^10.1.0” and “@nguniversal/express-engine”: “^10.1.0”

It seems that since 10.1.0 all people can’t build in SSR anymore because of a potential script with window.

I am working with angular 12, and I am still facing this issue with a third library. I tried all the domino solutions but they are not working.

Nope. Essentially it breaks multi platform support in angular and makes using ‘isPlatformBrowser’ a waste of time that accomplished nothing.

I eventually got past it by literally mocking out every single call that anything I use uses (even had to do it for libs used by libs I use).

Amazing waste of time, total hack, and the fact this totally breaking change hasn’t been even really looked at makes my lose confidence in Angular. I won’t be selecting it for future projects.

@alan-agius4 bro


After speaking with the author of ngx-masonry and taking a look myself, the calls to masonry.js are ALREADY correctly guarded with an isPlatformBrowser check.

So what are we supposed to do in this case? It seems like no matter how you guard against it the error will be thrown for SSR merely by importing the module no matter who’s guarding against it.

Hello I have the same problem with angular universal. I have a check with the method isBrowserPlatform in all the places where I called the window but nothing changed. I did the same for the localStorage with the @Santoshah method but nothing to do. I have a huge project and these changes take me a lot of time I also tried many other solutions like domino etc
 There is nothing to do My angular version is 12 Thanks anyway for all yours answers

@mocanapena Let’s not assume things on our own.

I had the same issue 10 days back. Whenever I try to run npm run serve:ssr I get windows not defined.

I have also open stack explaing my question here . I fixed them by following this website

and lastly i updated the tsconfig.server.json

“module”: “commonjs” inside compilerOptions solve the issue for window not defined.

You need to update every window object to the reference. Also make sure you do no have localstorage used or you need to make a reference to window in that case also.

I have manage to fixed this issue in my Angular 12.0.1 v project. Its very big and it took 3 days to fixed that issue and making changes all over the site. If you need personal assistant I can help. (its FREE). lets connect via skype: cssmaniaa

@alan-agius4

I’m sure your root cause is accurate but it happens in many libraries, not just ngx-masonry.

The workaround I have posted only works in v9. It breaks in v10.

What needs to be changed using that technique to have it work in v10?

Hello, we reviewed this issue and determined that it doesn’t fall into the bug report or feature request category. This issue tracker is not suitable for support requests, please repost your issue on StackOverflow using tag angular-universal.

If you are wondering why we don’t resolve support issues via the issue tracker, please check out this explanation.

hey, maybe someone will find this useful, works for angular 11 (and leaflet) p.s. thanks @Santoshah i was missing “module”: “commonjs” inside compilerOptions image

Is there a concrete solution for this yet? I’m stuck. Thanks.

@kamilmysliwiec looks like #451 might be related to the topic here. Definitely related to #830 (comment and a couple above). It seems to be relying solely on Angular CLI Builders for building & serving SSR apps caused this regression bug and domino is no longer working for packages accessing window/other DOM objects directly.

I’ve created 2 repos: one with working Angular 8 and not working Angular 9 using NestJS and its applyDomino, which under the hood is basically domino we tend to use. On of the projects have Angular 10 and it also doesn’t work there, I suspect that’s the same problem with Angular 11 - looks like this problem hasn’t been tackled yet.

@jasonburrows, sorry for the late reply.

The problem here is that ngx-masonry depends on a browser only library masonry-layout. When running the server, the JavaScript engine will need to parse the entire contents of the server JS bundle, it will encounter a window reference and throw an error because this is not defined.

The problem in v9+ occuries because NGCC is transforming the lazy require statement https://github.com/wynfred/ngx-masonry/blob/9c616d8b0598d8795760d1f5fd93aa629669e6b3/src/lib/ngx-masonry.component.ts#L52-L54 into a import statement, which is changing the lazy import into a proper import:

import masonryLayout from 'masonry-layout';
import { __decorate, __param } from 'tslib';
import { EventEmitter, Inject, PLATFORM_ID, ElementRef, Input, Output, Component, forwardRef, Renderer2, Directive, NgModule } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { style, animate, AnimationBuilder } from '@angular/animations';

import * as ɔngcc0 from '@angular/core';
import * as ɔngcc1 from '@angular/animations';

const _c0 = ["ngx-masonry", ""];
const _c1 = ["*"];
let masonryConstructor;
let NgxMasonryComponent = class NgxMasonryComponent {
    constructor(platformId, _element) {
        this.platformId = platformId;
        this._element = _element;
        this.updateLayout = false;
        // Outputs
        this.layoutComplete = new EventEmitter();
        this.removeComplete = new EventEmitter();
    }
    ngOnInit() {
        if (isPlatformBrowser(this.platformId) && masonryConstructor === undefined) {
            masonryConstructor = masonryLayout;
        }
    ...

Note: using a require, there is far from ideal, I think it can be replaced with an import() without causing any issue.

In order to get around this, you can mock the window object, prior to importing the AppServerModule, as per your comment: https://github.com/angular/universal/issues/1675#issuecomment-627030594

There is a great article here by @CaerusKaru to why using isPlatformBrowser is not a good idea and might result in errors, while he also explains better alternatives https://medium.com/@caerus.karu/isplatformbrowser-is-not-your-friend-5ee52b06da89

@Teebo This doesn’t work for me either, apparently for now the only way to use incompatible third party libraries is by editing the code from the same library, this is obviously bad practice. Since in future updates we will have problems, but to solve it momentarily it is a good option. So it would be great if @jasonburrows showed how I solve this by editing the code of those incompatible libraries. In my case I have error in jspdf and @ zxing / ngx-scanner

@jasonburrows thanks, I am wrapping the editor.js code within the isPlatformBrowser, thank you for the points you have raised I will consider those, you have helped me a lot to at least find a way out, I will take it from here, all the best!