Answers:
目前这对我有用(2018-03,使用AoT的angular 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的解决方案,该错误修复了我在将以前的解决方案与其他版本一起使用时遇到的错误edit6:添加了示例自定义Window键入
@Inject
我不会No provider for Window
出错。不需要手册真是太好了@Inject
!
@Inject(Window)
这个工作
window
,但由于两者之间具有服务,因此可以window
在单元测试中存储本机内容,并且正如您提到的SSR一样,可以提供替代服务,该服务公开了服务器的模拟/循环窗口。我提到AOT的原因是当Angular更新时AOT中包装窗口中断的几种早期解决方案。
随着角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
}
window.var
页面内容时,该内容不会更改
您可以从注入的文档获取窗口。
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);
}
}
为了使其在Angular 2.1.1上运行,我必须@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 }
在@Component声明之前,您也可以这样做,
declare var window: any;
编译器实际上将允许您立即访问全局窗口变量,因为您将其声明为类型为any的假定全局变量。
我不建议您在应用程序中的任何地方访问窗口,您应该创建访问/修改所需窗口属性的服务(并将这些服务注入组件中),以限制您可以对窗口执行的操作,而不用让它们修改窗口内容。整个窗口对象。
我使用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: [] }),
];
并仅用于导入 WINDOW_PROVIDERS
在Angular 2.0.0-rc-4中的引导程序中。
但是随着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 {}
截至今天(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;
}
}
有机会通过文档直接访问窗口对象
document.defaultView == window
这是我厌倦了defaultView
从DOCUMENT
内置令牌获取并检查其是否为null 之后最近提出的另一种解决方案:
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;
}
});
@Inject(WINDOW) private _window: any
并像Angular提供的DOCUMENT注入令牌一样使用它?
我知道问题是如何将窗口对象注入到组件中,但是您这样做似乎只是为了进入localStorage。如果您真的只想要localStorage,为什么不使用公开了该服务的服务,例如h5webstorage。然后,您的组件将描述其真正的依赖关系,这将使您的代码更具可读性。
这是我在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);
}
}
这足以做
export class AppWindow extends Window {}
并做
{ provide: 'AppWindow', useValue: window }
使AOT开心
将标记DOCUMENT
为可选也是一个好主意。根据Angular文档:
当应用程序上下文和呈现上下文不同时(例如,将应用程序运行到Web Worker中时),文档可能在应用程序上下文中不可用。
这是使用的示例,DOCUMENT
以查看浏览器是否支持SVG:
import { Optional, Component, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common'
...
constructor(@Optional() @Inject(DOCUMENT) document: Document) {
this.supportsSvg = !!(
document &&
document.createElementNS &&
document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect
);
@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});
}
简短而甜美,就像魅力。
当在整个应用程序中都可以访问全局变量时,通过DI(依赖注入)获取窗口对象不是一个好主意。
但是,如果您不想使用窗口对象,则也可以使用self
也指向窗口对象的关键字。
保持简单,伙计们!
export class HeroesComponent implements OnInit {
heroes: Hero[];
window = window;
}
<div>{{window.Object.entries({ foo: 1 }) | json}}</div>
实际上,访问窗口对象非常简单,这是我的基本组件,我对其进行了测试
import { Component, OnInit,Inject } from '@angular/core';
import {DOCUMENT} from '@angular/platform-browser';
@Component({
selector: 'app-verticalbanners',
templateUrl: './verticalbanners.component.html',
styleUrls: ['./verticalbanners.component.css']
})
export class VerticalbannersComponent implements OnInit {
constructor(){ }
ngOnInit() {
console.log(window.innerHeight );
}
}
ORIGINAL EXCEPTION: No provider for Window!
。但是,删除它为我解决了这个问题。仅使用前2条全局行就足够了。