angular - Ako vložiť window do služby?


Píšem službu Angular 2 v TypeScripte, ktorá bude využívať localstorage. Chcem do svojej služby vložiť referenciu na objekt window prehliadača, pretože nechcem odkazovať na žiadne globálne premenné ako Angular 1.x $window.

Ako to urobím?

Odpoveď č. 1

Toto momentálne pre mňa funguje (2018-03, Angular 5.2 s AoT, testované v angular-cli a v vlastnej webpack zostave):

Najprv vytvorte injektovateľnú službu, ktorá poskytuje odkaz na okno:

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

Teraz zaregistrujte túto službu vo vašom koreňovom AppModule, aby mohla byť injektovaná všade:

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

@NgModule({        
  providers: [
    WindowRefService 
  ],
  ...
})
export class AppModule {}

a potom neskôr, keď budete potrebovať vložiť 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;
    }
...

Možno budete chcieť pridať nativeDocument a ďalšie globálne premenné do tejto služby podobným spôsobom, ak ich používate vo svojej aplikácii.

edit: Aktualizované podľa návrhu Truchainz. edit2: Aktualizované pre Angular 2.1.2 edit3: Pridané poznámky k AoT edit4: Pridanie poznámky k obchádzke typu any edit5: Aktualizované riešenie na použitie WindowRefService, ktoré opravuje chybu, ktorú som dostával pri používaní predchádzajúceho riešenia s inou zostavou edit6: pridanie príkladu vlastného typovania Window

Odpoveď č. 2

Môžete získať okno z injektovaného dokumentu.

import { Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

export class MyClass {

  private window: Window;

  constructor(@Inject(DOCUMENT) private document: Document) {
     this.window = this.document.defaultView;
  }

  check() {
    console.log(this.document);
    console.log(this.window);
  }

}

Ak máte na window vlastné vlastnosti, môžete použiť vlastné rozhranie

import { Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

export interface CustomWindow extends Window {
  $: JQuery
  customProperty: boolean;
}

export class MyClass {

  private window: CustomWindow;

  constructor(@Inject(DOCUMENT) private document: Document) {
     this.window = <any>this.document.defaultView;
  }

  check() {
    console.log(this.document);
    console.log(this.window);
  }

}

Odpoveď č. 3

S vydaním Angular 2.0.0-rc.5 bol predstavený NgModule. Predchádzajúce riešenie prestalo pre mňa fungovať. Toto som urobil, aby som to opravil:

app.module.ts:

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

V niektorom komponente:

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

@Component({...})
export class MyComponent {
    constructor (@Inject('Window') window: Window) {}
}

Môžete tiež použiť OpaqueToken namiesto reťazca 'Window'

Upraviť:

AppModule sa používa na spustenie vašej aplikácie v main.ts takto:

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

platformBrowserDynamic().bootstrapModule(AppModule)

Pre viac informácií o NgModule si prečítajte dokumentáciu k Angular 2: https://angular.io/docs/ts/latest/guide/ngmodule.html

Odpoveď č. 4

Môžete ho jednoducho injektovať po tom, čo nastavíte poskytovateľa:

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

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

Odpoveď č. 5

Aby to fungovalo na Angular 2.1.1, musel som @Inject window používať pomocou reťazca

  constructor( @Inject('Window') private window: Window) { }

a potom to takto posmievať

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

a v bežnom @NgModule ho poskytujem takto

{ provide: 'Window', useValue: window }

Odpoveď č. 6

Tu je ďalšie riešenie, na ktoré som prišiel nedávno, potom čo som sa unavil zisťovaním defaultView z vstavaného tokenu DOCUMENT a kontrolovaním, či je 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;
        }
    });

Poznámka: Tento token som vydal spolu s mnohými ďalšími prístupmi k globálnym objektom ako malú knižnicu dostupnú tu: https://github.com/ng-web-apis/common

Pozrite si zvyšok organizácie pre mnoho ďalších natívnych knižníc API pre Angular: https://github.com/ng-web-apis

Odpoveď č. 7

Pred deklaráciou @Component to môžete urobiť tiež,

declare var window: any;

Kompilátor vám teraz v skutočnosti umožní pristupovať k globálnej premennej window, pretože ju deklarujete ako predpokladanú globálnu premennú s typom any.

Nedoporučoval by som pristupovať k oknu všade vo vašej aplikácii, mali by ste vytvoriť služby, ktoré pristupujú k potrebným atribútom okna alebo ich menia (a tieto služby vkladať do vašich komponentov), aby ste obmedzili, čo môžete robiť s oknom, bez toho aby ste umožnili meniť celý objekt okna.

Odpoveď č. 8

V Angular RC4 funguje nasledovné, čo je kombinácia niektorých z vyššie uvedených odpovedí, vo vašom hlavnom súbore app.ts ho pridajte do providers:

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

Potom vo vašej službe atď. injektujte to do konštruktora

constructor(
      @Inject(Window) private _window: Window,
)

Súvisiace články