web-dev-qa-db-ja.com

Angular 2 - Angular 2サービスにウィンドウを挿入する方法

私はlocalstorageを利用するTypeScriptでAngular 2サービスを書いています。また、グローバル変数を参照したくないので、ブラウザウィンドウオブジェクトへの参照を自分のサービスに挿入したいと思います。角1.x $windowのように。それ、どうやったら出来るの?

103
lokanx

これは現在私のために働いています(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を使う:カスタムウィンドウをタイプする例を追加する

126
elwyn

角度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

30
JNK

プロバイダを設定した後に注入するだけです。

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

constructor(private window: Window) {
    // this.window
}
17
Paul Dutka

これは私があなたのために作ったサービスです。 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;
  }
}
15
gdi2290

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 }
13
Klas Mellbourn

Angular RC4では、上記の答えのいくつかを組み合わせた次のような作品が、あなたのルートのapp.tsにプロバイダとして追加されています。

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

それからあなたのサービスなどでそれをコンストラクタに注入します

constructor(
      @Inject(Window) private _window: Window,
)
9
Joel Davey

@Component宣言の前に、それもできます。

declare var window: any;

あなたはそれをany型の仮定された大域変数として宣言するので、コンパイラは実際に大域ウィンドウ変数にアクセスできるようになります。

ただし、アプリケーションのいたるところでウィンドウにアクセスすることはお勧めしません。必要なウィンドウ属性にアクセスしたり変更したり(およびコンポーネントにそれらのサービスを追加したり)するサービスを作成してください。ウィンドウオブジェクト全体。

9
S.Galarneau

'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 {}
9
Chyngyz

あなたは注入された文書からウィンドウを得ることができます。

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

}
7
Alex Nikulin

Angular 4ではInjectTokenが導入されていて、それらは DOCUMENT というドキュメントのトークンも作成します。これは公式の解決策であり、AoTで機能すると思います。

同じロジックを使用して、 ngx-window-token という小さなライブラリを作成し、これを何度も繰り返すのを防ぎます。

私は他のプロジェクトでそれを使用し、問題なくAoTでビルドしました。

これが私がこれを使った方法です 他のパッケージ

これが plunker です

あなたのモジュールで

コンポーネントのimports: [ BrowserModule, WindowTokenModule ]

constructor(@Inject(WINDOW) _window) { }

6
maxisam

今日(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;
    }
}
6
Will de la Vega

文書を通してwindowのオブジェクトに直接アクセスする機会があります

document.defaultView == window

やれば十分

export class AppWindow extends Window {} 

そして、やります

{ provide: 'AppWindow', useValue: window } 

aOTを幸せにするために

3

私は質問がどのようにウィンドウオブジェクトをコンポーネントにインジェクトするかということを知っていますが、あなたはこれをlocalStorageに到達させるためだけにやっているようです。本当にlocalStorageがほしい場合は、 h5webstorage のように、それだけを公開するサービスを使用しないでください。それから、あなたのコンポーネントはあなたのコードをもっと読みやすくする本当の依存関係を記述します。

3
SirDarquan

これは私が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);
  }
}
3
nwarp

Angular 4にNgZoneを使用できます。

import { NgZone } from '@angular/core';

constructor(private zone: NgZone) {}

print() {
    this.zone.runOutsideAngular(() => window.print());
}
2
Leonardo Pinto

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
);
1
Ole

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

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

短くて甘いし、魅力のように働きます。

0
Andrew Alderson

アプリケーション全体でグローバル変数にアクセスできる場合は、DI(依存性注入)を介してウィンドウオブジェクトを取得することはお勧めできません。

しかし、ウィンドウオブジェクトを使いたくない場合は、ウィンドウオブジェクトを指すselfキーワードを使うこともできます。

0
Shivang Gupta

実はここでウィンドウオブジェクトにアクセスするのがとても簡単なのが私の基本的なコンポーネントであり、私はそれが動作することをテストしました

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

}
0
Vikas Kandari