Angular2 - 如何将窗口注入angular2服务

lok*_*anx 107 dependency-injection typescript angular

我在TypeScript中编写了一个Angular2服务,它将使用localstorage.我想将浏览器窗口对象的引用注入我的服务,因为我不想引用任何全局变量.像角1.x的localstorage.我怎么做?

elw*_*wyn 128

这对我来说当前很有用(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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

import { WindowRefService } from './window-ref.service';

@NgModule({        
  providers: [
    WindowRefService 
  ],
  ...
})
export class AppModule {}
Run Code Online (Sandbox Code Playgroud)

然后在你需要注入的地方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;
    }
...
Run Code Online (Sandbox Code Playgroud)

nativeDocument如果在应用程序中使用这些,也可能希望以类似的方式向此服务添加和其他全局变量.


编辑:更新了Truchainz建议.edit2:更新了角度2.1.2 edit3:添加了AoT注释edit4:添加any类型解决方法注意edit5:更新了解决方案以使用WindowRefService修复了我在使用以前的解决方案时使用不同的构建编辑时遇到的错误6:添加示例自定义窗口类型

  • @Brian是的,它仍然在访问`window`,但是它之间的服务允许在单元测试中存根本地`window`的东西,并且正如您提到的SSR一样,可以提供替代服务,该服务公开了模拟/循环窗口服务器。我提到AOT的原因是当Angular更新时AOT中包装窗口中断的几种早期解决方案。 (2认同)

JNK*_*JNK 31

随着角度2.0.0-rc.5的发布,推出了NgModule.之前的解决方案停止了为我工作.这是我做的修复它:

app.module.ts:

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

在某些组件中:

import { Component, Inject } from '@angular/core';

@Component({...})
export class MyComponent {
    constructor (@Inject('Window') window: Window) {}
}
Run Code Online (Sandbox Code Playgroud)

您也可以使用OpaqueToken而不是字符串'Window'

编辑:

AppModule用于在main.ts中引导您的应用程序,如下所示:

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

platformBrowserDynamic().bootstrapModule(AppModule)
Run Code Online (Sandbox Code Playgroud)

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


小智 18

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

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

constructor(private window: Window) {
    // this.window
}
Run Code Online (Sandbox Code Playgroud)

  • 这在Safari中不起作用,因为Window不是可注入的.我必须创建自己的Injectable类型,其中包含我需要的Window属性.如其他答案中所述,可能有更好的方法来创建服务 (6认同)

Kla*_*urn 15

为了使它在Angular 2.1.1上工作,我不得不@Inject使用字符串窗口

  constructor( @Inject('Window') private window: Window) { }
Run Code Online (Sandbox Code Playgroud)

然后像这样嘲笑它

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

而在平常中,@NgModule我提供这样的

{ provide: 'Window', useValue: window }
Run Code Online (Sandbox Code Playgroud)


Ale*_*lin 13

您可以从注入的文档获取窗口。

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

}
Run Code Online (Sandbox Code Playgroud)

  • 这是 2021 年的正确答案...尽管你必须先删除 `this.window` (7认同)

wat*_*lea 11

这是我最近在厌倦了defaultViewDOCUMENT内置令牌中获取并检查它是否为空后提出的另一个解决方案:

import {DOCUMENT} from '@angular/common';
import {inject, InjectionToken} from '@angular/core';

export const WINDOW = new InjectionToken<Window>(
    'An abstraction over global window object',
    {
        factory: () => {
            const {defaultView} = inject(DOCUMENT);

            if (!defaultView) {
                throw new Error('Window is not available');
            }

            return defaultView;
        }
    });
Run Code Online (Sandbox Code Playgroud)

注意:我已经将这个令牌与许多其他全局对象访问器一起发布为一个小库:https : //github.com/ng-web-apis/common

查看组织的其余部分,了解许多其他用于 Angular 的本机 API 库:https : //github.com/ng-web-apis


Chy*_*gyz 9

我使用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: [] }),
];
Run Code Online (Sandbox Code Playgroud)

并且仅用于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 { }
Run Code Online (Sandbox Code Playgroud)

并且只是在我的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 {}
Run Code Online (Sandbox Code Playgroud)


Joe*_*vey 9

在Angular RC4中,以下工作是上述一些答案的组合,在您的根应用程序中添加提供程序:

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

然后在您的服务等中将它注入构造函数

constructor(
      @Inject(Window) private _window: Window,
)
Run Code Online (Sandbox Code Playgroud)


S.G*_*eau 9

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

declare var window: any;
Run Code Online (Sandbox Code Playgroud)

编译器实际上将允许您现在访问全局窗口变量,因为您将其声明为具有类型any的假定全局变量.

我建议您不要在应用程序的任何地方访问窗口,您应该创建访问/修改所需窗口属性的服务(并在组件中注入这些服务),以便对窗口的操作进行范围调整,而不必让他们修改整个窗口对象.


Wil*_*ega 6

截至今天(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;
    }
}
Run Code Online (Sandbox Code Playgroud)


max*_*sam 6

Angular 4引入了InjectToken,它们还为名为DOCUMENT的文档创建了一个令牌.我认为这是官方解决方案,它适用于AoT.

我使用相同的逻辑创建一个名为ngx-window-token的小型库,以防止一遍又一遍地执行此操作.

我已经在其他项目中使用它并在没有问题的情况下在AoT中构建.

这是我在其他包中使用它的方式

这是掠夺者

在你的模块中

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

constructor(@Inject(WINDOW) _window) { }


Vyt*_*nas 5

做就够了

export class AppWindow extends Window {} 
Run Code Online (Sandbox Code Playgroud)

并做

{ provide: 'AppWindow', useValue: window } 
Run Code Online (Sandbox Code Playgroud)

让AOT开心