Angular2 – Cómo inyectar ventana en un servicio angular2

Estoy escribiendo un servicio Angular2 en TypeScript que hará uso del almacenamiento local. Y quiero insertar una referencia al objeto de la ventana del navegador en mi servicio, ya que no quiero hacer referencia a ninguna variable global. Al igual que la $window angular 1.x $window . ¿Cómo puedo hacer eso?

Esto funciona para mí actualmente (2018-03, angular 5.2 con AoT, probado en angular-cli y una construcción de paquete web personalizado):

Primero, cree un servicio inyectable que proporcione una referencia a la ventana:

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

Ahora, registre ese servicio con su AppModule raíz para que pueda ser inyectado en todas partes:

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

y luego en donde necesitas inyectar la 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; } ... 

También puede agregar nativeDocument y otros globales a este servicio de manera similar si los usa en su aplicación.


editar: actualizado con la sugerencia de Truchainz. edit2: actualizado para angular 2.1.2 edit3: notas AoT agregadas edit4: Agregar any tipo de solución temporal edit5: Solución actualizada para usar un WindowRefService que corrige un error que estaba obteniendo al usar una solución anterior con una edición diferente edit6: agregar ejemplo personalizado Mecanografía de ventanas

Con el lanzamiento de angular 2.0.0-rc.5, se introdujo NgModule. La solución anterior dejó de funcionar para mí. Esto es lo que hice para arreglarlo:

app.module.ts:

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

En algún componente:

 import { Component, Inject } from '@angular/core'; @Component({...}) export class MyComponent { constructor (@Inject('Window') window: Window) {} } 

También podría usar un OpaqueToken en lugar de la cadena ‘Ventana’

Editar:

El AppModule se utiliza para iniciar su aplicación en main.ts de esta manera:

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

Para obtener más información acerca de NgModule, lea la documentación de Angular 2: https://angular.io/docs/ts/latest/guide/ngmodule.html

Solo puede inyectarlo después de configurar el proveedor:

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

aquí hay un servicio que hice para ti. https://gist.github.com/gdi2290/f8a524cdfb1f54f1a59c

tu también puedes
import {WINDOW, WINDOW_PROVIDERS} from './window-service';
o
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; } } 

Para que funcione en Angular 2.1.1 tuve que @Inject window usando una cadena

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

y luego burlarse de esta manera

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

y en el @NgModule ordinario lo proporciono así

 { provide: 'Window', useValue: window } 

Utilicé OpaqueToken para la cadena ‘Ventana’:

 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: [] }), ]; 

Y se usa solo para importar WINDOW_PROVIDERS en bootstrap en Angular 2.0.0-rc-4.

Pero con el lanzamiento de Angular 2.0.0-rc.5 necesito crear un módulo separado:

 import { NgModule } from '@angular/core'; import { WINDOW_PROVIDERS } from './window'; @NgModule({ providers: [WINDOW_PROVIDERS] }) export class WindowModule { } 

y solo se define en la propiedad de importación de mi app.module.ts principal

 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 {} 

Antes de la statement @Component, puedes hacer eso también,

 declare var window: any; 

El comstackdor realmente le permitirá acceder a la variable de ventana global ahora, ya que la declara como una variable global asumida con type any.

Sin embargo, no recomendaría acceder a la ventana en todas partes en su aplicación. Debería crear servicios que accedan / modifiquen los atributos de ventana necesarios (e inyecte esos servicios en sus componentes) para determinar qué se puede hacer con la ventana sin permitirles modificar el todo el objeto de la ventana.

A partir de hoy (abril de 2016), el código en la solución anterior no funciona, creo que es posible inyectar ventanas directamente en App.ts y luego reunir los valores que necesita en un servicio para acceso global en la aplicación, pero si prefiere crear e inyectar su propio servicio, una solución mucho más simple es esta.

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

En Angular RC4, funciona lo siguiente, que es una combinación de algunas de las respuestas anteriores, en su aplicación raíz. Añádalo a los proveedores:

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

Luego, en su servicio, etc. inyéctelo en el constructor

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

Angular 4 introduce InjectToken, y también crean un token para el documento llamado DOCUMENT . Creo que esta es la solución oficial y funciona en AoT.

Utilizo la misma lógica para crear una pequeña biblioteca llamada ngx-window-token para evitar hacer esto una y otra vez.

Lo he usado en otro proyecto y comstackdo en AoT sin problemas.

Así es como lo usé en otro paquete

Aquí está el plunker

En tu módulo

imports: [ BrowserModule, WindowTokenModule ] en su componente

constructor(@Inject(WINDOW) _window) { }

Sé que la pregunta es cómo inyectar el objeto de la ventana en un componente, pero estás haciendo esto solo para llegar a LocalStorage, parece. Si realmente quiere localStorage, ¿por qué no utilizar un servicio que expone precisamente eso, como h5webstorage ? Luego, su componente describirá sus dependencias reales, lo que hace que su código sea más legible.

Es suficiente hacer

 export class AppWindow extends Window {} 

y hacer

 { provide: 'AppWindow', useValue: window } 

para hacer AOT feliz

Puedes usar NgZone en Angular 4:

 import { NgZone } from '@angular/core'; constructor(private zone: NgZone) {} print() { this.zone.runOutsideAngular(() => window.print()); } 

Hay una oportunidad para el acceso directo al objeto de ventana a través del documento

 document.defaultView == window 

Esta es la respuesta más corta / más limpia que he encontrado trabajando con Angular 4 AOT

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

@maxisam gracias por ngx-window-token . Estaba haciendo algo similar, pero cambié al tuyo. Este es mi servicio para escuchar eventos de cambio de tamaño de ventana y notificar a los suscriptores.

 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 => {width: event['currentTarget'].innerWidth, height: event['currentTarget'].innerHeight}) .subscribe((windowSize) => { this.windowSizeChanged$.next(windowSize); }); } readonly windowSizeChanged$ = new BehaviorSubject({width: this._window.innerWidth, height: this._window.innerHeight}); } 

¡Mantenlo simple, amigos!

 export class HeroesComponent implements OnInit { heroes: Hero[]; window = window; } 
{{window.Object.entries({ foo: 1 }) | json}}

Obtención de objetos de ventana a través de DI (Inyección de Dependencia) no es una buena idea cuando las variables globales son accesibles a través de la aplicación.

Pero si no desea usar el objeto ventana, también puede usar self palabra clave self que también apunta al objeto ventana.