What's the difference between providing and injecting 'Window' vs Window in Angular 8 and 9?

In order for your app to work with Server Side Rendering I suggest you not only use window through token, but also create this token in SSR friendly manner, without referencing window at all. Angular has built-in DOCUMENT token for accessing document. Here's what I came up with for my projects to use window through tokens:

import {DOCUMENT} from '@angular/common';
import {inject, InjectionToken} from '@angular/core';

export const WINDOW = new InjectionToken<Window>(
    'An abstraction over global window object',
    {
        factory: () => {
            const {defaultView} = inject(DOCUMENT);

            if (!defaultView) {
                throw new Error('Window is not available');
            }

            return defaultView;
        },
    },
);

Edit: Since this is something people often need, we've used this technique to create a tiny open-source library with injection tokens for global objects, so you can use it:

https://github.com/ng-web-apis/common

It has a sister library for mocks to be used with SSR in Angular Universal:

https://github.com/ng-web-apis/universal

Overall, check out our hub for native APIs in Angular:

https://ng-web-apis.github.io/


Considering the ValueProvider interface:

export declare interface ValueProvider extends ValueSansProvider {
    /**
     * An injection token. Typically an instance of `Type` or `InjectionToken`, but can be `any`.
     */
    provide: any;
    /**
     * When true, injector returns an array of instances. This is useful to allow multiple
     * providers spread across many files to provide configuration information to a common token.
     */
    multi?: boolean;
}

The provide property is of type any. That means any object (included the Window constructor) can go inside it. The object actually doesn't matter, only the reference matters to identify which provider should be used to inject a parameter in a constructor.

It should not be considered as a good practice to use the native Window constructor as an injection token. It fails at compile time because Window exists at run time in a browser environment, it also exists as a TypeScript declare but the Angular 8 compiler cannot do static code analysis to correlate the Window in the providers and the Window in a constructor's parameters, since the assignment of Window is done by the browser, not by the code. Not sure why it works in Angular 9, though...

You should create your own injection token that represents the dependency provider. This injection token should be either:

  • A dedicated string (like you did with 'Window')
  • A dedicated InjectionToken. For example export const window = new InjectionToken<Window>('window');

Moreover, the Angular code should be platform agnostic (should be executable in a browser and on a Node.js server as well) so it would be better to use a factory that returns window or undefined/null, then handle the undefined/null case in the components.