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.