[angular] How to inject window into a service?

I'm writing an Angular 2 service in TypeScript that will make use of localstorage. I want to inject a reference to the browser window object into my service since I don't want to reference any global variables like Angular 1.x $window.

How do I do that?

This question is related to angular typescript dependency-injection

The answer is


@maxisam thanks for ngx-window-token. I was doing something similar but switched to yours. This is my service for listening to window resize events and notifying subscribers.

_x000D_
_x000D_
import { Inject, Injectable } from '@angular/core';_x000D_
import { BehaviorSubject } from 'rxjs/BehaviorSubject';_x000D_
import { Observable } from 'rxjs/Observable';_x000D_
import 'rxjs/add/observable/fromEvent';_x000D_
import { WINDOW } from 'ngx-window-token';_x000D_
_x000D_
_x000D_
export interface WindowSize {_x000D_
    readonly width: number;_x000D_
    readonly height: number;_x000D_
}_x000D_
_x000D_
@Injectable()_x000D_
export class WindowSizeService {_x000D_
_x000D_
    constructor( @Inject(WINDOW) private _window: any ) {_x000D_
        Observable.fromEvent(_window, 'resize')_x000D_
        .auditTime(100)_x000D_
        .map(event => <WindowSize>{width: event['currentTarget'].innerWidth, height: event['currentTarget'].innerHeight})_x000D_
        .subscribe((windowSize) => {_x000D_
            this.windowSizeChanged$.next(windowSize);_x000D_
        });_x000D_
    }_x000D_
_x000D_
    readonly windowSizeChanged$ = new BehaviorSubject<WindowSize>(<WindowSize>{width: this._window.innerWidth, height: this._window.innerHeight});_x000D_
}
_x000D_
_x000D_
_x000D_

Short and sweet and works like a charm.


Getting window object via DI(Dependency Injection) is not a good idea when global variables are accessible throughout the application.

But if you don't want to use window object then you can also use self keyword which also points to window object.


I know the question is how to inject the window object into a component but you're doing this just to get to localStorage it seems. If you realy just want localStorage, why not use a service that exposes just that, like h5webstorage. Then you component will describe its real dependencies which makes your code more readable.


There is an opportunity for direct access to the object of window through the document

document.defaultView == window

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


You can get window from injected document.

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

export class MyClass {

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

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

}

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


Angular 4 introduce InjectToken, and they also create a token for document called DOCUMENT. I think this is the official solution and it works in AoT.

I use the same logic to create a small library called ngx-window-token to prevent doing this over and over.

I have used it in other project and build in AoT without issues.

Here is how I used it in other package

Here is the plunker

In your module

imports: [ BrowserModule, WindowTokenModule ] In your component

constructor(@Inject(WINDOW) _window) { }


Actually its very simple to access window object here is my basic component and i tested it its working

import { Component, OnInit,Inject } from '@angular/core';
import {DOCUMENT} from '@angular/platform-browser';

@Component({
  selector: 'app-verticalbanners',
  templateUrl: './verticalbanners.component.html',
  styleUrls: ['./verticalbanners.component.css']
})
export class VerticalbannersComponent implements OnInit {

  constructor(){ }

  ngOnInit() {
    console.log(window.innerHeight );
  }

}

Before the @Component declaration, you can do that too,

declare var window: any;

The compiler will actually let you then access the global window variable now since you declare it as an assumed global variable with type any.

I wouldn't suggest to access window everywhere in your application though, You should create services that access/modify the needed window attributes (and inject those services in your components) to scope what you can do with the window without letting them to modify the whole window object.


Here's another solution I came up recently after I got tired of getting defaultView from DOCUMENT built-in token and checking it for null:

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;
        }
    });

Note: I have released this token with many other global object accessors as a tiny library available here: https://github.com/ng-web-apis/common

Check out the rest of the org for many other native API libraries for Angular: https://github.com/ng-web-apis


You can use NgZone on Angular 4:

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

constructor(private zone: NgZone) {}

print() {
    this.zone.runOutsideAngular(() => window.print());
}

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 }

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
}

This is the shortest/cleanest answer that I've found working with Angular 4 AOT

Source: https://github.com/angular/angular/issues/12631#issuecomment-274260009

@Injectable()
export class WindowWrapper extends Window {}

export function getWindow() { return window; }

@NgModule({
  ...
  providers: [
    {provide: WindowWrapper, useFactory: getWindow}
  ]
  ...
})
export class AppModule {
  constructor(w: WindowWrapper) {
    console.log(w);
  }
}

It's also a good idea to mark the DOCUMENT as optional. Per the Angular docs:

Document might not be available in the Application Context when Application and Rendering Contexts are not the same (e.g. when running the application into a Web Worker).

Here's an example of using the DOCUMENT to see whether the browser has SVG support:

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

...

