Odpowiedzi:
To działa dla mnie obecnie (2018-03, angular 5.2 with AoT, testowane w angular-cli i niestandardowej kompilacji webpacka):
Najpierw utwórz usługę do wstrzykiwania, która zawiera odniesienie do 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();
}
}
Teraz zarejestruj tę usługę w swoim głównym module AppModule, aby można ją było wstrzykiwać wszędzie:
import { WindowRefService } from './window-ref.service';
@NgModule({
providers: [
WindowRefService
],
...
})
export class AppModule {}
a później, gdzie należy wykonać wstrzyknięcie 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żesz także chcieć dodać nativeDocument
i inne globalne do tej usługi w podobny sposób, jeśli używasz ich w swojej aplikacji.
edycja: Zaktualizowano sugestią Truchainza. edit2: Zaktualizowano dla Angular 2.1.2 edit3: Dodano uwagi AoT edit4: Dodanie opisu any
obejścia problemu
@Inject
pojawiały się No provider for Window
błędy. To całkiem fajne, że nie potrzebujesz instrukcji @Inject
!
@Inject(Window)
aby to zadziałało
window
, ale z usługą pomiędzy pozwala na usuwanie natywnych window
rzeczy w testach jednostkowych, a jak wspomniałeś dla SSR, można zapewnić alternatywną usługę, która ujawnia okno pozorowane / noop dla serwera. Powodem, dla którego wspominam o AOT, jest kilka wczesnych rozwiązań do zawijania okna, które zepsuły się w AOT po aktualizacji Angular.
Wraz z wydaniem angular 2.0.0-rc.5 NgModule został wprowadzony. Poprzednie rozwiązanie przestało dla mnie działać. Oto, co zrobiłem, aby to naprawić:
app.module.ts:
@NgModule({
providers: [
{ provide: 'Window', useValue: window }
],
declarations: [...],
imports: [...]
})
export class AppModule {}
W jakimś komponencie:
import { Component, Inject } from '@angular/core';
@Component({...})
export class MyComponent {
constructor (@Inject('Window') window: Window) {}
}
Możesz również użyć OpaqueToken zamiast ciągu „Window”
Edytować:
AppModule służy do ładowania aplikacji w main.ts w następujący sposób:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
Aby uzyskać więcej informacji o NgModule, przeczytaj dokumentację Angular 2: https://angular.io/docs/ts/latest/guide/ngmodule.html
Możesz go po prostu wstrzyknąć po ustawieniu dostawcy:
import {provide} from 'angular2/core';
bootstrap(..., [provide(Window, {useValue: window})]);
constructor(private window: Window) {
// this.window
}
window.var
zawartość strony się nie zmienia
Możesz pobrać okno z wstrzykniętego dokumentu.
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);
}
}
Aby to działało w Angular 2.1.1, musiałem @Inject
otworzyć okno za pomocą ciągu znaków
constructor( @Inject('Window') private window: Window) { }
a potem kpić z tego w ten sposób
beforeEach(() => {
let windowMock: Window = <any>{ };
TestBed.configureTestingModule({
providers: [
ApiUriService,
{ provide: 'Window', useFactory: (() => { return windowMock; }) }
]
});
i zwykle @NgModule
tak to zapewniam
{ provide: 'Window', useValue: window }
W Angular RC4 następujące prace, które są kombinacją niektórych z powyższych odpowiedzi, w aplikacji root. T dodaj dostawców:
@Component({
templateUrl: 'build/app.html',
providers: [
anotherProvider,
{ provide: Window, useValue: window }
]
})
Następnie w usłudze itp. Wstrzyknij go do konstruktora
constructor(
@Inject(Window) private _window: Window,
)
Przed deklaracją @Component też możesz to zrobić,
declare var window: any;
Kompilator faktycznie pozwoli ci teraz uzyskać dostęp do globalnej zmiennej okna, ponieważ zadeklarujesz ją jako zakładaną zmienną globalną o typie any.
Nie sugerowałbym jednak dostępu do okna w każdym miejscu aplikacji. Powinieneś tworzyć usługi, które uzyskują dostęp / modyfikują potrzebne atrybuty okna (i wstrzykują te usługi do swoich komponentów), aby określić zakres tego, co możesz zrobić z oknem, nie pozwalając im modyfikować cały obiekt okna.
Użyłem OpaqueToken dla ciągu znaków „Window”:
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: [] }),
];
I używane tylko do importowania WINDOW_PROVIDERS
w bootstrapie w Angular 2.0.0-rc-4.
Ale wraz z wydaniem Angulara 2.0.0-rc.5 muszę stworzyć osobny moduł:
import { NgModule } from '@angular/core';
import { WINDOW_PROVIDERS } from './window';
@NgModule({
providers: [WINDOW_PROVIDERS]
})
export class WindowModule { }
i właśnie zdefiniowano we właściwości Import mojego pliku głównego 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 {}
Angular 4 wprowadza InjectToken, a także tworzy token dla dokumentu o nazwie DOCUMENT . Myślę, że jest to oficjalne rozwiązanie i działa w AoT.
Używam tej samej logiki do stworzenia małej biblioteki o nazwie ngx-window-token aby zapobiec robieniu tego w kółko.
Użyłem go w innym projekcie i kompilowałem w AoT bez problemów.
Oto jak użyłem go w innym pakiecie
Tutaj jest plunker
W Twoim module
imports: [ BrowserModule, WindowTokenModule ]
W twoim komponencie
constructor(@Inject(WINDOW) _window) { }
Na dzień dzisiejszy (kwiecień 2016 r.) Kod w poprzednim rozwiązaniu nie działa, myślę, że można wstrzyknąć okno bezpośrednio do App.ts, a następnie zebrać potrzebne wartości do usługi globalnego dostępu w aplikacji, ale jeśli wolisz tworzyć i wprowadzać własne usługi, jest to o wiele prostsze rozwiązanie.
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;
}
}
Oto inne rozwiązanie, które wpadłem niedawno po tym, jak zmęczyłem się pobieraniem defaultView
z DOCUMENT
wbudowanego tokena i sprawdzaniem, czy jest pusty:
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
i używać go jak tokena iniekcyjnego DOKUMENTU dostarczonego przez Angular?
To wystarczy
export class AppWindow extends Window {}
i robić
{ provide: 'AppWindow', useValue: window }
aby uszczęśliwić AOT
Istnieje możliwość bezpośredniego dostępu do obiektu okna poprzez dokument
document.defaultView == window
Wiem, że pytanie brzmi, jak wstrzyknąć obiekt okna do komponentu, ale wydaje się, że robisz to tylko po to, aby dostać się do localStorage. Jeśli naprawdę chcesz tylko localStorage, dlaczego nie skorzystać z usługi, która to ujawnia, na przykład h5webstorage . Następnie komponent opiszesz jego rzeczywiste zależności, dzięki czemu Twój kod będzie bardziej czytelny.
To najkrótsza / najczystsza odpowiedź, jaką znalazłem podczas pracy z Angular 4 AOT
Źródło: 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);
}
}
Możesz użyć NgZone w Angular 4:
import { NgZone } from '@angular/core';
constructor(private zone: NgZone) {}
print() {
this.zone.runOutsideAngular(() => window.print());
}
Dobrym pomysłem jest również oznaczenie tego DOCUMENT
jako opcjonalnego. Zgodnie z dokumentacją Angular:
Dokument może nie być dostępny w kontekście aplikacji, gdy kontekst aplikacji i renderowania nie są takie same (np. Podczas uruchamiania aplikacji w module roboczym sieci Web).
Oto przykład użycia, DOCUMENT
aby sprawdzić, czy przeglądarka obsługuje 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 dzięki za ngx-window-token . Robiłem coś podobnego, ale przeszedłem na twoje. To jest moja usługa do słuchania zdarzeń zmiany rozmiaru okna i powiadamiania subskrybentów.
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});
}
Krótkie i słodkie, działa jak urok.
Pobieranie obiektu okna przez DI (Dependency Injection) nie jest dobrym pomysłem, gdy zmienne globalne są dostępne w całej aplikacji.
Ale jeśli nie chcesz używać obiektu okna, możesz również użyć self
słowa kluczowego, które również wskazuje na obiekt okna.
Niech to będzie proste, ludzie!
export class HeroesComponent implements OnInit {
heroes: Hero[];
window = window;
}
<div>{{window.Object.entries({ foo: 1 }) | json}}</div>
Właściwie bardzo łatwo jest uzyskać dostęp do obiektu okna, tutaj jest moim podstawowym komponentem i przetestowałem jego działanie
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!
. Jednak usunięcie go rozwiązało problem. Użycie tylko pierwszych 2 globalnych linii było dla mnie wystarczające.