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,
)
