Antworten:
Dies funktioniert derzeit für mich (2018-03, Angular 5.2 mit AoT, getestet in Angular-CLI und einem benutzerdefinierten Webpack-Build):
Erstellen Sie zunächst einen injizierbaren Dienst, der einen Verweis auf das Fenster enthält:
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();
}
}
Registrieren Sie diesen Dienst jetzt bei Ihrem Root-AppModule, damit er überall injiziert werden kann:
import { WindowRefService } from './window-ref.service';
@NgModule({
providers: [
WindowRefService
],
...
})
export class AppModule {}
und dann später, wo Sie injizieren müssen 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;
}
...
Möglicherweise möchten nativeDocument
Sie diesem Service auch ähnliche globale Elemente hinzufügen , wenn Sie diese in Ihrer Anwendung verwenden.
bearbeiten: Aktualisiert mit Truchainz Vorschlag. edit2: Aktualisiert für Angular 2.1.2 edit3: AoT-Notizen hinzugefügt edit4: Hinzufügen von Problemumgehungsnotizen für Notizen edit5: Die any
Lösung wurde aktualisiert, um einen WindowRefService zu verwenden, der einen Fehler behebt, den ich bei der Verwendung einer früheren Lösung mit einem anderen Build erhalten habe. edit6: Hinzufügen einer benutzerdefinierten Fenstertypisierung
@Inject
ich No provider for Window
Fehler bekam. Das ist ziemlich schön, das Handbuch nicht zu brauchen @Inject
!
@Inject(Window)
arbeiten
window
, aber mit dem dazwischen liegenden Dienst können native Inhalte in Komponententests gelöscht werden. window
Wie Sie für SSR erwähnen, kann ein alternativer Dienst bereitgestellt werden, der ein Mock / Noop-Fenster für den Server anzeigt. Der Grund, warum ich AOT erwähne, sind einige der frühen Lösungen für das Umbrechen von Fenstern, die bei der Aktualisierung von Angular in AOT kaputt gegangen sind.
Mit der Freigabe von Angular 2.0.0-rc.5 wurde NgModule eingeführt. Die vorherige Lösung hat bei mir nicht mehr funktioniert. Folgendes habe ich getan, um das Problem zu beheben:
app.module.ts:
@NgModule({
providers: [
{ provide: 'Window', useValue: window }
],
declarations: [...],
imports: [...]
})
export class AppModule {}
In einigen Komponenten:
import { Component, Inject } from '@angular/core';
@Component({...})
export class MyComponent {
constructor (@Inject('Window') window: Window) {}
}
Sie können auch ein OpaqueToken anstelle der Zeichenfolge 'Window' verwenden.
Bearbeiten:
Das AppModule wird verwendet, um Ihre Anwendung in main.ts wie folgt zu booten:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
Weitere Informationen zu NgModule finden Sie in der Angular 2-Dokumentation: https://angular.io/docs/ts/latest/guide/ngmodule.html
Sie können es einfach injizieren, nachdem Sie den Anbieter festgelegt haben:
import {provide} from 'angular2/core';
bootstrap(..., [provide(Window, {useValue: window})]);
constructor(private window: Window) {
// this.window
}
window.var
den Inhalt der Seite ändere, ändert sich nicht
Sie können Fenster aus injiziertem Dokument erhalten.
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);
}
}
Damit es in Angular 2.1.1 funktioniert, musste ich @Inject
eine Zeichenfolge verwenden
constructor( @Inject('Window') private window: Window) { }
und dann verspotten Sie es so
beforeEach(() => {
let windowMock: Window = <any>{ };
TestBed.configureTestingModule({
providers: [
ApiUriService,
{ provide: 'Window', useFactory: (() => { return windowMock; }) }
]
});
und im gewöhnlichen biete @NgModule
ich es so an
{ provide: 'Window', useValue: window }
In Angular RC4 funktioniert das Folgende, was eine Kombination einiger der oben genannten Antworten ist. Fügen Sie in Ihren Root-Apps die Anbieter hinzu:
@Component({
templateUrl: 'build/app.html',
providers: [
anotherProvider,
{ provide: Window, useValue: window }
]
})
Dann in Ihren Dienst usw. in den Konstruktor injizieren
constructor(
@Inject(Window) private _window: Window,
)
Vor der @ Component-Deklaration können Sie dies auch tun.
declare var window: any;
Der Compiler lässt Sie dann tatsächlich auf die globale Fenstervariable zugreifen, da Sie sie als angenommene globale Variable mit dem Typ any deklarieren.
Ich würde jedoch nicht empfehlen, überall in Ihrer Anwendung auf das Fenster zuzugreifen. Sie sollten Dienste erstellen, die auf die erforderlichen Fensterattribute zugreifen / diese ändern (und diese Dienste in Ihre Komponenten einfügen), um zu erfassen, was Sie mit dem Fenster tun können, ohne dass sie das Fenster ändern können ganzes Fensterobjekt.
Ich habe OpaqueToken für die ' Window' -Zeichenfolge verwendet:
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: [] }),
];
Und nur zum Importieren WINDOW_PROVIDERS
in Bootstrap in Angular 2.0.0-rc-4 verwendet.
Aber mit der Veröffentlichung von Angular 2.0.0-rc.5 muss ich ein separates Modul erstellen:
import { NgModule } from '@angular/core';
import { WINDOW_PROVIDERS } from './window';
@NgModule({
providers: [WINDOW_PROVIDERS]
})
export class WindowModule { }
und gerade in der Import-Eigenschaft meines Haupt definiert 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 {}
Ab heute (April 2016) funktioniert der Code in der vorherigen Lösung nicht mehr. Ich denke, es ist möglich, Fenster direkt in App.ts einzufügen und dann die Werte, die Sie benötigen, in einem Dienst für den globalen Zugriff in der App zu sammeln Wenn Sie es vorziehen, Ihren eigenen Service zu erstellen und einzufügen, ist dies eine einfachere Lösung.
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;
}
}
Angular 4 führt InjectToken ein und erstellt ein Token für das Dokument DOCUMENT . Ich denke, dies ist die offizielle Lösung und sie funktioniert in AoT.
Ich verwende dieselbe Logik, um eine kleine Bibliothek namens ngx-window-token zu erstellen , um dies immer wieder zu verhindern.
Ich habe es in anderen Projekten verwendet und AoT ohne Probleme eingebaut.
Hier ist, wie ich es in einem anderen Paket verwendet habe
Hier ist der Plunker
In Ihrem Modul
imports: [ BrowserModule, WindowTokenModule ]
In Ihrer Komponente
constructor(@Inject(WINDOW) _window) { }
Es besteht die Möglichkeit, über das Dokument direkt auf das Objekt des Fensters zuzugreifen
document.defaultView == window
Hier ist eine andere Lösung, die ich kürzlich gefunden habe, nachdem ich es satt hatte, defaultView
von einem DOCUMENT
eingebauten Token zu kommen und es auf null zu überprüfen:
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
und verwenden Sie es wie das von Angular bereitgestellte DOKUMENT-Injektions-Token?
Ich weiß, dass die Frage ist, wie das Fensterobjekt in eine Komponente eingefügt wird, aber Sie tun dies nur, um zu localStorage zu gelangen, wie es scheint. Wenn Sie wirklich nur localStorage möchten, können Sie einen Dienst wie h5webstorage verwenden, der genau das verfügbar macht . Anschließend beschreibt Ihre Komponente die tatsächlichen Abhängigkeiten, wodurch Ihr Code besser lesbar wird.
Dies ist die kürzeste / sauberste Antwort, die ich bei der Arbeit mit Angular 4 AOT gefunden habe
Quelle: 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);
}
}
Es ist genug zu tun
export class AppWindow extends Window {}
und TU
{ provide: 'AppWindow', useValue: window }
AOT glücklich machen
Sie können NgZone für Angular 4 verwenden:
import { NgZone } from '@angular/core';
constructor(private zone: NgZone) {}
print() {
this.zone.runOutsideAngular(() => window.print());
}
Es ist auch eine gute Idee, das DOCUMENT
als optional zu markieren . Gemäß den Angular-Dokumenten:
Das Dokument ist möglicherweise nicht im Anwendungskontext verfügbar, wenn der Anwendungs- und der Renderkontext nicht identisch sind (z. B. wenn die Anwendung in einem Web Worker ausgeführt wird).
Hier ist ein Beispiel für die Verwendung von DOCUMENT
, um festzustellen, ob der Browser SVG-Unterstützung bietet:
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 danke für ngx-window-token . Ich habe etwas Ähnliches gemacht, aber zu dir gewechselt. Dies ist mein Dienst zum Abhören von Ereignissen zur Größenänderung von Fenstern und zum Benachrichtigen von Abonnenten.
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});
}
Kurz und bündig und wirkt wie ein Zauber.
Das Abrufen von Fensterobjekten über DI (Dependency Injection) ist keine gute Idee, wenn auf globale Variablen in der gesamten Anwendung zugegriffen werden kann.
Wenn Sie jedoch kein Fensterobjekt verwenden möchten, können Sie auch ein self
Schlüsselwort verwenden, das auch auf ein Fensterobjekt verweist.
Halte es einfach, Leute!
export class HeroesComponent implements OnInit {
heroes: Hero[];
window = window;
}
<div>{{window.Object.entries({ foo: 1 }) | json}}</div>
Eigentlich ist der sehr einfache Zugriff auf das Fensterobjekt hier meine Grundkomponente und ich habe getestet, ob es funktioniert
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!
. Das Entfernen hat das Problem jedoch für mich behoben. Es hat mir gereicht, nur die ersten beiden globalen Linien zu verwenden.