Angular2 – 如何将窗口注入到angular2服务中

我正在使用本地存储在TypeScript中编写一个Angular2服务。 我想将浏览器窗口对象的引用注入到我的服务中,因为我不想引用任何全局variables。 像angular1.x $window 。 我怎么做?

这是目前为我工作(2017-06,angular度4.1与AoT,在angular度cli和自定义webpack构buildtesting):

首先,创build一个可注入的服务,提供对窗口的引用:

 import { Injectable } from '@angular/core'; function getWindow (): any { return window; } @Injectable() export class WindowRefService { get nativeWindow (): any { return getWindow(); } } 

现在,用你的根AppModule注册该服务,以便可以注入到任何地方:

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

然后在需要注入window地方稍后:

 import { Component} from '@angular/core'; import { WindowRefService } from './window-ref.service'; @Component({ ... }) export default class MyCoolComponent { private _window: Window; constructor ( windowRef: WindowRefService ) { this._window = windowRef.nativeWindow; } public doThing (): void { let foo = this._window.XMLHttpRequest; } ... 

编辑:更新与Truchainzbuild议。 编辑2:更新为angular2.1.2编辑3:添加了AoT注释编辑4:添加anytypes的解决方法注意编辑5:更新的解决scheme,以使用WindowRefService,它修复了一个错误我得到时使用以前的解决scheme与不同的构build

您可以在设置提供程序后注入它:

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

随着2.0.0-rc.5版本的发布,NgModule被引入。 以前的解决scheme停止为我工作。 这是我所做的修复它:

app.module.ts:

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

在一些组件中:

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

你也可以使用一个OpaqueToken而不是string'Window'

编辑:

AppModule被用来像这样在main.ts中引导你的应用程序:

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

有关NgModule的更多信息,请阅读Angular 2文档: https ://angular.io/docs/ts/latest/guide/ngmodule.html

这是我为你做的一项服务。 https://gist.github.com/gdi2290/f8a524cdfb1f54f1a59c

你也可以
import {WINDOW, WINDOW_PROVIDERS} from './window-service';
要么
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; } } 

我用“窗口”string的OpaqueToken :

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

并且用于在Angular 2.0.0-rc-4的bootstrap中导入WINDOW_PROVIDERS

但是随着Angular 2.0.0-rc.5的发布,我需要创build一个单独的模块:

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

并刚刚定义在我的主要app.module.ts的import属性

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

为了让它在Angular 2.1.1上工作,我不得不使用一个string@Inject窗口

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

然后像这样嘲笑它

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

并在普通的@NgModule我提供这样的

 { provide: 'Window', useValue: window } 

截至今天(2016年4月),以前的解决scheme中的代码不起作用,我认为可以直接在App.ts中注入窗口,然后将所需的值收集到应用程序中的全局访问服务中,但是如果你喜欢创build和注入自己的服务,更简单的解决scheme是这样的。

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

在Angular RC4中,下面的作品是上面的一些答案的组合,在你的根app.ts中添加提供者:

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

然后在你的服务等注入到构造函数

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

Angular 4引入了InjectToken,并且还为文档DOCUMENT创build了一个标记。 我认为这是官方解决scheme,它在AoT中有效。

我使用相同的逻辑来创build一个名为ngx-window-token的小型库,以防止这样做。

我已经在其他项目中使用它,并在没有问题的情况下构buildAoT。

这是我如何在其他包中使用它

这里是抢劫犯

在你的模块中

imports: [ BrowserModule, WindowTokenModule ]在你的组件中

constructor(@Inject(WINDOW) _window) { }

我知道问题是如何将窗口对象注入到组件中,但是您只是为了访问localStorage而已。 如果你真的只想要localStorage,为什么不使用公开的服务,如h5webstorage 。 然后你的组件将描述它的真正的依赖关系,这使得你的代码更具可读性。

在@Component声明之前,你也可以这样做,

 declare var window: any; 

编译器实际上让你然后现在访问全局窗口variables,因为你声明它是一个假设的全局variables,types为any。

我不会build议您在应用程序的任何地方访问窗口,但是您应该创build访问/修改所需窗口属性(并在组件中注入这些服务)的服务,以确定您可以对窗口执行的操作,而不用让它们修改整个窗口对象。

这足以做到

 export class AppWindow extends Window {} 

并做

 { provide: 'AppWindow', useValue: window } 

让AOT高兴

这是我用Angular 4 AOT发现的最短/最干净的答案

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

你可以在Angular 4上使用NgZone:

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

@maxisam感谢ngx-window-token 。 我正在做类似的事情,但切换到你的。 这是我收听窗口大小调整事件和通知订阅者的服务。

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

通过文档有直接访问窗口对象的机会

 document.defaultView == window