Javascriptservices: Window is not defined error even after using isBrowser

Created on 19 Jan 2017  路  3Comments  路  Source: aspnet/JavaScriptServices

I added slick.js to my project.

entry: {
        vendor: [
            ....
            'jquery',
             ....
            'slick-carousel',
            'slick-carousel/slick/slick.css'
        ]
    }

Added a typescript file for it and referenced it in one of my components.

import { isBrowser, isNode } from 'angular2-universal';
import 'slick-carousel';

@Component({
  ....
})

export class HomeComponent {
    constructor() {
    ....
        if (isBrowser) {

            $('#recom-carousel').slick({
                dots: false,
                infinite: true,
                speed: 300,
                slidesToShow: 4,
                slidesToScroll: 1,
                variableWidth: true
            });
        }
    }
}

However, even after using isBrowser, i still get window is not defined error.
capture

Most helpful comment

slick.js doesn't support being loaded in a Node environment. It tries to reference window immediately, even before you invoke any of its exports. It's not enough to use an if(isBrowser) { ... }, check - simply having an import that loads the slick.js code is going to throw in a non-browser environment.

Fortunately this is fairly easy to work around. Three things you need to do:

  1. Don't put the import 'slick-carousel'; line into any Angular 2 component (because those files load for server-side execution). Instead, put that line into your boot-client.ts file. Then the library will only be loaded for client-side execution.
  2. Now, in each component where you want to use it, put import { isBrowser } from 'angular2-universal'; and import * as $ from 'jquery'; at the top, then use the if(isBrowser) { ... } test around any place you try to call $(...).slick.
  3. Be sure to update your package.json file to reference jQuery 3.0.0+ (e.g., "jquery": "^3.0.0"), because that's what slick.js depends on, and then rebuild both your vendor and main bundles. If you don't do this, then your code is referencing a different version of jQuery than slick is, so Webpack will bundle two differently-versioned copies of jQuery, and it won't work at runtime because slick will have attached itself to a different instance of jQuery than your code receives.

All 3 comments

slick.js doesn't support being loaded in a Node environment. It tries to reference window immediately, even before you invoke any of its exports. It's not enough to use an if(isBrowser) { ... }, check - simply having an import that loads the slick.js code is going to throw in a non-browser environment.

Fortunately this is fairly easy to work around. Three things you need to do:

  1. Don't put the import 'slick-carousel'; line into any Angular 2 component (because those files load for server-side execution). Instead, put that line into your boot-client.ts file. Then the library will only be loaded for client-side execution.
  2. Now, in each component where you want to use it, put import { isBrowser } from 'angular2-universal'; and import * as $ from 'jquery'; at the top, then use the if(isBrowser) { ... } test around any place you try to call $(...).slick.
  3. Be sure to update your package.json file to reference jQuery 3.0.0+ (e.g., "jquery": "^3.0.0"), because that's what slick.js depends on, and then rebuild both your vendor and main bundles. If you don't do this, then your code is referencing a different version of jQuery than slick is, so Webpack will bundle two differently-versioned copies of jQuery, and it won't work at runtime because slick will have attached itself to a different instance of jQuery than your code receives.

you're so awesome. thanks! :)

i actually saw that part of slick js that immediately calls 'window'. got a hunch that maybe that's causing the issue. but your explanation about importing it in the components cleared everything. thanks again!

Steve, thanks for the info.
I have a similar issue and this has helped get me on right tracks but I can't quite figure out how to get around my issue. I am using the Ace editor. I imported the following js in the boot-client instead of the component I was using it as suggested.
import 'brace/theme/monokai';
import 'brace/mode/javascript';

I am using the AceEditorDirective so in the module file I use the isBrowser like so:

var decs: any = [
EqualValidator,
AppComponent,
NavMenuComponent,
HomeComponent,
PricesComponent,
ContactComponent,
SignupComponent,
LoginComponent,
TestsComponent,
TestComponent
];

if (isBrowser) {
console.log("broswer");
decs.push(AceEditorDirective);
};

Still having the issue. It seems to be pushing this directive even when not a browser or maybe I am missing something. (newbie to Angular 2 and Typescript)

Thanks

Was this page helpful?
0 / 5 - 0 ratings

Related issues

asadsahi picture asadsahi  路  3Comments

mounthorse-slns picture mounthorse-slns  路  3Comments

harsimranb picture harsimranb  路  3Comments

Sampath-Lokuge picture Sampath-Lokuge  路  4Comments

justinyoo picture justinyoo  路  3Comments