我在TypeScript中编写了一个Angular2服务,它将使用localstorage.我想将浏览器窗口对象的引用注入我的服务,因为我不想引用任何全局变量.像角1.x的localstorage
.我怎么做?
这对我来说当前很有用(2018-03,带有AoT的角度5.2,在angular-cli和自定义webpack版本中测试):
首先,创建一个可注入的服务,提供对窗口的引用:
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(); } }
现在,使用您的根AppModule注册该服务,以便可以在任何地方注入:
import { WindowRefService } from './window-ref.service'; @NgModule({ providers: [ WindowRefService ], ... }) export class AppModule {}
然后在你需要注入的地方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; } ...
nativeDocument
如果在应用程序中使用这些,也可能希望以类似的方式向此服务添加和其他全局变量.
编辑:更新了Truchainz建议.edit2:更新了角度2.1.2 edit3:添加了AoT注释edit4:添加any
类型解决方法注意edit5:更新了解决方案以使用WindowRefService修复了我在使用以前的解决方案时使用不同的构建编辑时遇到的错误6:添加示例自定义窗口类型
随着角度2.0.0-rc.5的发布,推出了NgModule.之前的解决方案停止了为我工作.这是我做的修复它:
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而不是字符串'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
您可以在设置提供程序后注入它:
import {provide} from 'angular2/core'; bootstrap(..., [provide(Window, {useValue: window})]); constructor(private window: Window) { // this.window }
为了使它在Angular 2.1.1上工作,我不得不@Inject
使用字符串窗口
constructor( @Inject('Window') private window: Window) { }
然后像这样嘲笑它
beforeEach(() => { let windowMock: Window ={ }; TestBed.configureTestingModule({ providers: [ ApiUriService, { provide: 'Window', useFactory: (() => { return windowMock; }) } ] });
而在平常中,@NgModule
我提供这样的
{ provide: 'Window', useValue: window }
您可以从注入的文档获取窗口。
import { Inject } from '@angular/core'; import { DOCUMENT } from '@angular/common'; export class MyClass { constructor(@Inject(DOCUMENT) private document: Document) { this.window = this.document.defaultView; } check() { console.log(this.document); console.log(this.window); } }
我使用OpaqueToken作为'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: [] }), ];
并且仅用于WINDOW_PROVIDERS
在Angular 2.0.0-rc-4中的bootstrap中导入.
但随着Angular 2.0.0-rc.5的发布,我需要创建一个单独的模块:
import { NgModule } from '@angular/core'; import { WINDOW_PROVIDERS } from './window'; @NgModule({ providers: [WINDOW_PROVIDERS] }) export class WindowModule { }
并且只是在我的main的imports属性中定义的 app.module.ts
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 RC4中,以下工作是上述一些答案的组合,在您的根应用程序中添加提供程序:
@Component({ templateUrl: 'build/app.html', providers: [ anotherProvider, { provide: Window, useValue: window } ] })
然后在您的服务等中将它注入构造函数
constructor( @Inject(Window) private _window: Window, )
在@Component声明之前,你也可以这样做,
declare var window: any;
编译器实际上将允许您现在访问全局窗口变量,因为您将其声明为具有类型any的假定全局变量.
我建议您不要在应用程序的任何地方访问窗口,您应该创建访问/修改所需窗口属性的服务(并在组件中注入这些服务),以便对窗口的操作进行范围调整,而不必让他们修改整个窗口对象.
截至今天(2016年4月),上一个解决方案中的代码不起作用,我认为可以将窗口直接注入到App.ts中,然后将所需的值收集到服务中以便在App中进行全局访问,但是如果您更喜欢创建和注入自己的服务,那么更简单的解决方案就是这样.
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 4引入了InjectToken,它们还为名为DOCUMENT的文档创建了一个令牌.我认为这是官方解决方案,它适用于AoT.
我使用相同的逻辑创建一个名为ngx-window-token的小型库,以防止一遍又一遍地执行此操作.
我已经在其他项目中使用它并在没有问题的情况下在AoT中构建.
这是我在其他包中使用它的方式
这是掠夺者
在你的模块中
imports: [ BrowserModule, WindowTokenModule ]
在您的组件中
constructor(@Inject(WINDOW) _window) { }