私はlocalstorageを利用するTypeScriptでAngular 2サービスを書いています。また、グローバル変数を参照したくないので、ブラウザウィンドウオブジェクトへの参照を自分のサービスに挿入したいと思います。角1.x $window
のように。それ、どうやったら出来るの?
これは現在私のために働いています(2018-03、AoTを使った角度5.2、Angular-CliとカスタムWebPackビルドでテスト済み):
まず、windowへの参照を提供する注入可能なサービスを作成します。
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:以前のソリューションを使用したときのエラーを修正。)別のbuild edit6を使う:カスタムウィンドウをタイプする例を追加する
角度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
}
これは私があなたのために作ったサービスです。 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;
}
}
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 }
Angular RC4では、上記の答えのいくつかを組み合わせた次のような作品が、あなたのルートのapp.tsにプロバイダとして追加されています。
@Component({
templateUrl: 'build/app.html',
providers: [
anotherProvider,
{ provide: Window, useValue: window }
]
})
それからあなたのサービスなどでそれをコンストラクタに注入します
constructor(
@Inject(Window) private _window: Window,
)
@Component宣言の前に、それもできます。
declare var window: any;
あなたはそれをany型の仮定された大域変数として宣言するので、コンパイラは実際に大域ウィンドウ変数にアクセスできるようになります。
ただし、アプリケーションのいたるところでウィンドウにアクセスすることはお勧めしません。必要なウィンドウ属性にアクセスしたり変更したり(およびコンポーネントにそれらのサービスを追加したり)するサービスを作成してください。ウィンドウオブジェクト全体。
'Window'文字列に 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のブートストラップでWINDOW_PROVIDERS
をインポートするためだけに使用されていました。
しかしAngular 2.0.0-rc.5がリリースされたので、私は別のモジュールを作る必要があります:
import { NgModule } from '@angular/core';
import { WINDOW_PROVIDERS } from './window';
@NgModule({
providers: [WINDOW_PROVIDERS]
})
export class WindowModule { }
そしてちょうど私のメインの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 {}
あなたは注入された文書からウィンドウを得ることができます。
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 4ではInjectTokenが導入されていて、それらは DOCUMENT というドキュメントのトークンも作成します。これは公式の解決策であり、AoTで機能すると思います。
同じロジックを使用して、 ngx-window-token という小さなライブラリを作成し、これを何度も繰り返すのを防ぎます。
私は他のプロジェクトでそれを使用し、問題なくAoTでビルドしました。
これが私がこれを使った方法です 他のパッケージ
これが plunker です
あなたのモジュールで
コンポーネントのimports: [ BrowserModule, WindowTokenModule ]
constructor(@Inject(WINDOW) _window) { }
今日(2016年4月)現在、前のソリューションのコードは機能しません。Windowsを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;
}
}
文書を通してwindowのオブジェクトに直接アクセスする機会があります
document.defaultView == window
やれば十分
export class AppWindow extends Window {}
そして、やります
{ provide: 'AppWindow', useValue: window }
aOTを幸せにするために
私は質問がどのようにウィンドウオブジェクトをコンポーネントにインジェクトするかということを知っていますが、あなたはこれを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);
}
}
Angular 4にNgZoneを使用できます。
import { NgZone } from '@angular/core';
constructor(private zone: NgZone) {}
print() {
this.zone.runOutsideAngular(() => window.print());
}
DOCUMENT
をオプションとしてマークすることもお勧めです。 Angular docs:
アプリケーションコンテキストとレンダリングコンテキストが同じでない場合(たとえば、アプリケーションをWebワーカーに実行する場合)、ドキュメントはアプリケーションコンテキストで使用できない場合があります。
これは、ブラウザがSVGをサポートしているかどうかを確認するためにDOCUMENT
を使用する例です。
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
);
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;
}
});
@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
キーワードを使うこともできます。
実はここでウィンドウオブジェクトにアクセスするのがとても簡単なのが私の基本的なコンポーネントであり、私はそれが動作することをテストしました
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 );
}
}