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:添加any
types的解决方法注意编辑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