How to inject window into a service?

Solution 1:

This is working for me currently (2018-03, angular 5.2 with AoT, tested in angular-cli and a custom webpack build):

First, create an injectable service that provides a reference to window:

import { Injectable } from '@angular/core';

// This interface is optional, showing how you can add strong typings for custom globals.
// Just use "Window" as the type if you don't have custom global stuff
export interface ICustomWindow extends Window {
    __custom_global_stuff: string;
}

function getWindow (): any {
    return window;
}

@Injectable()
export class WindowRefService {
    get nativeWindow (): ICustomWindow {
        return getWindow();
    }
}

Now, register that service with your root AppModule so it can be injected everywhere:

import { WindowRefService } from './window-ref.service';

@NgModule({        
  providers: [
    WindowRefService 
  ],
  ...
})
export class AppModule {}

and then later on where you need to inject window:

import { Component} from '@angular/core';
import { WindowRefService, ICustomWindow } from './window-ref.service';

@Component({ ... })
export default class MyCoolComponent {
    private _window: ICustomWindow;

    constructor (
        windowRef: WindowRefService
    ) {
        this._window = windowRef.nativeWindow;
    }

    public doThing (): void {
        let foo = this._window.XMLHttpRequest;
        let bar = this._window.__custom_global_stuff;
    }
...

You may also wish to add nativeDocument and other globals to this service in a similar way if you use these in your application.


edit: Updated with Truchainz suggestion. edit2: Updated for angular 2.1.2 edit3: Added AoT notes edit4: Adding any type workaround note edit5: Updated solution to use a WindowRefService which fixes an error I was getting when using previous solution with a different build edit6: adding example custom Window typing

Solution 2:

You can get window from injected document.

import { Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

export class MyClass {

  private window: Window;

  constructor(@Inject(DOCUMENT) private document: Document) {
     this.window = this.document.defaultView;
  }

  check() {
    console.log(this.document);
    console.log(this.window);
  }

}

Solution 3:

With the release of angular 2.0.0-rc.5 NgModule was introduced. The previous solution stopped working for me. This is what I did to fix it:

app.module.ts:

@NgModule({        
  providers: [
    { provide: 'Window',  useValue: window }
  ],
  declarations: [...],
  imports: [...]
})
export class AppModule {}

In some component:

import { Component, Inject } from '@angular/core';

@Component({...})
export class MyComponent {
    constructor (@Inject('Window') window: Window) {}
}

You could also use an OpaqueToken instead of the string 'Window'

Edit:

The AppModule is used to bootstrap your application in main.ts like this:

import { platformBrowserDynamic  } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule)

For more information about NgModule read the Angular 2 documentation: https://angular.io/docs/ts/latest/guide/ngmodule.html

Solution 4:

You can just inject it after you've set the provider:

import {provide} from 'angular2/core';
bootstrap(..., [provide(Window, {useValue: window})]);

constructor(private window: Window) {
    // this.window
}

Solution 5:

To get it to work on Angular 2.1.1 I had to @Inject window using a string

  constructor( @Inject('Window') private window: Window) { }

and then mock it like this

beforeEach(() => {
  let windowMock: Window = <any>{ };
  TestBed.configureTestingModule({
    providers: [
      ApiUriService,
      { provide: 'Window', useFactory: (() => { return windowMock; }) }
    ]
  });

and in the ordinary @NgModule I provide it like this

{ provide: 'Window', useValue: window }