J'écris un service Angular2 dans TypeScript qui utilisera localstorage. Et je souhaite injecter une référence à l'objet de fenêtre de navigateur dans mon service, car je ne souhaite référencer aucune variable globale. Comme angulaire 1.x $window
. Comment je fais ça?
Cela fonctionne pour moi actuellement (2018-03, version angulaire 5.2 avec AoT, testée dans angular-cli et construction d'un pack Web personnalisé):
Commencez par créer un service injectable qui fournit une référence à 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();
}
}
Enregistrez maintenant ce service avec votre racine AppModule pour pouvoir l’injecter partout:
import { WindowRefService } from './window-ref.service';
@NgModule({
providers: [
WindowRefService
],
...
})
export class AppModule {}
et plus tard, où vous devez injecter 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;
}
...
Vous pouvez également souhaiter ajouter nativeDocument
et d'autres éléments globaux à ce service de manière similaire si vous les utilisez dans votre application.
edit: Mis à jour avec la suggestion de Truchainz . edit2: Mis à jour pour les valeurs angulaires 2.1.2 edit3: Ajout de notes AoT edit4: Ajout de any
note de contournement type edit5: Solution mise à jour pour utiliser un WindowRefService qui corrige une erreur que je recevais lors de l'utilisation de la solution précédente avec une version différente edit6: ajout d'un exemple de frappe de fenêtre personnalisée
Avec la sortie de angular 2.0.0-rc.5, NgModule a été introduit. La solution précédente a cessé de fonctionner pour moi. Voici ce que j'ai fait pour résoudre ce problème:
app.module.ts:
@NgModule({
providers: [
{ provide: 'Window', useValue: window }
],
declarations: [...],
imports: [...]
})
export class AppModule {}
Dans certains composants:
import { Component, Inject } from '@angular/core';
@Component({...})
export class MyComponent {
constructor (@Inject('Window') window: Window) {}
}
Vous pouvez aussi utiliser un OpaqueToken au lieu de la chaîne 'Window'
Modifier:
AppModule est utilisé pour amorcer votre application dans main.ts comme ceci:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
Pour plus d'informations sur NgModule, consultez la documentation Angular 2: https://angular.io/docs/ts/latest/guide/ngmodule.html
Vous pouvez simplement l'injecter après avoir défini le fournisseur:
import {provide} from 'angular2/core';
bootstrap(..., [provide(Window, {useValue: window})]);
constructor(private window: Window) {
// this.window
}
voici un service que j'ai créé pour vous . https://Gist.github.com/gdi2290/f8a524cdfb1f54f1a59c }
tu peux soitimport {WINDOW, WINDOW_PROVIDERS} from './window-service';
ouimport {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;
}
}
Pour que cela fonctionne avec Angular 2.1.1, je devais @Inject
window en utilisant une chaîne
constructor( @Inject('Window') private window: Window) { }
puis se moquer comme ça
beforeEach(() => {
let windowMock: Window = <any>{ };
TestBed.configureTestingModule({
providers: [
ApiUriService,
{ provide: 'Window', useFactory: (() => { return windowMock; }) }
]
});
et dans l'ordinaire @NgModule
je le fournis comme ceci
{ provide: 'Window', useValue: window }
Dans Angular RC4, les travaux suivants combinant certaines des réponses ci-dessus sont ajoutés à l’application racine, ajoutez les fournisseurs:
@Component({
templateUrl: 'build/app.html',
providers: [
anotherProvider,
{ provide: Window, useValue: window }
]
})
Puis dans votre service, etc., l'injecter dans le constructeur
constructor(
@Inject(Window) private _window: Window,
)
J'ai utilisé OpaqueToken pour la chaîne '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: [] }),
];
Et utilisé uniquement pour importer WINDOW_PROVIDERS
dans bootstrap dans Angular 2.0.0-rc-4.
Mais avec la sortie de Angular 2.0.0-rc.5, je dois créer un module séparé:
import { NgModule } from '@angular/core';
import { WINDOW_PROVIDERS } from './window';
@NgModule({
providers: [WINDOW_PROVIDERS]
})
export class WindowModule { }
et juste défini dans la propriété imports de mon app.module.ts
principal
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 {}
Avant la déclaration @Component, vous pouvez également le faire,
declare var window: any;
En fait, le compilateur vous permettra d’accéder maintenant à la variable de fenêtre globale puisque vous la déclarez comme une variable globale supposée de type any.
Je ne conseillerais cependant pas d’accéder à window partout dans votre application. Vous devez créer des services qui permettent d’accéder/modifier les attributs de fenêtre nécessaires (et d’injecter ces services dans vos composants) afin de déterminer ce que vous pouvez faire avec la fenêtre sans les laisser modifier objet de la fenêtre entière.
À compter d'aujourd'hui (avril 2016), le code de la solution précédente ne fonctionnait pas. Je pense qu'il est possible d'injecter une fenêtre directement dans App.ts, puis de rassembler les valeurs dont vous avez besoin dans un service d'accès global dans l'application, mais Si vous préférez créer et injecter votre propre service, voici une solution plus simple.
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 présente InjectToken et crée également un jeton pour le document appelé DOCUMENT . Je pense que c'est la solution officielle et que cela fonctionne en mode AoT.
J'utilise la même logique pour créer une petite bibliothèque appelée ngx-window-token afin d'éviter que cela ne se répète.
Je l'ai utilisé dans un autre projet et construit en AoT sans problèmes.
Voici comment je l'ai utilisé dans autre paquet
Voici le plunker
Dans votre module
imports: [ BrowserModule, WindowTokenModule ]
In votre composant
constructor(@Inject(WINDOW) _window) { }
Il existe une possibilité d'accès direct à l'objet de fenêtre à travers le document
document.defaultView == window
Vous pouvez obtenir une fenêtre à partir d'un document injecté.
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);
}
}
Je sais que la question est de savoir comment injecter l'objet window dans un composant, mais vous le faites simplement pour accéder à localStorage semble-t-il. Si vous ne voulez vraiment que localStorage, pourquoi ne pas utiliser un service qui expose simplement cela, comme h5webstorage . Ensuite, votre composant décrira ses dépendances réelles, ce qui rendra votre code plus lisible.
C’est la réponse la plus courte/propre que j’ai trouvée en travaillant avec Angular 4 AOT
Source: 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);
}
}
Vous pouvez utiliser NgZone sur Angular 4:
import { NgZone } from '@angular/core';
constructor(private zone: NgZone) {}
print() {
this.zone.runOutsideAngular(() => window.print());
}
Il suffit de faire
export class AppWindow extends Window {}
et fait
{ provide: 'AppWindow', useValue: window }
rendre AOT heureux
C'est aussi une bonne idée de marquer la DOCUMENT
comme facultative. Selon les documents angulaires:
Le document peut ne pas être disponible dans le contexte d'application lorsque les contextes d'application et de rendu ne sont pas identiques (par exemple lors de l'exécution de l'application dans un Web Worker).
Voici un exemple d'utilisation de la variable DOCUMENT
pour savoir si le navigateur prend en charge le format 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
);
Voici une autre solution que j'ai trouvée récemment après que j'en ai eu marre de récupérer defaultView
de DOCUMENT
et de le vérifier pour 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;
}
});
@ Maxisam merci pour ngx-window-token . Je faisais quelque chose de similaire mais je suis passé au vôtre. Ceci est mon service pour écouter les événements de redimensionnement de la fenêtre et pour notifier les abonnés.
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});
}
Court et doux et fonctionne comme un charme.
Obtenir un objet window via DI (Dependency Injection) n'est pas une bonne idée lorsque des variables globales sont accessibles dans l'application.
Mais si vous ne voulez pas utiliser l'objet window, vous pouvez également utiliser le mot clé self
qui pointe également vers l'objet window.
En fait, c’est très simple d’accéder à l’objet window Voici mon composant de base et je l’ai testé son fonctionnement
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 );
}
}