constructor(@Optional() @Inject(DOCUMENT) document: Document) {
   this.supportsSvg = !!(
   document &&
   document.createElementNS &&
   document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect
);

In Angular RC4 the following works which is a combination of some of the above answers, in your root app.ts add it the providers:

@Component({
    templateUrl: 'build/app.html',
    providers: [
        anotherProvider,
        { provide: Window, useValue: window }
    ]
})

Then in your service etc inject it into the constructor

constructor(
      @Inject(Window) private _window: Window,
)

It is enough to do

export class AppWindow extends Window {} 

and do

{ provide: 'AppWindow', useValue: window } 

to make AOT happy


As of today (April 2016), the code in the previous solution doesn't work, I think it is possible to inject window directly into App.ts and then gather the values you need into a service for global access in the App, but if you prefer to create and inject your own service, a way simpler solution is this.

https://gist.github.com/WilldelaVega777/9afcbd6cc661f4107c2b74dd6090cebf

//--------------------------------------------------------------------------------------------------
// Imports Section:
//--------------------------------------------------------------------------------------------------
import {Injectable} from 'angular2/core'
import {window} from 'angular2/src/facade/browser';

//--------------------------------------------------------------------------------------------------
// Service Class:
//--------------------------------------------------------------------------------------------------
@Injectable()
export class WindowService
{
    //----------------------------------------------------------------------------------------------
    // Constructor Method Section:
    //----------------------------------------------------------------------------------------------
    constructor(){}

    //----------------------------------------------------------------------------------------------
    // Public Properties Section:
    //----------------------------------------------------------------------------------------------
    get nativeWindow() : Window
    {
        return window;
    }
}

I used OpaqueToken for 'Window' string:

import {unimplemented} from '@angular/core/src/facade/exceptions';
import {OpaqueToken, Provider} from '@angular/core/index';

function _window(): any {
    return window;
}

export const WINDOW: OpaqueToken = new OpaqueToken('WindowToken');

export abstract class WindowRef {
    get nativeWindow(): any {
        return unimplemented();
    }
}

export class BrowserWindowRef extends WindowRef {
    constructor() {
        super();
    }
    get nativeWindow(): any {
        return _window();
    }
}


export const WINDOW_PROVIDERS = [
    new Provider(WindowRef, { useClass: BrowserWindowRef }),
    new Provider(WINDOW, { useFactory: _window, deps: [] }),
];

And used just to import WINDOW_PROVIDERS in bootstrap in Angular 2.0.0-rc-4.

But with the release of Angular 2.0.0-rc.5 I need to create a separate module:

import { NgModule } from '@angular/core';
import { WINDOW_PROVIDERS } from './window';

@NgModule({
    providers: [WINDOW_PROVIDERS]
})
export class WindowModule { }

and just defined in the imports property of my main app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { WindowModule } from './other/window.module';

import { AppComponent } from './app.component';

@NgModule({
    imports: [ BrowserModule, WindowModule ],
    declarations: [ ... ],
    providers: [ ... ],
    bootstrap: [ AppComponent ]
})
export class AppModule {}

Keep it simple, folks!

export class HeroesComponent implements OnInit {
  heroes: Hero[];
  window = window;
}

<div>{{window.Object.entries({ foo: 1 }) | json}}</div>

Examples related to angular

error NG6002: Appears in the NgModule.imports of AppModule, but could not be resolved to an NgModule class error TS1086: An accessor cannot be declared in an ambient context in Angular 9 TS1086: An accessor cannot be declared in ambient context @angular/material/index.d.ts' is not a module Why powershell does not run Angular commands? error: This is probably not a problem with npm. There is likely additional logging output above Angular @ViewChild() error: Expected 2 arguments, but got 1 Schema validation failed with the following errors: Data path ".builders['app-shell']" should have required property 'class' Access blocked by CORS policy: Response to preflight request doesn't pass access control check origin 'http://localhost:4200' has been blocked by CORS policy in Angular7

Examples related to typescript

TS1086: An accessor cannot be declared in ambient context Element implicitly has an 'any' type because expression of type 'string' can't be used to index Angular @ViewChild() error: Expected 2 arguments, but got 1 Typescript: No index signature with a parameter of type 'string' was found on type '{ "A": string; } Understanding esModuleInterop in tsconfig file How can I solve the error 'TS2532: Object is possibly 'undefined'? Typescript: Type 'string | undefined' is not assignable to type 'string' Typescript: Type X is missing the following properties from type Y length, pop, push, concat, and 26 more. [2740] Can't perform a React state update on an unmounted component TypeScript and React - children type?

Examples related to dependency-injection

Are all Spring Framework Java Configuration injection examples buggy? Passing data into "router-outlet" child components ASP.NET Core Dependency Injection error: Unable to resolve service for type while attempting to activate Error when trying to inject a service into an angular component "EXCEPTION: Can't resolve all parameters for component", why? org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'demoRestController' How to inject window into a service? How to get bean using application context in spring boot Resolving instances with ASP.NET Core DI from within ConfigureServices How to inject a Map using the @Value Spring Annotation? WELD-001408: Unsatisfied dependencies for type Customer with qualifiers @Default