Angular2 - How to enter a window into an Angular2 service

I am writing an Angular2 service in TypeScript that will use localstorage. And I want to insert a link to the browser window object in my service, since I do not want to refer to any global variables. Like angular 1.x $window . How to do it?

+106
dependency-injection angular typescript
Dec 09 '15 at 11:05
source share
22 answers

This currently works for me (2018-03, angular 5.2 with AoT, tested in angular-cli and custom web package build):

First, create an injection service that provides a link to the 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 this service in your root AppModule so that it can be deployed everywhere:

 import { WindowRefService } from './window-ref.service'; @NgModule({ providers: [ WindowRefService ], ... }) export class AppModule {} 

and then later where you need to enter 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 can also add nativeDocument and other global variables to this service in the same way if you use them in your application.




edit: Updated with the offer of Truhaynets. edit2: updated for angular 2.1.2 edit3: added note AoT edit4: added workaround of any type edit5: updated solution for using WindowRefService, which fixes the error received when using the previous solution with another assembly edit6: added example of custom typing Window

+127
May 12 '16 at 3:51
source share

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

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 can also use OpaqueToken instead of the Window string.

Edit:

AppModule is used to load your application into main.ts as follows:

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

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

+31
Aug 10 '16 at 13:50
source share

You can simply enter it after you have installed the provider:

 import {provide} from 'angular2/core'; bootstrap(..., [provide(Window, {useValue: window})]); constructor(private window: Window) { // this.window } 
+18
Jan 25 '16 at 21:58
source share

To make it work in Angular 2.1.1, I had to @Inject with a string

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

and then scoff at it

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

and in the usual @NgModule I provide it like this

 { provide: 'Window', useValue: window } 
+15
Oct 27 '16 at 18:56
source share

here is the service i have done for you. https://gist.github.com/gdi2290/f8a524cdfb1f54f1a59c

you can either import {WINDOW, WINDOW_PROVIDERS} from './window-service';
or
import {WindowRef, WINDOW_PROVIDERS} from './window-service';

 @Component({ providers: [WINDOW_PROVIDERS] }) class App { constructor(win: WindowRef, @Inject(WINDOW) win2) { var $window = win.nativeWindow; var $window2 = win2; } } 
+12
Dec 09 '15 at 22:26
source share

You can get a window from the entered 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); } } 
+10
03 Oct '18 at 5:23
source share

I used OpaqueToken for the 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 it is used to import WINDOW_PROVIDERS into 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 import 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 {} 
+9
Aug 14 '16 at 9:32
source share

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

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

Then at your service, etc. insert it into the constructor

 constructor( @Inject(Window) private _window: Window, ) 
+9
Sep 14 '16 at 12:54 on
source share

Before declaring @Component, you can also do this,

 declare var window: any; 

Now the compiler will allow you to access the global window variable as you declare it as an assumed global variable of type any.

I would not suggest accessing the window throughout your application, but you should create services that access / modify the necessary attributes of the window (and embed these services in your components) to the amount that you can do with the window without allowing them to change entire window object.

+9
Jun 15 '17 at 20:43 on
source share

Today (April 2016), the code in the previous solution does not work, I think that you can embed the window directly in App.ts and then collect the necessary values ​​in the service for global access in the application, but If you prefer to create and implement your own service, you can find a simpler solution.

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; } } 
+6
Apr 27 '16 at 20:30
source share

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

I use the same logic to create a small library called ngx-window-token to prevent this from happening again and again.

I used it in another project and built in AoT without any problems.

This is how I used it in another package

Here is the plunker

In your module

imports: [ BrowserModule, WindowTokenModule ] in your component

constructor(@Inject(WINDOW) _window) { }

+6
May 2, '17 at 21:32
source share

It is possible to directly access the window object through a document

 document.defaultView == window 
+4
Aug 31 '17 at 13:47 on
source share

I know that the question is how to insert a window object into the component, but you only do this in order to get to localStorage. If you really want localStorage, why not use a service that provides exactly that, like h5webstorage . Your component will then describe its real dependencies, which make your code more readable.

+3
Apr 28 '16 at 18:31
source share

This is the shortest / cleanest answer I have found while 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); } } 
+3
May 19 '17 at 10:44
source share

Enough to do

 export class AppWindow extends Window {} 

and do

 { provide: 'AppWindow', useValue: window } 

make aot happy

+3
Aug 02 '17 at 2:04 on
source share

You can use NgZone on Angular 4:

 import { NgZone } from '@angular/core'; constructor(private zone: NgZone) {} print() { this.zone.runOutsideAngular(() => window.print()); } 
+2
Aug 25 '17 at 15:21
source share

It is also nice to mark DOCUMENT as optional. In corner documents:

A document may not be available in the application context when the application and rendering contexts do not match (for example, when the application starts in Web Worker).

Here is an example of using DOCUMENT to see if 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 ); 
+1
Nov 14 '18 at 2:02
source share

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

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

Short and sweet and works like a charm.

0
Jun 16 '17 at 2:03
source share

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

But if you do not want to use the window object, you can also use the self keyword, which also points to the window object.

0
Jan 31 '18 at 10:38
source share

Here is another solution that I defaultView recently after I got tired of getting defaultView from the built-in DOCUMENT token and checking it for zero:

 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; } }); 
0
Jul 02 '19 at 16:08
source share

Be easy guys!

 export class HeroesComponent implements OnInit { heroes: Hero[]; window = window; } <div>{{window.Object.entries({ foo: 1 }) | json}}</div> 
-one
Jul 08 '18 at 13:29
source share

Actually this is my main component very easy to access the window object, and I checked its operation

 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 ); } } 
-one
Nov 13 '18 at 17:37
source share



All Articles