web-dev-qa-db-de.com

Angular2 - Fenster zum Einfügen eines Fensters in einen angle2-Dienst

Ich schreibe einen Angular2-Dienst in TypeScript, der localstorage verwendet. Und ich möchte einen Verweis auf das Browserfensterobjekt in meinen Dienst einfügen, da ich keine globalen Variablen referenzieren möchte. Wie Winkel 1.x $window. Wie mache ich das?

93
lokanx

Dies funktioniert derzeit für mich (2018-03, Winkel 5.2 mit AoT, getestet in Winkel-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 eingefügt werden kann:

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

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

und später, wo Sie window injizieren müssen:

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

Auf ähnliche Weise können Sie auch nativeDocument und andere Globals zu diesem Dienst hinzufügen, wenn Sie diese in Ihrer Anwendung verwenden.


edit: Aktualisiert mit Truchainz-Vorschlag . edit2: Aktualisiert für Winkel 2.1.2 edit3: Hinzugefügte AoT-Hinweise edit4: Hinzufügen eines Workaround-Hinweises vom Typ any edit5: Lösung wurde aktualisiert, um einen WindowRefService zu verwenden, der einen Fehler behebt, den ich erhalten hatte, wenn die vorherige Lösung mit einem anderen Build verwendet wurde edit6: Hinzufügen von benutzerdefinierten Fenstertypen

120
elwyn

Mit dem Release von eckig 2.0.0-rc.5 wurde NgModule eingeführt. Die vorherige Lösung funktionierte nicht mehr für mich. So habe ich das Problem behoben:

app.module.ts:

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

In einer Komponente:

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 Dokumentation zu Angular 2: https://angular.io/docs/ts/latest/guide/ngmodule.html

29
JNK

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
}
17
Paul Dutka

hier ist ein Service, den ich für Sie gemacht habe . https://Gist.github.com/gdi2290/f8a524cdfb1f54f1a59c

du kannst entweder
import {WINDOW, WINDOW_PROVIDERS} from './window-service';
oder
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

Damit es mit Angular 2.1.1 funktioniert, musste ich @Inject-Fenster mit einem String verwenden

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

und dann verspotten es so

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

und im gewöhnlichen @NgModule gebe ich es so vor

{ provide: 'Window', useValue: window }
12
Klas Mellbourn

In Angular RC4 funktioniert Folgendes, was eine Kombination einiger der oben genannten Antworten ist, in Ihrem Root-App.ts fügen Sie die Anbieter hinzu:

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

Dann in Ihrem Service etc in den Konstruktor einfügen

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

Ich habe OpaqueToken für 'Window'-String 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 von 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 nur in der Importeigenschaft meines Hauptcodes 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 {}
9
Chyngyz

Vor der @Component-Deklaration können Sie das auch tun

declare var window: any;

Der Compiler lässt Sie nun 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, auf das Fenster in Ihrer Anwendung überall zuzugreifen. Sie sollten Dienste erstellen, die auf die erforderlichen Fensterattribute zugreifen bzw. diese ändern (und diese Dienste in Ihre Komponenten einfügen), um die Möglichkeiten zu bestimmen, die Sie mit dem Fenster tun können, ohne dass Sie es ändern müssen gesamtes Fensterobjekt.

8
S.Galarneau

Seit heute (April 2016) funktioniert der Code in der vorherigen Lösung nicht. Ich denke, es ist möglich, Fenster direkt in App.ts einzuspeisen und dann die erforderlichen Werte für einen globalen Zugriff in der App zu sammeln Wenn Sie es vorziehen, Ihren eigenen Dienst zu erstellen und einzuspeisen, 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;
    }
}
6
Will de la Vega

In Angular 4 wird InjectToken eingeführt. Außerdem wird ein Token für das Dokument namens DOCUMENT erstellt. Ich denke, das ist die offizielle Lösung und funktioniert in AoT.

Ich verwende dieselbe Logik, um eine kleine Bibliothek mit dem Namen ngx-window-token zu erstellen, um dies nicht immer wieder zu verhindern. 

Ich habe es in einem anderen Projekt verwendet und in AoT ohne Probleme erstellt.

So habe ich es in other package verwendet 

Hier ist der Plunker

In deinem Modul 

imports: [ BrowserModule, WindowTokenModule ] .__In Ihrer Komponente

constructor(@Inject(WINDOW) _window) { }

5
maxisam

Es besteht die Möglichkeit, über das Dokument direkt auf das Fensterobjekt zuzugreifen

document.defaultView == window

Sie können ein Fenster aus dem eingefügten 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);
  }

}
3
Alex Nikulin

Ich weiß, die Frage ist, wie man das Fensterobjekt in eine Komponente einfügt, aber Sie tun dies nur, um zu localStorage zu gelangen. Wenn Sie wirklich nur localStorage möchten, können Sie einen Dienst verwenden, der genau das bereitstellt, z. B. h5webstorage . Dann wird Ihre Komponente ihre realen Abhängigkeiten beschreiben, wodurch Ihr Code lesbarer wird.

3
SirDarquan

Dies ist die kürzeste/sauberste Antwort, die ich 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);
  }
}
3
nwarp

Sie können NgZone auf Angular 4 verwenden:

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

constructor(private zone: NgZone) {}

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

Es ist genug zu tun 

export class AppWindow extends Window {} 

und TU 

{ provide: 'AppWindow', useValue: window } 

aOT glücklich zu machen

2

Es ist auch eine gute Idee, DOCUMENT als optional zu markieren. Nach den Angular-Dokumenten:

Dokument ist möglicherweise nicht im Anwendungskontext verfügbar, wenn Anwendungs- und Wiedergabekontext nicht identisch sind (z. B. beim Ausführen der Anwendung in einem Web Worker).

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

Hier ist eine andere Lösung, die ich kürzlich gefunden habe, nachdem ich es satt hatte, defaultView von DOCUMENT eingebautem Token zu bekommen und es auf null zu prü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;
        }
    });
0
pokrishka

@maxisam Danke für ngx-window-token . Ich machte etwas Ähnliches, wechselte aber zu dir. Dies ist mein Service zum Abhören von Fenstern zur Größenänderung von Ereignissen 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 süß und wirkt wie ein Zauber.

0
Andrew Alderson

Das Holen eines Fensterobjekts über DI (Dependency Injection) ist keine gute Idee, wenn globale Variablen in der gesamten Anwendung verfügbar sind.

Wenn Sie jedoch kein Fensterobjekt verwenden möchten, können Sie auch das Schlüsselwort self verwenden, das auch auf das Fensterobjekt verweist.

0
Shivang Gupta

Eigentlich ist es sehr einfach, auf das Window-Objekt zuzugreifen

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