回答:
これは現在私のために機能しています(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を編集edit6:カスタムウィンドウタイプの例を追加
@Inject
、私はなっていたNo provider for Window
エラーを。マニュアルがいらないのはいいですね@Inject
!
@Inject(Window)
、これを機能させるために使用しなければなりませんでした
window
していますが、その間のサービスを使用するwindow
と、単体テストでネイティブスタッフをスタブアウトできます。SSRについて言及したように、サーバーのモック/ヌープウィンドウを公開する代替サービスを提供できます。私がAOTについて言及した理由は、Angularが更新されたときに、ラッピングウィンドウの初期のソリューションのいくつかがAOTで壊れたためです。
angular 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) {}
}
文字列「Window」の代わりにOpaqueTokenを使用することもできます
編集:
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 { }
そしてちょうど私のメインのインポートプロパティで定義されています 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に直接挿入し、必要な値をサービスに収集して、アプリケーションのグローバルアクセスに使用できると思いますが、独自のサービスを作成して注入したい場合、より簡単な解決策はこれです。
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) { }
それで十分です
export class AppWindow extends Window {}
そして、やります
{ provide: 'AppWindow', useValue: window }
AOTを幸せにする
これは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);
}
}
DOCUMENT
をオプションとしてマークすることもお勧めします。Angularのドキュメントによると:
アプリケーションコンテキストとレンダリングコンテキストが異なる場合(アプリケーションをWebワーカーで実行している場合など)、ドキュメントはアプリケーションコンテキストで使用できない場合があります。
を使用し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(Dependency Injection)を介してウィンドウオブジェクトを取得することはお勧めできません。
ただし、ウィンドウオブジェクトを使用しない場合は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つのグローバル回線を使用するだけで十分でした。