Angular2 – Come iniettare una finestra in un servizio angular2

Sto scrivendo un servizio Angular2 in TypeScript che farà uso di localstorage. E voglio iniettare un riferimento all’object finestra del browser nel mio servizio poiché non voglio fare riferimento a nessuna variabile globale. Come $window angular 1.x $window . Come lo faccio?

Attualmente sto lavorando per me (2018-03, angular 5.2 con AoT, testato in angular-cli e una build webpack personalizzata):

Innanzitutto, crea un servizio iniettabile che fornisce un riferimento alla finestra:

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

Ora, registra quel servizio con il tuo AppModule di root in modo che possa essere iniettato ovunque:

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

e poi in seguito dove è necessario iniettare 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; } ... 

Potresti anche voler aggiungere nativeDocument e altri globals a questo servizio in modo simile se li usi nella tua applicazione.


modifica: aggiornato con il suggerimento Truchainz. edit2: aggiornato per angular 2.1.2 edit3: aggiunto note AoT edit4: aggiunta di any tipo di nota alternativa edit5: soluzione aggiornata per l’utilizzo di un WindowRefService che corregge un errore che stavo ricevendo quando si utilizzava la soluzione precedente con una modifica di compilazione diversa: aggiunta di un esempio personalizzato Digitazione di windows

Con il rilascio di Angular 2.0.0-rc.5 è stato introdotto NgModule. La soluzione precedente ha smesso di funzionare per me. Questo è quello che ho fatto per risolverlo:

app.module.ts:

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

In alcuni componenti:

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

Puoi anche usare un OpaqueToken invece della stringa ‘Window’

Modificare:

L’AppModule viene utilizzato per eseguire il bootstrap dell’applicazione in main.ts come questo:

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

Per maggiori informazioni su NgModule leggi la documentazione di Angular 2: https://angular.io/docs/ts/latest/guide/ngmodule.html

Puoi iniettarlo solo dopo aver impostato il provider:

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

ecco un servizio che ho fatto per te. https://gist.github.com/gdi2290/f8a524cdfb1f54f1a59c

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

Per farlo funzionare su Angular 2.1.1 ho dovuto @Inject finestra @Inject usando una stringa

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

e poi deriderlo in questo modo

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

e nell’ordinario @NgModule lo fornisco in questo modo

 { provide: 'Window', useValue: window } 

Ho usato OpaqueToken per la stringa ‘Window’:

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

E utilizzato solo per importare WINDOW_PROVIDERS in bootstrap in Angular 2.0.0-rc-4.

Ma con il rilascio di Angular 2.0.0-rc.5 ho bisogno di creare un modulo separato:

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

e appena definito nella proprietà app.module.ts del mio app.module.ts principale

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

Prima della dichiarazione @Component, puoi farlo anche tu,

 declare var window: any; 

Il compilatore ti permetterà di accedere alla variabile della finestra globale ora poiché lo dichiari come una variabile globale ipotizzata con tipo any.

Non suggerirei di accedere alla finestra ovunque nell’applicazione, tuttavia, dovresti creare servizi che accedono / modificano gli attributi della finestra necessari (e iniettano tali servizi nei tuoi componenti) per mettere in mostra ciò che puoi fare con la finestra senza consentire loro di modificare il intero object finestra.

Ad oggi (aprile 2016), il codice nella soluzione precedente non funziona, penso che sia ansible iniettare la finestra direttamente in App.ts e quindi raccogliere i valori necessari in un servizio per l’accesso globale nell’app, ma se si preferisce creare e iniettare il proprio servizio, una soluzione più semplice è questa.

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

In Angular RC4 i seguenti lavori che sono una combinazione di alcune delle risposte di cui sopra, nella root app.ts aggiungono i provider:

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

Poi nel tuo servizio, ecc., Iniettalo nel costruttore

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

Angular 4 introduce InjectToken e crea anche un token per il documento chiamato DOCUMENT . Penso che questa sia la soluzione ufficiale e funziona in AoT.

Io uso la stessa logica per creare una piccola libreria chiamata ngx-window-token per evitare di farlo ripetutamente.

L’ho usato in altri progetti e costruito in AoT senza problemi.

Ecco come l’ho usato in un altro pacchetto

Ecco il plunker

Nel tuo modulo

imports: [ BrowserModule, WindowTokenModule ] nel componente

constructor(@Inject(WINDOW) _window) { }

So che la domanda è come iniettare l’object finestra in un componente, ma lo stai facendo solo per arrivare a localStorage a quanto pare. Se si desidera solo localStorage, perché non utilizzare un servizio che espone solo questo, come h5webstorage . Quindi il componente descriverà le sue reali dipendenze che renderà il tuo codice più leggibile.

È abbastanza da fare

 export class AppWindow extends Window {} 

e fai

 { provide: 'AppWindow', useValue: window } 

rendere felice AOT

Puoi utilizzare NgZone su Angular 4:

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

C’è un’opportunità per l’accesso diretto all’object della finestra attraverso il documento

 document.defaultView == window 

Questa è la risposta più breve / pulita che ho trovato a lavorare con Angular 4 AOT

Fonte: 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 grazie per ngx-window-token . Stavo facendo qualcosa di simile ma sono passato al tuo. Questo è il mio servizio per ascoltare gli eventi di ridimensionamento delle windows e notificare gli abbonati.

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

Keep it simple, gente!

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

Ottenere object finestra tramite DI (Dependency Injection) non è una buona idea quando le variabili globali sono accessibili in tutta l’applicazione.

Ma se non si desidera utilizzare l’object finestra, è ansible utilizzare anche self parola chiave self che punta anche all’object finestra.